int megasas_reset_fusion()

in megaraid/megaraid_sas_fusion.c [4903:5192]


int megasas_reset_fusion(struct Scsi_Host *shost, int reason)
{
	int retval = SUCCESS, i, j, convert = 0;
	struct megasas_instance *instance;
	struct megasas_cmd_fusion *cmd_fusion, *r1_cmd;
	struct fusion_context *fusion;
	u32 abs_state, status_reg, reset_adapter, fpio_count = 0;
	u32 io_timeout_in_crash_mode = 0;
	struct scsi_cmnd *scmd_local = NULL;
	struct scsi_device *sdev;
	int ret_target_prop = DCMD_FAILED;
	bool is_target_prop = false;
	bool do_adp_reset = true;
	int max_reset_tries = MEGASAS_FUSION_MAX_RESET_TRIES;

	instance = (struct megasas_instance *)shost->hostdata;
	fusion = instance->ctrl_context;

	mutex_lock(&instance->reset_mutex);

	if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) {
		dev_warn(&instance->pdev->dev, "Hardware critical error, "
		       "returning FAILED for scsi%d.\n",
			instance->host->host_no);
		mutex_unlock(&instance->reset_mutex);
		return FAILED;
	}
	status_reg = instance->instancet->read_fw_status_reg(instance);
	abs_state = status_reg & MFI_STATE_MASK;

	/* IO timeout detected, forcibly put FW in FAULT state */
	if (abs_state != MFI_STATE_FAULT && instance->crash_dump_buf &&
		instance->crash_dump_app_support && reason) {
		dev_info(&instance->pdev->dev, "IO/DCMD timeout is detected, "
			"forcibly FAULT Firmware\n");
		atomic_set(&instance->adprecovery, MEGASAS_ADPRESET_SM_INFAULT);
		status_reg = megasas_readl(instance, &instance->reg_set->doorbell);
		writel(status_reg | MFI_STATE_FORCE_OCR,
			&instance->reg_set->doorbell);
		readl(&instance->reg_set->doorbell);
		mutex_unlock(&instance->reset_mutex);
		do {
			ssleep(3);
			io_timeout_in_crash_mode++;
			dev_dbg(&instance->pdev->dev, "waiting for [%d] "
				"seconds for crash dump collection and OCR "
				"to be done\n", (io_timeout_in_crash_mode * 3));
		} while ((atomic_read(&instance->adprecovery) != MEGASAS_HBA_OPERATIONAL) &&
			(io_timeout_in_crash_mode < 80));

		if (atomic_read(&instance->adprecovery) == MEGASAS_HBA_OPERATIONAL) {
			dev_info(&instance->pdev->dev, "OCR done for IO "
				"timeout case\n");
			retval = SUCCESS;
		} else {
			dev_info(&instance->pdev->dev, "Controller is not "
				"operational after 240 seconds wait for IO "
				"timeout case in FW crash dump mode\n do "
				"OCR/kill adapter\n");
			retval = megasas_reset_fusion(shost, 0);
		}
		return retval;
	}

	if (instance->requestorId && !instance->skip_heartbeat_timer_del)
		del_timer_sync(&instance->sriov_heartbeat_timer);
	set_bit(MEGASAS_FUSION_IN_RESET, &instance->reset_flags);
	set_bit(MEGASAS_FUSION_OCR_NOT_POSSIBLE, &instance->reset_flags);
	atomic_set(&instance->adprecovery, MEGASAS_ADPRESET_SM_POLLING);
	instance->instancet->disable_intr(instance);
	megasas_sync_irqs((unsigned long)instance);

	/* First try waiting for commands to complete */
	if (megasas_wait_for_outstanding_fusion(instance, reason,
						&convert)) {
		atomic_set(&instance->adprecovery, MEGASAS_ADPRESET_SM_INFAULT);
		dev_warn(&instance->pdev->dev, "resetting fusion "
		       "adapter scsi%d.\n", instance->host->host_no);
		if (convert)
			reason = 0;

		if (megasas_dbg_lvl & OCR_DEBUG)
			dev_info(&instance->pdev->dev, "\nPending SCSI commands:\n");

		/* Now return commands back to the OS */
		for (i = 0 ; i < instance->max_scsi_cmds; i++) {
			cmd_fusion = fusion->cmd_list[i];
			/*check for extra commands issued by driver*/
			if (instance->adapter_type >= VENTURA_SERIES) {
				r1_cmd = fusion->cmd_list[i + instance->max_fw_cmds];
				megasas_return_cmd_fusion(instance, r1_cmd);
			}
			scmd_local = cmd_fusion->scmd;
			if (cmd_fusion->scmd) {
				if (megasas_dbg_lvl & OCR_DEBUG) {
					sdev_printk(KERN_INFO,
						cmd_fusion->scmd->device, "SMID: 0x%x\n",
						cmd_fusion->index);
					megasas_dump_fusion_io(cmd_fusion->scmd);
				}

				if (cmd_fusion->io_request->Function ==
					MPI2_FUNCTION_SCSI_IO_REQUEST)
					fpio_count++;

				scmd_local->result =
					megasas_check_mpio_paths(instance,
							scmd_local);
				if (instance->ldio_threshold &&
					megasas_cmd_type(scmd_local) == READ_WRITE_LDIO)
					atomic_dec(&instance->ldio_outstanding);
				megasas_return_cmd_fusion(instance, cmd_fusion);
				scsi_dma_unmap(scmd_local);
				scsi_done(scmd_local);
			}
		}

		dev_info(&instance->pdev->dev, "Outstanding fastpath IOs: %d\n",
			fpio_count);

		atomic_set(&instance->fw_outstanding, 0);

		status_reg = instance->instancet->read_fw_status_reg(instance);
		abs_state = status_reg & MFI_STATE_MASK;
		reset_adapter = status_reg & MFI_RESET_ADAPTER;
		if (instance->disableOnlineCtrlReset ||
		    (abs_state == MFI_STATE_FAULT && !reset_adapter)) {
			/* Reset not supported, kill adapter */
			dev_warn(&instance->pdev->dev, "Reset not supported"
			       ", killing adapter scsi%d.\n",
				instance->host->host_no);
			goto kill_hba;
		}

		/* Let SR-IOV VF & PF sync up if there was a HB failure */
		if (instance->requestorId && !reason) {
			msleep(MEGASAS_OCR_SETTLE_TIME_VF);
			do_adp_reset = false;
			max_reset_tries = MEGASAS_SRIOV_MAX_RESET_TRIES_VF;
		}

		/* Now try to reset the chip */
		for (i = 0; i < max_reset_tries; i++) {
			/*
			 * Do adp reset and wait for
			 * controller to transition to ready
			 */
			if (megasas_adp_reset_wait_for_ready(instance,
				do_adp_reset, 1) == FAILED)
				continue;

			/* Wait for FW to become ready */
			if (megasas_transition_to_ready(instance, 1)) {
				dev_warn(&instance->pdev->dev,
					"Failed to transition controller to ready for "
					"scsi%d.\n", instance->host->host_no);
				continue;
			}
			megasas_reset_reply_desc(instance);
			megasas_fusion_update_can_queue(instance, OCR_CONTEXT);

			if (megasas_ioc_init_fusion(instance)) {
				continue;
			}

			if (megasas_get_ctrl_info(instance)) {
				dev_info(&instance->pdev->dev,
					"Failed from %s %d\n",
					__func__, __LINE__);
				goto kill_hba;
			}

			megasas_refire_mgmt_cmd(instance,
						(i == (MEGASAS_FUSION_MAX_RESET_TRIES - 1)
							? 1 : 0));

			/* Reset load balance info */
			if (fusion->load_balance_info)
				memset(fusion->load_balance_info, 0,
				       (sizeof(struct LD_LOAD_BALANCE_INFO) *
				       MAX_LOGICAL_DRIVES_EXT));

			if (!megasas_get_map_info(instance)) {
				megasas_sync_map_info(instance);
			} else {
				/*
				 * Return pending polled mode cmds before
				 * retrying OCR
				 */
				megasas_return_polled_cmds(instance);
				continue;
			}

			megasas_setup_jbod_map(instance);

			/* reset stream detection array */
			if (instance->adapter_type >= VENTURA_SERIES) {
				for (j = 0; j < MAX_LOGICAL_DRIVES_EXT; ++j) {
					memset(fusion->stream_detect_by_ld[j],
					0, sizeof(struct LD_STREAM_DETECT));
				 fusion->stream_detect_by_ld[j]->mru_bit_map
						= MR_STREAM_BITMAP;
				}
			}

			clear_bit(MEGASAS_FUSION_IN_RESET,
				  &instance->reset_flags);
			instance->instancet->enable_intr(instance);
			megasas_enable_irq_poll(instance);
			shost_for_each_device(sdev, shost) {
				if ((instance->tgt_prop) &&
				    (instance->nvme_page_size))
					ret_target_prop = megasas_get_target_prop(instance, sdev);

				is_target_prop = (ret_target_prop == DCMD_SUCCESS) ? true : false;
				megasas_set_dynamic_target_properties(sdev, is_target_prop);
			}

			status_reg = instance->instancet->read_fw_status_reg
					(instance);
			abs_state = status_reg & MFI_STATE_MASK;
			if (abs_state != MFI_STATE_OPERATIONAL) {
				dev_info(&instance->pdev->dev,
					 "Adapter is not OPERATIONAL, state 0x%x for scsi:%d\n",
					 abs_state, instance->host->host_no);
				goto out;
			}
			atomic_set(&instance->adprecovery, MEGASAS_HBA_OPERATIONAL);

			dev_info(&instance->pdev->dev,
				 "Adapter is OPERATIONAL for scsi:%d\n",
				 instance->host->host_no);

			/* Restart SR-IOV heartbeat */
			if (instance->requestorId) {
				if (!megasas_sriov_start_heartbeat(instance, 0))
					megasas_start_timer(instance);
				else
					instance->skip_heartbeat_timer_del = 1;
			}

			if (instance->crash_dump_drv_support &&
				instance->crash_dump_app_support)
				megasas_set_crash_dump_params(instance,
					MR_CRASH_BUF_TURN_ON);
			else
				megasas_set_crash_dump_params(instance,
					MR_CRASH_BUF_TURN_OFF);

			if (instance->snapdump_wait_time) {
				megasas_get_snapdump_properties(instance);
				dev_info(&instance->pdev->dev,
					 "Snap dump wait time\t: %d\n",
					 instance->snapdump_wait_time);
			}

			retval = SUCCESS;

			/* Adapter reset completed successfully */
			dev_warn(&instance->pdev->dev,
				 "Reset successful for scsi%d.\n",
				 instance->host->host_no);

			goto out;
		}
		/* Reset failed, kill the adapter */
		dev_warn(&instance->pdev->dev, "Reset failed, killing "
		       "adapter scsi%d.\n", instance->host->host_no);
		goto kill_hba;
	} else {
		/* For VF: Restart HB timer if we didn't OCR */
		if (instance->requestorId) {
			megasas_start_timer(instance);
		}
		clear_bit(MEGASAS_FUSION_IN_RESET, &instance->reset_flags);
		instance->instancet->enable_intr(instance);
		megasas_enable_irq_poll(instance);
		atomic_set(&instance->adprecovery, MEGASAS_HBA_OPERATIONAL);
		goto out;
	}
kill_hba:
	megaraid_sas_kill_hba(instance);
	megasas_enable_irq_poll(instance);
	instance->skip_heartbeat_timer_del = 1;
	retval = FAILED;
out:
	clear_bit(MEGASAS_FUSION_OCR_NOT_POSSIBLE, &instance->reset_flags);
	mutex_unlock(&instance->reset_mutex);
	return retval;
}