diff options
Diffstat (limited to 'drivers/scsi/qla2xxx/qla_nx.c')
-rw-r--r-- | drivers/scsi/qla2xxx/qla_nx.c | 132 |
1 files changed, 131 insertions, 1 deletions
diff --git a/drivers/scsi/qla2xxx/qla_nx.c b/drivers/scsi/qla2xxx/qla_nx.c index ae2acacc0003..9175e847b93a 100644 --- a/drivers/scsi/qla2xxx/qla_nx.c +++ b/drivers/scsi/qla2xxx/qla_nx.c @@ -2343,6 +2343,17 @@ qla82xx_set_qsnt_ready(struct qla_hw_data *ha) qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, qsnt_state); } +void +qla82xx_clear_qsnt_ready(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t qsnt_state; + + qsnt_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); + qsnt_state &= ~(QLA82XX_DRVST_QSNT_RDY << (ha->portnum * 4)); + qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, qsnt_state); +} + static int qla82xx_load_fw(scsi_qla_host_t *vha) { @@ -3261,6 +3272,104 @@ dev_ready: return QLA_SUCCESS; } +/* +* qla82xx_need_qsnt_handler +* Code to start quiescence sequence +* +* Note: +* IDC lock must be held upon entry +* +* Return: void +*/ + +static void +qla82xx_need_qsnt_handler(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t dev_state, drv_state, drv_active; + unsigned long reset_timeout; + + if (vha->flags.online) { + /*Block any further I/O and wait for pending cmnds to complete*/ + qla82xx_quiescent_state_cleanup(vha); + } + + /* Set the quiescence ready bit */ + qla82xx_set_qsnt_ready(ha); + + /*wait for 30 secs for other functions to ack */ + reset_timeout = jiffies + (30 * HZ); + + drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); + drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); + /* Its 2 that is written when qsnt is acked, moving one bit */ + drv_active = drv_active << 0x01; + + while (drv_state != drv_active) { + + if (time_after_eq(jiffies, reset_timeout)) { + /* quiescence timeout, other functions didn't ack + * changing the state to DEV_READY + */ + qla_printk(KERN_INFO, ha, + "%s: QUIESCENT TIMEOUT\n", QLA2XXX_DRIVER_NAME); + qla_printk(KERN_INFO, ha, + "DRV_ACTIVE:%d DRV_STATE:%d\n", drv_active, + drv_state); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, + QLA82XX_DEV_READY); + qla_printk(KERN_INFO, ha, + "HW State: DEV_READY\n"); + qla82xx_idc_unlock(ha); + qla2x00_perform_loop_resync(vha); + qla82xx_idc_lock(ha); + + qla82xx_clear_qsnt_ready(vha); + return; + } + + qla82xx_idc_unlock(ha); + msleep(1000); + qla82xx_idc_lock(ha); + + drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); + drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); + drv_active = drv_active << 0x01; + } + dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); + /* everyone acked so set the state to DEV_QUIESCENCE */ + if (dev_state == QLA82XX_DEV_NEED_QUIESCENT) { + qla_printk(KERN_INFO, ha, "HW State: DEV_QUIESCENT\n"); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_QUIESCENT); + } +} + +/* +* qla82xx_wait_for_state_change +* Wait for device state to change from given current state +* +* Note: +* IDC lock must not be held upon entry +* +* Return: +* Changed device state. +*/ +uint32_t +qla82xx_wait_for_state_change(scsi_qla_host_t *vha, uint32_t curr_state) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t dev_state; + + do { + msleep(1000); + qla82xx_idc_lock(ha); + dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); + qla82xx_idc_unlock(ha); + } while (dev_state == curr_state); + + return dev_state; +} + static void qla82xx_dev_failed_handler(scsi_qla_host_t *vha) { @@ -3443,11 +3552,25 @@ qla82xx_device_state_handler(scsi_qla_host_t *vha) qla82xx_need_reset_handler(vha); break; case QLA82XX_DEV_NEED_QUIESCENT: - qla82xx_set_qsnt_ready(ha); + qla82xx_need_qsnt_handler(vha); + /* Reset timeout value after quiescence handler */ + dev_init_timeout = jiffies + (ha->nx_dev_init_timeout\ + * HZ); + break; case QLA82XX_DEV_QUIESCENT: + /* Owner will exit and other will wait for the state + * to get changed + */ + if (ha->flags.quiesce_owner) + goto exit; + qla82xx_idc_unlock(ha); msleep(1000); qla82xx_idc_lock(ha); + + /* Reset timeout value after quiescence handler */ + dev_init_timeout = jiffies + (ha->nx_dev_init_timeout\ + * HZ); break; case QLA82XX_DEV_FAILED: qla82xx_dev_failed_handler(vha); @@ -3490,6 +3613,13 @@ void qla82xx_watchdog(scsi_qla_host_t *vha) &ha->mbx_cmd_flags)) complete(&ha->mbx_intr_comp); } + } else if (dev_state == QLA82XX_DEV_NEED_QUIESCENT && + !test_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags)) { + DEBUG(qla_printk(KERN_INFO, ha, + "scsi(%ld) %s - detected quiescence needed\n", + vha->host_no, __func__)); + set_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); } else { qla82xx_check_fw_alive(vha); } |