diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c index 79babab8353f..be299c83e07e 100644 --- a/drivers/scsi/qla2xxx/qla_bsg.c +++ b/drivers/scsi/qla2xxx/qla_bsg.c @@ -531,66 +531,11 @@ done_unmap_sg: done: return rval; } -/* - * Set the port configuration to enable the internal or external loopback - * depending on the loopback mode. - */ -static inline int -qla81xx_set_loopback_mode(scsi_qla_host_t *vha, uint16_t *config, - uint16_t *new_config, uint16_t mode) -{ - int ret = 0; - int rval = 0; - struct qla_hw_data *ha = vha->hw; - - if (!IS_QLA81XX(ha) && !IS_QLA8031(ha)) - goto done_set_internal; - - if (mode == INTERNAL_LOOPBACK) - new_config[0] = config[0] | (ENABLE_INTERNAL_LOOPBACK << 1); - else if (mode == EXTERNAL_LOOPBACK) - new_config[0] = config[0] | (ENABLE_EXTERNAL_LOOPBACK << 1); - ql_dbg(ql_dbg_user, vha, 0x70be, - "new_config[0]=%02x\n", (new_config[0] & INTERNAL_LOOPBACK_MASK)); - - 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) { - ql_log(ql_log_warn, vha, 0x7021, - "set port config failed.\n"); - 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))) { - ql_dbg(ql_dbg_user, vha, 0x7022, - "State change notification not received.\n"); - rval = -EINVAL; - } else { - if (ha->flags.idc_compl_status) { - ql_dbg(ql_dbg_user, vha, 0x70c3, - "Bad status in IDC Completion AEN\n"); - rval = -EINVAL; - ha->flags.idc_compl_status = 0; - } else - ql_dbg(ql_dbg_user, vha, 0x7023, - "State change received.\n"); - } - - ha->notify_dcbx_comp = 0; - -done_set_internal: - return rval; -} /* Disable loopback mode */ static inline int qla81xx_reset_loopback_mode(scsi_qla_host_t *vha, uint16_t *config, - int wait) + int wait) { int ret = 0; int rval = 0; @@ -638,6 +583,71 @@ done_reset_internal: return rval; } +/* + * Set the port configuration to enable the internal or external loopback + * depending on the loopback mode. + */ +static inline int +qla81xx_set_loopback_mode(scsi_qla_host_t *vha, uint16_t *config, + uint16_t *new_config, uint16_t mode) +{ + int ret = 0; + int rval = 0; + struct qla_hw_data *ha = vha->hw; + + if (!IS_QLA81XX(ha) && !IS_QLA8031(ha)) + goto done_set_internal; + + if (mode == INTERNAL_LOOPBACK) + new_config[0] = config[0] | (ENABLE_INTERNAL_LOOPBACK << 1); + else if (mode == EXTERNAL_LOOPBACK) + new_config[0] = config[0] | (ENABLE_EXTERNAL_LOOPBACK << 1); + ql_dbg(ql_dbg_user, vha, 0x70be, + "new_config[0]=%02x\n", (new_config[0] & INTERNAL_LOOPBACK_MASK)); + + 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) { + ql_log(ql_log_warn, vha, 0x7021, + "set port config failed.\n"); + 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))) { + ql_dbg(ql_dbg_user, vha, 0x7022, + "State change notification not received.\n"); + ret = qla81xx_reset_loopback_mode(vha, new_config, 0); + /* + * If the reset of the loopback mode doesn't work take a FCoE + * dump and reset the chip. + */ + if (ret) { + ha->isp_ops->fw_dump(vha, 0); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + } + rval = -EINVAL; + } else { + if (ha->flags.idc_compl_status) { + ql_dbg(ql_dbg_user, vha, 0x70c3, + "Bad status in IDC Completion AEN\n"); + rval = -EINVAL; + ha->flags.idc_compl_status = 0; + } else + ql_dbg(ql_dbg_user, vha, 0x7023, + "State change received.\n"); + } + + ha->notify_dcbx_comp = 0; + +done_set_internal: + return rval; +} + static int qla2x00_process_loopback(struct fc_bsg_job *bsg_job) { @@ -781,11 +791,24 @@ qla2x00_process_loopback(struct fc_bsg_job *bsg_job) rval = qla2x00_loopback_test(vha, &elreq, response); if (new_config[0]) { + int ret; + /* Revert back to original port config * Also clear internal loopback */ - qla81xx_reset_loopback_mode(vha, + ret = qla81xx_reset_loopback_mode(vha, new_config, 0); + if (ret) { + /* + * If the reset of the loopback mode + * doesn't work take FCoE dump and then + * reset the chip. + */ + ha->isp_ops->fw_dump(vha, 0); + set_bit(ISP_ABORT_NEEDED, + &vha->dpc_flags); + } + } if (response[0] == MBS_COMMAND_ERROR && diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 26a3086a7e3a..9380d961616c 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -985,13 +985,21 @@ skip_rio: mb[1], mb[2], mb[3]); break; case MBA_IDC_NOTIFY: - /* See if we need to quiesce any I/O */ - if (IS_QLA8031(vha->hw)) - if ((mb[2] & 0x7fff) == MBC_PORT_RESET || - (mb[2] & 0x7fff) == MBC_SET_PORT_CONFIG) { + if (IS_QLA8031(vha->hw)) { + mb[4] = RD_REG_WORD(®24->mailbox4); + if (((mb[2] & 0x7fff) == MBC_PORT_RESET || + (mb[2] & 0x7fff) == MBC_SET_PORT_CONFIG) && + (mb[4] & INTERNAL_LOOPBACK_MASK) != 0) { set_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags); + /* + * Extend loop down timer since port is active. + */ + if (atomic_read(&vha->loop_state) == LOOP_DOWN) + atomic_set(&vha->loop_down_timer, + LOOP_DOWN_TIME); qla2xxx_wake_dpc(vha); } + } case MBA_IDC_COMPLETE: case MBA_IDC_TIME_EXT: if (IS_QLA81XX(vha->hw) || IS_QLA8031(vha->hw))