Re: [PATCH v2 18/24] mpi3mr: add complete support of soft reset

Tomas Henzl
2021-04-15T12:33:03Z

On 4/7/21 4:04 AM, Kashyap Desai wrote:
> Unlock the host diagnostic registers and write the specific
> reset type to that, wait for reset acknowledgment from the
> controller, if the reset is not successful retry for the
> predefined number of times
>
> Signed-off-by: Kashyap Desai <kashyap.desai@broadcom.com>
> Reviewed-by: Hannes Reinecke <hare@suse.de>
> Cc: sathya.prakash@broadcom.com
> ---
> drivers/scsi/mpi3mr/mpi3mr.h | 3 +
> drivers/scsi/mpi3mr/mpi3mr_fw.c | 245 +++++++++++++++++++++++++++++++-
> 2 files changed, 246 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/scsi/mpi3mr/mpi3mr.h b/drivers/scsi/mpi3mr/mpi3mr.h
> index 3ac7b0f119bb..ff3e68a6d0b5 100644
> --- a/drivers/scsi/mpi3mr/mpi3mr.h
> +++ b/drivers/scsi/mpi3mr/mpi3mr.h
> @@ -189,6 +189,9 @@ enum mpi3mr_reset_reason {
> MPI3MR_RESET_FROM_EVTACK_TIMEOUT = 19,
> MPI3MR_RESET_FROM_CIACTVRST_TIMER = 20,
> MPI3MR_RESET_FROM_GETPKGVER_TIMEOUT = 21,
> + MPI3MR_RESET_FROM_PELABORT_TIMEOUT = 22,
> + MPI3MR_RESET_FROM_SYSFS = 23,
> + MPI3MR_RESET_FROM_SYSFS_TIMEOUT = 24
> };
>
> /**
> diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c
> index 32b79876f0d2..79a9f98c736e 100644
> --- a/drivers/scsi/mpi3mr/mpi3mr_fw.c
> +++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c
> @@ -642,6 +642,100 @@ static const char *mpi3mr_iocstate_name(enum mpi3mr_iocstate mrioc_state)
> return name;
> }
>
> +/* Reset reason to name mapper structure*/
> +static const struct {
> + enum mpi3mr_reset_reason value;
> + char *name;
> +} mpi3mr_reset_reason_codes[] = {
> + { MPI3MR_RESET_FROM_BRINGUP, "timeout in bringup" },
> + { MPI3MR_RESET_FROM_FAULT_WATCH, "fault" },
> + { MPI3MR_RESET_FROM_IOCTL, "application invocation" },
> + { MPI3MR_RESET_FROM_EH_HOS, "error handling" },
> + { MPI3MR_RESET_FROM_TM_TIMEOUT, "TM timeout" },
> + { MPI3MR_RESET_FROM_IOCTL_TIMEOUT, "IOCTL timeout" },
> + { MPI3MR_RESET_FROM_MUR_FAILURE, "MUR failure" },
> + { MPI3MR_RESET_FROM_CTLR_CLEANUP, "timeout in controller cleanup" },
> + { MPI3MR_RESET_FROM_CIACTIV_FAULT, "component image activation fault" },
> + { MPI3MR_RESET_FROM_PE_TIMEOUT, "port enable timeout" },
> + { MPI3MR_RESET_FROM_TSU_TIMEOUT, "time stamp update timeout" },
> + { MPI3MR_RESET_FROM_DELREQQ_TIMEOUT, "delete request queue timeout" },
> + { MPI3MR_RESET_FROM_DELREPQ_TIMEOUT, "delete reply queue timeout" },
> + {
> + MPI3MR_RESET_FROM_CREATEREPQ_TIMEOUT,
> + "create request queue timeout"
> + },
> + {
> + MPI3MR_RESET_FROM_CREATEREQQ_TIMEOUT,
> + "create reply queue timeout"
> + },
> + { MPI3MR_RESET_FROM_IOCFACTS_TIMEOUT, "IOC facts timeout" },
> + { MPI3MR_RESET_FROM_IOCINIT_TIMEOUT, "IOC init timeout" },
> + { MPI3MR_RESET_FROM_EVTNOTIFY_TIMEOUT, "event notify timeout" },
> + { MPI3MR_RESET_FROM_EVTACK_TIMEOUT, "event acknowledgment timeout" },
> + {
> + MPI3MR_RESET_FROM_CIACTVRST_TIMER,
> + "component image activation timeout"
> + },
> + {
> + MPI3MR_RESET_FROM_GETPKGVER_TIMEOUT,
> + "get package version timeout"
> + },
> + { MPI3MR_RESET_FROM_SYSFS, "sysfs invocation" },
> + { MPI3MR_RESET_FROM_SYSFS_TIMEOUT, "sysfs TM timeout" },
> +};
> +
> +/**
> + * mpi3mr_reset_rc_name - get reset reason code name
> + * @reason_code: reset reason code value
> + *
> + * Map reset reason to an NULL terminated ASCII string
> + *
> + * Return: Name corresponding to reset reason value or NULL.
> + */
> +static const char *mpi3mr_reset_rc_name(enum mpi3mr_reset_reason reason_code)
> +{
> + int i;
> + char *name = NULL;
> +
> + for (i = 0; i < ARRAY_SIZE(mpi3mr_reset_reason_codes); i++) {
> + if (mpi3mr_reset_reason_codes[i].value == reason_code) {
> + name = mpi3mr_reset_reason_codes[i].name;
> + break;
> + }
> + }
> + return name;
> +}
> +
> +/* Reset type to name mapper structure*/
> +static const struct {
> + u16 reset_type;
> + char *name;
> +} mpi3mr_reset_types[] = {
> + { MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET, "soft" },
> + { MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT, "diag fault" },
> +};
> +
> +/**
> + * mpi3mr_reset_type_name - get reset type name
> + * reset_type: reset type value
> + *
> + * Map reset type to an NULL terminated ASCII string
> + *
> + * Return: Name corresponding to reset type value or NULL.
> + */
> +static const char *mpi3mr_reset_type_name(u16 reset_type)
> +{
> + int i;
> + char *name = NULL;
> +
> + for (i = 0; i < ARRAY_SIZE(mpi3mr_reset_types); i++) {
> + if (mpi3mr_reset_types[i].reset_type == reset_type) {
> + name = mpi3mr_reset_types[i].name;
> + break;
> + }
> + }
> + return name;
> +}
>
> /**
> * mpi3mr_print_fault_info - Display fault information
> @@ -805,6 +899,48 @@ static int mpi3mr_bring_ioc_ready(struct mpi3mr_ioc *mrioc)
> return -1;
> }
>
> +/**
> + * mpi3mr_soft_reset_success - Check softreset is success or not
> + * @ioc_status: IOC status register value
> + * @ioc_config: IOC config register value
> + *
> + * Check whether the soft reset is successful or not based on
> + * IOC status and IOC config register values.
> + *
> + * Return: True when the soft reset is success, false otherwise.
> + */
> +static inline bool
> +mpi3mr_soft_reset_success(u32 ioc_status, u32 ioc_config)
> +{
> + if (!((ioc_status & MPI3_SYSIF_IOC_STATUS_READY) ||
> + (ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT) ||
> + (ioc_config & MPI3_SYSIF_IOC_CONFIG_ENABLE_IOC)))
> + return true;
> + return false;
> +}
> +
> +/**
> + * mpi3mr_diagfault_success - Check diag fault is success or not
> + * @mrioc: Adapter reference
> + * @ioc_status: IOC status register value
> + *
> + * Check whether the controller hit diag reset fault code.
> + *
> + * Return: True when there is diag fault, false otherwise.
> + */
> +static inline bool mpi3mr_diagfault_success(struct mpi3mr_ioc *mrioc,
> + u32 ioc_status)
> +{
> + u32 fault;
> +
> + if (!(ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT))
> + return false;
> + fault = readl(&mrioc->sysif_regs->Fault) & MPI3_SYSIF_FAULT_CODE_MASK;
> + if (fault == MPI3_SYSIF_FAULT_CODE_DIAG_FAULT_RESET)
> + return true;
> + return false;
> +}
> +
> /**
> * mpi3mr_set_diagsave - Set diag save bit for snapdump
> * @mrioc: Adapter reference
> @@ -829,14 +965,117 @@ static inline void mpi3mr_set_diagsave(struct mpi3mr_ioc *mrioc)
> * @reset_type: Reset type
> * @reset_reason: Reset reason code
> *
> - * TBD
> + * Unlock the host diagnostic registers and write the specific
> + * reset type to that, wait for reset acknowledgment from the
> + * controller, if the reset is not successful retry for the
> + * predefined number of times.
> *
> * Return: 0 on success, non-zero on failure.
> */
> static int mpi3mr_issue_reset(struct mpi3mr_ioc *mrioc, u16 reset_type,
> u32 reset_reason)
> {
> - return 0;
> + int retval = -1;
> + u8 unlock_retry_count, reset_retry_count = 0;
> + u32 host_diagnostic, timeout, ioc_status, ioc_config;
> +
> + pci_cfg_access_lock(mrioc->pdev);
> + if ((reset_type != MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET) &&
> + (reset_type != MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT))
> + goto out;
> + if (mrioc->unrecoverable)
> + goto out;
> +retry_reset:
> + unlock_retry_count = 0;
> + mpi3mr_clear_reset_history(mrioc);
> + do {
> + ioc_info(mrioc,
> + "Write magic sequence to unlock host diag register (retry=%d)\n",
> + ++unlock_retry_count);
> + if (unlock_retry_count >= MPI3MR_HOSTDIAG_UNLOCK_RETRY_COUNT) {
> + writel(reset_reason, &mrioc->sysif_regs->Scratchpad[0]);
> + mrioc->unrecoverable = 1;
> + goto out;
> + }
> +
> + writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_FLUSH,
> + &mrioc->sysif_regs->WriteSequence);
> + writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_1ST,
> + &mrioc->sysif_regs->WriteSequence);
> + writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_2ND,
> + &mrioc->sysif_regs->WriteSequence);
> + writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_3RD,
> + &mrioc->sysif_regs->WriteSequence);
> + writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_4TH,
> + &mrioc->sysif_regs->WriteSequence);
> + writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_5TH,
> + &mrioc->sysif_regs->WriteSequence);
> + writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_6TH,
> + &mrioc->sysif_regs->WriteSequence);
> + usleep_range(1000, 1100);
> + host_diagnostic = readl(&mrioc->sysif_regs->HostDiagnostic);
> + ioc_info(mrioc,
> + "wrote magic sequence: retry_count(%d), host_diagnostic(0x%08x)\n",
> + unlock_retry_count, host_diagnostic);
> + } while (!(host_diagnostic & MPI3_SYSIF_HOST_DIAG_DIAG_WRITE_ENABLE));
> +
> + writel(reset_reason, &mrioc->sysif_regs->Scratchpad[0]);
> + ioc_info(mrioc, "%s reset due to %s(0x%x)\n",
> + mpi3mr_reset_type_name(reset_type),
> + mpi3mr_reset_rc_name(reset_reason), reset_reason);
> + writel(host_diagnostic | reset_type,
> + &mrioc->sysif_regs->HostDiagnostic);
> + timeout = mrioc->ready_timeout * 10;
> + if (reset_type == MPI3_SYSIF_HOST_DIAG_RESET_ACTION_SOFT_RESET) {
> + do {
> + ioc_status = readl(&mrioc->sysif_regs->IOCStatus);
> + if (ioc_status &
> + MPI3_SYSIF_IOC_STATUS_RESET_HISTORY) {
> + mpi3mr_clear_reset_history(mrioc);
> + ioc_config =
> + readl(&mrioc->sysif_regs->IOCConfiguration);
> + if (mpi3mr_soft_reset_success(ioc_status,
> + ioc_config)) {
> + retval = 0;
> + break;
> + }
> + }
> + msleep(100);
> + } while (--timeout);
> + writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_2ND,
> + &mrioc->sysif_regs->WriteSequence);
> + } else if (reset_type == MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT) {
> + do {
> + ioc_status = readl(&mrioc->sysif_regs->IOCStatus);
> + if (mpi3mr_diagfault_success(mrioc, ioc_status)) {
> + retval = 0;
> + break;
> + }
> + msleep(100);
> + } while (--timeout);
> + mpi3mr_clear_reset_history(mrioc);
> + writel(MPI3_SYSIF_WRITE_SEQUENCE_KEY_VALUE_2ND,
> + &mrioc->sysif_regs->WriteSequence);
> + }
> + if (retval && ((++reset_retry_count) < MPI3MR_MAX_RESET_RETRY_COUNT)) {
> + ioc_status = readl(&mrioc->sysif_regs->IOCStatus);
> + ioc_config = readl(&mrioc->sysif_regs->IOCConfiguration);
> + ioc_info(mrioc,
> + "Base IOC Sts/Config after reset try %d is (0x%x)/(0x%x)\n",
> + reset_retry_count, ioc_status, ioc_config);
> + goto retry_reset;
> + }
> +
> +out:
> + pci_cfg_access_unlock(mrioc->pdev);
> + ioc_status = readl(&mrioc->sysif_regs->IOCStatus);
> + ioc_config = readl(&mrioc->sysif_regs->IOCConfiguration);
> +
> + ioc_info(mrioc,
> + "Base IOC Sts/Config after %s reset is (0x%x)/(0x%x)\n",
> + (!retval)?"successful":"failed", ioc_status,
> + ioc_config);
> + return retval;
> }
>
> /**
> @@ -3458,6 +3697,8 @@ int mpi3mr_diagfault_reset_handler(struct mpi3mr_ioc *mrioc,
> {
> int retval = 0;
>
> + ioc_info(mrioc, "Entry: reason code: %s\n",
> + mpi3mr_reset_rc_name(reset_reason));
> mrioc->reset_in_progress = 1;
>
> mpi3mr_ioc_disable_intr(mrioc);
>

Looks good

Reviewed-by: Tomas Henzl <thenzl@redhat.com>