diff options
author | Seokmann Ju <seokmann.ju@qlogic.com> | 2007-07-05 20:16:51 (GMT) |
---|---|---|
committer | James Bottomley <jejb@mulgrave.localdomain> | 2007-07-15 00:08:05 (GMT) |
commit | 2c3dfe3f6ad8daff5acdb01713e4f2b116e78136 (patch) | |
tree | 8d95e2356c0f5121ceab48ab564d2796b6530d29 /drivers/scsi/qla2xxx/qla_mbx.c | |
parent | 968a5763fb7247feb0e69573a2975a7a0c094267 (diff) | |
download | linux-fsl-qoriq-2c3dfe3f6ad8daff5acdb01713e4f2b116e78136.tar.xz |
[SCSI] qla2xxx: add support for NPIV
Following patch adds support for NPIV (N-Port ID Virtualization) to the
qla2xxx.
- supported within switched-fabric topologies only.
- supports up to 63 virtual ports on each physical port.
Signed-off-by: Seokmann Ju <seokmann.ju@qlogic.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/scsi/qla2xxx/qla_mbx.c')
-rw-r--r-- | drivers/scsi/qla2xxx/qla_mbx.c | 396 |
1 files changed, 383 insertions, 13 deletions
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 71e32a2..2cd0cff 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -42,25 +42,29 @@ qla2x00_mbx_sem_timeout(unsigned long data) * Kernel context. */ static int -qla2x00_mailbox_command(scsi_qla_host_t *ha, mbx_cmd_t *mcp) +qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp) { int rval; unsigned long flags = 0; - device_reg_t __iomem *reg = ha->iobase; + device_reg_t __iomem *reg; struct timer_list tmp_intr_timer; uint8_t abort_active; - uint8_t io_lock_on = ha->flags.init_done; + uint8_t io_lock_on; uint16_t command; uint16_t *iptr; uint16_t __iomem *optr; uint32_t cnt; uint32_t mboxes; unsigned long wait_time; + scsi_qla_host_t *ha = to_qla_parent(pvha); + + reg = ha->iobase; + io_lock_on = ha->flags.init_done; rval = QLA_SUCCESS; abort_active = test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags); - DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + DEBUG11(printk("%s(%ld): entered.\n", __func__, pvha->host_no)); /* * Wait for active mailbox commands to finish by waiting at most tov @@ -889,7 +893,7 @@ qla2x00_abort_target(fc_port_t *fcport) */ int qla2x00_get_adapter_id(scsi_qla_host_t *ha, uint16_t *id, uint8_t *al_pa, - uint8_t *area, uint8_t *domain, uint16_t *top) + uint8_t *area, uint8_t *domain, uint16_t *top, uint16_t *sw_cap) { int rval; mbx_cmd_t mc; @@ -899,8 +903,9 @@ qla2x00_get_adapter_id(scsi_qla_host_t *ha, uint16_t *id, uint8_t *al_pa, ha->host_no)); mcp->mb[0] = MBC_GET_ADAPTER_LOOP_ID; + mcp->mb[9] = ha->vp_idx; mcp->out_mb = MBX_0; - mcp->in_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_9|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0; mcp->tov = 30; mcp->flags = 0; rval = qla2x00_mailbox_command(ha, mcp); @@ -913,6 +918,7 @@ qla2x00_get_adapter_id(scsi_qla_host_t *ha, uint16_t *id, uint8_t *al_pa, *area = MSB(mcp->mb[2]); *domain = LSB(mcp->mb[3]); *top = mcp->mb[6]; + *sw_cap = mcp->mb[7]; if (rval != QLA_SUCCESS) { /*EMPTY*/ @@ -1009,7 +1015,11 @@ qla2x00_init_firmware(scsi_qla_host_t *ha, uint16_t size) DEBUG11(printk("qla2x00_init_firmware(%ld): entered.\n", ha->host_no)); - mcp->mb[0] = MBC_INITIALIZE_FIRMWARE; + if (ha->flags.npiv_supported) + mcp->mb[0] = MBC_MID_INITIALIZE_FIRMWARE; + else + mcp->mb[0] = MBC_INITIALIZE_FIRMWARE; + mcp->mb[2] = MSW(ha->init_cb_dma); mcp->mb[3] = LSW(ha->init_cb_dma); mcp->mb[4] = 0; @@ -1081,7 +1091,8 @@ qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt) mcp->mb[3] = LSW(pd_dma); mcp->mb[6] = MSW(MSD(pd_dma)); mcp->mb[7] = LSW(MSD(pd_dma)); - mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_0; + mcp->mb[9] = ha->vp_idx; + mcp->out_mb = MBX_9|MBX_7|MBX_6|MBX_3|MBX_2|MBX_0; mcp->in_mb = MBX_0; if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { mcp->mb[1] = fcport->loop_id; @@ -1259,7 +1270,8 @@ qla2x00_get_port_name(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t *name, ha->host_no)); mcp->mb[0] = MBC_GET_PORT_NAME; - mcp->out_mb = MBX_1|MBX_0; + mcp->mb[9] = ha->vp_idx; + mcp->out_mb = MBX_9|MBX_1|MBX_0; if (HAS_EXTENDED_IDS(ha)) { mcp->mb[1] = loop_id; mcp->mb[10] = opt; @@ -1447,6 +1459,7 @@ qla24xx_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain, lg->port_id[0] = al_pa; lg->port_id[1] = area; lg->port_id[2] = domain; + lg->vp_index = cpu_to_le16(ha->vp_idx); rval = qla2x00_issue_iocb(ha, lg, lg_dma, 0); if (rval != QLA_SUCCESS) { DEBUG2_3_11(printk("%s(%ld): failed to issue Login IOCB " @@ -1701,6 +1714,7 @@ qla24xx_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain, lg->port_id[0] = al_pa; lg->port_id[1] = area; lg->port_id[2] = domain; + lg->vp_index = cpu_to_le16(ha->vp_idx); rval = qla2x00_issue_iocb(ha, lg, lg_dma, 0); if (rval != QLA_SUCCESS) { DEBUG2_3_11(printk("%s(%ld): failed to issue Logout IOCB " @@ -1863,7 +1877,8 @@ qla2x00_get_id_list(scsi_qla_host_t *ha, void *id_list, dma_addr_t id_list_dma, mcp->mb[6] = MSW(MSD(id_list_dma)); mcp->mb[7] = LSW(MSD(id_list_dma)); mcp->mb[8] = 0; - mcp->out_mb |= MBX_8|MBX_7|MBX_6|MBX_3|MBX_2; + mcp->mb[9] = ha->vp_idx; + mcp->out_mb |= MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2; } else { mcp->mb[1] = MSW(id_list_dma); mcp->mb[2] = LSW(id_list_dma); @@ -2212,6 +2227,7 @@ qla24xx_abort_command(scsi_qla_host_t *ha, srb_t *sp) abt->port_id[0] = fcport->d_id.b.al_pa; abt->port_id[1] = fcport->d_id.b.area; abt->port_id[2] = fcport->d_id.b.domain; + abt->vp_index = fcport->vp_idx; rval = qla2x00_issue_iocb(ha, abt, abt_dma, 0); if (rval != QLA_SUCCESS) { DEBUG2_3_11(printk("%s(%ld): failed to issue IOCB (%x).\n", @@ -2249,7 +2265,7 @@ qla24xx_abort_target(fc_port_t *fcport) int rval; struct tsk_mgmt_cmd *tsk; dma_addr_t tsk_dma; - scsi_qla_host_t *ha; + scsi_qla_host_t *ha, *pha; if (fcport == NULL) return 0; @@ -2257,7 +2273,8 @@ qla24xx_abort_target(fc_port_t *fcport) DEBUG11(printk("%s(%ld): entered.\n", __func__, fcport->ha->host_no)); ha = fcport->ha; - tsk = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &tsk_dma); + pha = to_qla_parent(ha); + tsk = dma_pool_alloc(pha->s_dma_pool, GFP_KERNEL, &tsk_dma); if (tsk == NULL) { DEBUG2_3(printk("%s(%ld): failed to allocate Task Management " "IOCB.\n", __func__, ha->host_no)); @@ -2273,6 +2290,8 @@ qla24xx_abort_target(fc_port_t *fcport) tsk->p.tsk.port_id[0] = fcport->d_id.b.al_pa; tsk->p.tsk.port_id[1] = fcport->d_id.b.area; tsk->p.tsk.port_id[2] = fcport->d_id.b.domain; + tsk->p.tsk.vp_index = fcport->vp_idx; + rval = qla2x00_issue_iocb(ha, tsk, tsk_dma, 0); if (rval != QLA_SUCCESS) { DEBUG2_3_11(printk("%s(%ld): failed to issue Target Reset IOCB " @@ -2303,7 +2322,7 @@ qla24xx_abort_target(fc_port_t *fcport) } atarget_done: - dma_pool_free(ha->s_dma_pool, tsk, tsk_dma); + dma_pool_free(pha->s_dma_pool, tsk, tsk_dma); return rval; } @@ -2610,3 +2629,354 @@ qla2x00_set_idma_speed(scsi_qla_host_t *ha, uint16_t loop_id, return rval; } + +/* + * qla24xx_get_vp_database + * Get the VP's database for all configured ports. + * + * Input: + * ha = adapter block pointer. + * size = size of initialization control block. + * + * Returns: + * qla2x00 local function return status code. + * + * Context: + * Kernel context. + */ +int +qla24xx_get_vp_database(scsi_qla_host_t *ha, uint16_t size) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + DEBUG11(printk("scsi(%ld):%s - entered.\n", + ha->host_no, __func__)); + + mcp->mb[0] = MBC_MID_GET_VP_DATABASE; + mcp->mb[2] = MSW(ha->init_cb_dma); + mcp->mb[3] = LSW(ha->init_cb_dma); + mcp->mb[4] = 0; + mcp->mb[5] = 0; + mcp->mb[6] = MSW(MSD(ha->init_cb_dma)); + mcp->mb[7] = LSW(MSD(ha->init_cb_dma)); + mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_0; + mcp->in_mb = MBX_1|MBX_0; + mcp->buf_size = size; + mcp->flags = MBX_DMA_OUT; + mcp->tov = MBX_TOV_SECONDS; + rval = qla2x00_mailbox_command(ha, mcp); + + if (rval != QLA_SUCCESS) { + /*EMPTY*/ + DEBUG2_3_11(printk("%s(%ld): failed=%x " + "mb0=%x.\n", + __func__, ha->host_no, rval, mcp->mb[0])); + } else { + /*EMPTY*/ + DEBUG11(printk("%s(%ld): done.\n", + __func__, ha->host_no)); + } + + return rval; +} + +int +qla24xx_get_vp_entry(scsi_qla_host_t *ha, uint16_t size, int vp_id) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + + mcp->mb[0] = MBC_MID_GET_VP_ENTRY; + mcp->mb[2] = MSW(ha->init_cb_dma); + mcp->mb[3] = LSW(ha->init_cb_dma); + mcp->mb[4] = 0; + mcp->mb[5] = 0; + mcp->mb[6] = MSW(MSD(ha->init_cb_dma)); + mcp->mb[7] = LSW(MSD(ha->init_cb_dma)); + mcp->mb[9] = vp_id; + mcp->out_mb = MBX_9|MBX_7|MBX_6|MBX_3|MBX_2|MBX_0; + mcp->in_mb = MBX_0; + mcp->buf_size = size; + mcp->flags = MBX_DMA_OUT; + mcp->tov = 30; + rval = qla2x00_mailbox_command(ha, mcp); + + if (rval != QLA_SUCCESS) { + /*EMPTY*/ + DEBUG2_3_11(printk("qla24xx_get_vp_entry(%ld): failed=%x " + "mb0=%x.\n", + ha->host_no, rval, mcp->mb[0])); + } else { + /*EMPTY*/ + DEBUG11(printk("qla24xx_get_vp_entry(%ld): done.\n", + ha->host_no)); + } + + return rval; +} + +void +qla24xx_report_id_acquisition(scsi_qla_host_t *ha, + struct vp_rpt_id_entry_24xx *rptid_entry) +{ + uint8_t vp_idx; + scsi_qla_host_t *vha; + + if (rptid_entry->entry_status != 0) + return; + if (rptid_entry->entry_status != __constant_cpu_to_le16(CS_COMPLETE)) + return; + + if (rptid_entry->format == 0) { + DEBUG15(printk("%s:format 0 : scsi(%ld) number of VPs setup %d," + " number of VPs acquired %d\n", __func__, ha->host_no, + MSB(rptid_entry->vp_count), LSB(rptid_entry->vp_count))); + DEBUG15(printk("%s primary port id %02x%02x%02x\n", __func__, + rptid_entry->port_id[2], rptid_entry->port_id[1], + rptid_entry->port_id[0])); + } else if (rptid_entry->format == 1) { + vp_idx = LSB(rptid_entry->vp_idx); + DEBUG15(printk("%s:format 1: scsi(%ld): VP[%d] enabled " + "- status %d - " + "with port id %02x%02x%02x\n",__func__,ha->host_no, + vp_idx, MSB(rptid_entry->vp_idx), + rptid_entry->port_id[2], rptid_entry->port_id[1], + rptid_entry->port_id[0])); + if (vp_idx == 0) + return; + + if (MSB(rptid_entry->vp_idx) == 1) + return; + + list_for_each_entry(vha, &ha->vp_list, vp_list) + if (vp_idx == vha->vp_idx) + break; + + if (!vha) + return; + + vha->d_id.b.domain = rptid_entry->port_id[2]; + vha->d_id.b.area = rptid_entry->port_id[1]; + vha->d_id.b.al_pa = rptid_entry->port_id[0]; + + /* + * Cannot configure here as we are still sitting on the + * response queue. Handle it in dpc context. + */ + set_bit(VP_IDX_ACQUIRED, &vha->vp_flags); + set_bit(VP_DPC_NEEDED, &ha->dpc_flags); + + wake_up_process(ha->dpc_thread); + } +} + +/* + * qla24xx_modify_vp_config + * Change VP configuration for vha + * + * Input: + * vha = adapter block pointer. + * + * Returns: + * qla2xxx local function return status code. + * + * Context: + * Kernel context. + */ +int +qla24xx_modify_vp_config(scsi_qla_host_t *vha) +{ + int rval; + struct vp_config_entry_24xx *vpmod; + dma_addr_t vpmod_dma; + scsi_qla_host_t *pha; + + /* This can be called by the parent */ + pha = to_qla_parent(vha); + + vpmod = dma_pool_alloc(pha->s_dma_pool, GFP_KERNEL, &vpmod_dma); + if (!vpmod) { + DEBUG2_3(printk("%s(%ld): failed to allocate Modify VP " + "IOCB.\n", __func__, pha->host_no)); + return QLA_MEMORY_ALLOC_FAILED; + } + + memset(vpmod, 0, sizeof(struct vp_config_entry_24xx)); + vpmod->entry_type = VP_CONFIG_IOCB_TYPE; + vpmod->entry_count = 1; + vpmod->command = VCT_COMMAND_MOD_ENABLE_VPS; + vpmod->vp_count = 1; + vpmod->vp_index1 = vha->vp_idx; + vpmod->options_idx1 = BIT_3|BIT_4|BIT_5; + memcpy(vpmod->node_name_idx1, vha->node_name, WWN_SIZE); + memcpy(vpmod->port_name_idx1, vha->port_name, WWN_SIZE); + vpmod->entry_count = 1; + + rval = qla2x00_issue_iocb(pha, vpmod, vpmod_dma, 0); + if (rval != QLA_SUCCESS) { + DEBUG2_3_11(printk("%s(%ld): failed to issue VP config IOCB" + "(%x).\n", __func__, pha->host_no, rval)); + } else if (vpmod->comp_status != 0) { + DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " + "-- error status (%x).\n", __func__, pha->host_no, + vpmod->comp_status)); + rval = QLA_FUNCTION_FAILED; + } else if (vpmod->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) { + DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " + "-- completion status (%x).\n", __func__, pha->host_no, + le16_to_cpu(vpmod->comp_status))); + rval = QLA_FUNCTION_FAILED; + } else { + /* EMPTY */ + DEBUG11(printk("%s(%ld): done.\n", __func__, pha->host_no)); + fc_vport_set_state(vha->fc_vport, FC_VPORT_INITIALIZING); + } + dma_pool_free(pha->s_dma_pool, vpmod, vpmod_dma); + + return rval; +} + +/* + * qla24xx_control_vp + * Enable a virtual port for given host + * + * Input: + * ha = adapter block pointer. + * vhba = virtual adapter (unused) + * index = index number for enabled VP + * + * Returns: + * qla2xxx local function return status code. + * + * Context: + * Kernel context. + */ +int +qla24xx_control_vp(scsi_qla_host_t *vha, int cmd) +{ + int rval; + int map, pos; + struct vp_ctrl_entry_24xx *vce; + dma_addr_t vce_dma; + scsi_qla_host_t *ha = vha->parent; + int vp_index = vha->vp_idx; + + DEBUG11(printk("%s(%ld): entered. Enabling index %d\n", __func__, + ha->host_no, vp_index)); + + if (vp_index == 0 || vp_index >= MAX_MULTI_ID_LOOP) + return QLA_PARAMETER_ERROR; + + vce = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &vce_dma); + if (!vce) { + DEBUG2_3(printk("%s(%ld): " + "failed to allocate VP Control IOCB.\n", __func__, + ha->host_no)); + return QLA_MEMORY_ALLOC_FAILED; + } + memset(vce, 0, sizeof(struct vp_ctrl_entry_24xx)); + + vce->entry_type = VP_CTRL_IOCB_TYPE; + vce->entry_count = 1; + vce->command = cpu_to_le16(cmd); + vce->vp_count = __constant_cpu_to_le16(1); + + /* index map in firmware starts with 1; decrement index + * this is ok as we never use index 0 + */ + map = (vp_index - 1) / 8; + pos = (vp_index - 1) & 7; + down(&ha->vport_sem); + vce->vp_idx_map[map] |= 1 << pos; + up(&ha->vport_sem); + + rval = qla2x00_issue_iocb(ha, vce, vce_dma, 0); + if (rval != QLA_SUCCESS) { + DEBUG2_3_11(printk("%s(%ld): failed to issue VP control IOCB" + "(%x).\n", __func__, ha->host_no, rval)); + printk("%s(%ld): failed to issue VP control IOCB" + "(%x).\n", __func__, ha->host_no, rval); + } else if (vce->entry_status != 0) { + DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " + "-- error status (%x).\n", __func__, ha->host_no, + vce->entry_status)); + printk("%s(%ld): failed to complete IOCB " + "-- error status (%x).\n", __func__, ha->host_no, + vce->entry_status); + rval = QLA_FUNCTION_FAILED; + } else if (vce->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) { + DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " + "-- completion status (%x).\n", __func__, ha->host_no, + le16_to_cpu(vce->comp_status))); + printk("%s(%ld): failed to complete IOCB " + "-- completion status (%x).\n", __func__, ha->host_no, + le16_to_cpu(vce->comp_status)); + rval = QLA_FUNCTION_FAILED; + } else { + DEBUG2(printk("%s(%ld): done.\n", __func__, ha->host_no)); + } + + dma_pool_free(ha->s_dma_pool, vce, vce_dma); + + return rval; +} + +/* + * qla2x00_send_change_request + * Receive or disable RSCN request from fabric controller + * + * Input: + * ha = adapter block pointer + * format = registration format: + * 0 - Reserved + * 1 - Fabric detected registration + * 2 - N_port detected registration + * 3 - Full registration + * FF - clear registration + * vp_idx = Virtual port index + * + * Returns: + * qla2x00 local function return status code. + * + * Context: + * Kernel Context + */ + +int +qla2x00_send_change_request(scsi_qla_host_t *ha, uint16_t format, + uint16_t vp_idx) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + /* + * This command is implicitly executed by firmware during login for the + * physical hosts + */ + if (vp_idx == 0) + return QLA_FUNCTION_FAILED; + + mcp->mb[0] = MBC_SEND_CHANGE_REQUEST; + mcp->mb[1] = format; + mcp->mb[9] = vp_idx; + mcp->out_mb = MBX_9|MBX_1|MBX_0; + mcp->in_mb = MBX_0|MBX_1; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(ha, mcp); + + if (rval == QLA_SUCCESS) { + if (mcp->mb[0] != MBS_COMMAND_COMPLETE) { + rval = BIT_1; + } + } else + rval = BIT_1; + + return rval; +} |