scsi: mpt3sas: Reload SBR without rebooting HBA

Add a new IOCTL command MPT3ENABLEDIAGSBRRELOAD. As a part of firmware
update operation, applications use this IOCTL command to set the SBR reload
bit in the Host Diagnostic register. This permits HBA firmware to be
updated without powercycling the system.

Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202312280909.MZyhxwBL-lkp@intel.com/
Closes: https://lore.kernel.org/oe-kbuild-all/202312281141.jDyPezRn-lkp@intel.com/
Signed-off-by: Ranjan Kumar <ranjan.kumar@broadcom.com>
Link: https://lore.kernel.org/r/20231228114810.11923-2-ranjan.kumar@broadcom.com
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
Ranjan Kumar 2023-12-28 17:18:09 +05:30 committed by Martin K. Petersen
parent 10a39667a1
commit c0767560b0
5 changed files with 143 additions and 39 deletions

View File

@ -5481,7 +5481,7 @@ mpt3sas_atto_validate_nvram(struct MPT3SAS_ADAPTER *ioc,
* mpt3sas_atto_get_sas_addr - get the ATTO SAS address from mfg page 1
*
* @ioc : per adapter object
* @*sas_addr : return sas address
* @sas_addr : return sas address
* Return: 0 for success, non-zero for failure.
*/
static int
@ -7913,6 +7913,67 @@ mpt3sas_base_validate_event_type(struct MPT3SAS_ADAPTER *ioc, u32 *event_type)
mutex_unlock(&ioc->base_cmds.mutex);
}
/**
* mpt3sas_base_unlock_and_get_host_diagnostic- enable Host Diagnostic Register writes
* @ioc: per adapter object
* @host_diagnostic: host diagnostic register content
*
* Return: 0 for success, non-zero for failure.
*/
int
mpt3sas_base_unlock_and_get_host_diagnostic(struct MPT3SAS_ADAPTER *ioc,
u32 *host_diagnostic)
{
u32 count;
*host_diagnostic = 0;
count = 0;
do {
/* Write magic sequence to WriteSequence register
* Loop until in diagnostic mode
*/
drsprintk(ioc, ioc_info(ioc, "write magic sequence\n"));
writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, &ioc->chip->WriteSequence);
writel(MPI2_WRSEQ_1ST_KEY_VALUE, &ioc->chip->WriteSequence);
writel(MPI2_WRSEQ_2ND_KEY_VALUE, &ioc->chip->WriteSequence);
writel(MPI2_WRSEQ_3RD_KEY_VALUE, &ioc->chip->WriteSequence);
writel(MPI2_WRSEQ_4TH_KEY_VALUE, &ioc->chip->WriteSequence);
writel(MPI2_WRSEQ_5TH_KEY_VALUE, &ioc->chip->WriteSequence);
writel(MPI2_WRSEQ_6TH_KEY_VALUE, &ioc->chip->WriteSequence);
/* wait 100 msec */
msleep(100);
if (count++ > 20) {
ioc_info(ioc,
"Stop writing magic sequence after 20 retries\n");
_base_dump_reg_set(ioc);
return -EFAULT;
}
*host_diagnostic = ioc->base_readl_ext_retry(&ioc->chip->HostDiagnostic);
drsprintk(ioc,
ioc_info(ioc, "wrote magic sequence: count(%d), host_diagnostic(0x%08x)\n",
count, *host_diagnostic));
} while ((*host_diagnostic & MPI2_DIAG_DIAG_WRITE_ENABLE) == 0);
return 0;
}
/**
* mpt3sas_base_lock_host_diagnostic: Disable Host Diagnostic Register writes
* @ioc: per adapter object
*/
void
mpt3sas_base_lock_host_diagnostic(struct MPT3SAS_ADAPTER *ioc)
{
drsprintk(ioc, ioc_info(ioc, "disable writes to the diagnostic register\n"));
writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, &ioc->chip->WriteSequence);
}
/**
* _base_diag_reset - the "big hammer" start of day reset
* @ioc: per adapter object
@ -7933,49 +7994,21 @@ _base_diag_reset(struct MPT3SAS_ADAPTER *ioc)
drsprintk(ioc, ioc_info(ioc, "clear interrupts\n"));
count = 0;
do {
/* Write magic sequence to WriteSequence register
* Loop until in diagnostic mode
*/
drsprintk(ioc, ioc_info(ioc, "write magic sequence\n"));
writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, &ioc->chip->WriteSequence);
writel(MPI2_WRSEQ_1ST_KEY_VALUE, &ioc->chip->WriteSequence);
writel(MPI2_WRSEQ_2ND_KEY_VALUE, &ioc->chip->WriteSequence);
writel(MPI2_WRSEQ_3RD_KEY_VALUE, &ioc->chip->WriteSequence);
writel(MPI2_WRSEQ_4TH_KEY_VALUE, &ioc->chip->WriteSequence);
writel(MPI2_WRSEQ_5TH_KEY_VALUE, &ioc->chip->WriteSequence);
writel(MPI2_WRSEQ_6TH_KEY_VALUE, &ioc->chip->WriteSequence);
/* wait 100 msec */
msleep(100);
if (count++ > 20) {
ioc_info(ioc,
"Stop writing magic sequence after 20 retries\n");
_base_dump_reg_set(ioc);
goto out;
}
host_diagnostic = ioc->base_readl_ext_retry(&ioc->chip->HostDiagnostic);
drsprintk(ioc,
ioc_info(ioc, "wrote magic sequence: count(%d), host_diagnostic(0x%08x)\n",
count, host_diagnostic));
} while ((host_diagnostic & MPI2_DIAG_DIAG_WRITE_ENABLE) == 0);
mutex_lock(&ioc->hostdiag_unlock_mutex);
if (mpt3sas_base_unlock_and_get_host_diagnostic(ioc, &host_diagnostic))
goto out;
hcb_size = ioc->base_readl(&ioc->chip->HCBSize);
drsprintk(ioc, ioc_info(ioc, "diag reset: issued\n"));
writel(host_diagnostic | MPI2_DIAG_RESET_ADAPTER,
&ioc->chip->HostDiagnostic);
/*This delay allows the chip PCIe hardware time to finish reset tasks*/
/* This delay allows the chip PCIe hardware time to finish reset tasks */
msleep(MPI2_HARD_RESET_PCIE_FIRST_READ_DELAY_MICRO_SEC/1000);
/* Approximately 300 second max wait */
for (count = 0; count < (300000000 /
MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC); count++) {
MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC); count++) {
host_diagnostic = ioc->base_readl_ext_retry(&ioc->chip->HostDiagnostic);
@ -7988,13 +8021,15 @@ _base_diag_reset(struct MPT3SAS_ADAPTER *ioc)
if (!(host_diagnostic & MPI2_DIAG_RESET_ADAPTER))
break;
msleep(MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC / 1000);
/* Wait to pass the second read delay window */
msleep(MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC/1000);
}
if (host_diagnostic & MPI2_DIAG_HCB_MODE) {
drsprintk(ioc,
ioc_info(ioc, "restart the adapter assuming the HCB Address points to good F/W\n"));
ioc_info(ioc, "restart the adapter assuming the\n"
"HCB Address points to good F/W\n"));
host_diagnostic &= ~MPI2_DIAG_BOOT_DEVICE_SELECT_MASK;
host_diagnostic |= MPI2_DIAG_BOOT_DEVICE_SELECT_HCDW;
writel(host_diagnostic, &ioc->chip->HostDiagnostic);
@ -8008,9 +8043,8 @@ _base_diag_reset(struct MPT3SAS_ADAPTER *ioc)
writel(host_diagnostic & ~MPI2_DIAG_HOLD_IOC_RESET,
&ioc->chip->HostDiagnostic);
drsprintk(ioc,
ioc_info(ioc, "disable writes to the diagnostic register\n"));
writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, &ioc->chip->WriteSequence);
mpt3sas_base_lock_host_diagnostic(ioc);
mutex_unlock(&ioc->hostdiag_unlock_mutex);
drsprintk(ioc, ioc_info(ioc, "Wait for FW to go to the READY state\n"));
ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, 20);
@ -8028,6 +8062,7 @@ _base_diag_reset(struct MPT3SAS_ADAPTER *ioc)
out:
pci_cfg_access_unlock(ioc->pdev);
ioc_err(ioc, "diag reset: FAILED\n");
mutex_unlock(&ioc->hostdiag_unlock_mutex);
return -EFAULT;
}

View File

@ -1366,6 +1366,7 @@ struct MPT3SAS_ADAPTER {
u8 got_task_abort_from_ioctl;
struct mutex reset_in_progress_mutex;
struct mutex hostdiag_unlock_mutex;
spinlock_t ioc_reset_in_progress_lock;
u8 ioc_link_reset_in_progress;
@ -1790,6 +1791,9 @@ void mpt3sas_base_disable_msix(struct MPT3SAS_ADAPTER *ioc);
int mpt3sas_blk_mq_poll(struct Scsi_Host *shost, unsigned int queue_num);
void mpt3sas_base_pause_mq_polling(struct MPT3SAS_ADAPTER *ioc);
void mpt3sas_base_resume_mq_polling(struct MPT3SAS_ADAPTER *ioc);
int mpt3sas_base_unlock_and_get_host_diagnostic(struct MPT3SAS_ADAPTER *ioc,
u32 *host_diagnostic);
void mpt3sas_base_lock_host_diagnostic(struct MPT3SAS_ADAPTER *ioc);
/* scsih shared API */
struct scsi_cmnd *mpt3sas_scsih_scsi_lookup_get(struct MPT3SAS_ADAPTER *ioc,

View File

@ -2543,6 +2543,56 @@ _ctl_addnl_diag_query(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
return 0;
}
/**
* _ctl_enable_diag_sbr_reload - enable sbr reload bit
* @ioc: per adapter object
* @arg: user space buffer containing ioctl content
*
* Enable the SBR reload bit
*/
static int
_ctl_enable_diag_sbr_reload(struct MPT3SAS_ADAPTER *ioc, void __user *arg)
{
u32 ioc_state, host_diagnostic;
if (ioc->shost_recovery ||
ioc->pci_error_recovery || ioc->is_driver_loading ||
ioc->remove_host)
return -EAGAIN;
ioc_state = mpt3sas_base_get_iocstate(ioc, 1);
if (ioc_state != MPI2_IOC_STATE_OPERATIONAL)
return -EFAULT;
host_diagnostic = ioc->base_readl(&ioc->chip->HostDiagnostic);
if (host_diagnostic & MPI2_DIAG_SBR_RELOAD)
return 0;
if (mutex_trylock(&ioc->hostdiag_unlock_mutex)) {
if (mpt3sas_base_unlock_and_get_host_diagnostic(ioc, &host_diagnostic)) {
mutex_unlock(&ioc->hostdiag_unlock_mutex);
return -EFAULT;
}
} else
return -EAGAIN;
host_diagnostic |= MPI2_DIAG_SBR_RELOAD;
writel(host_diagnostic, &ioc->chip->HostDiagnostic);
host_diagnostic = ioc->base_readl(&ioc->chip->HostDiagnostic);
mpt3sas_base_lock_host_diagnostic(ioc);
mutex_unlock(&ioc->hostdiag_unlock_mutex);
if (!(host_diagnostic & MPI2_DIAG_SBR_RELOAD)) {
ioc_err(ioc, "%s: Failed to set Diag SBR Reload Bit\n", __func__);
return -EFAULT;
}
ioc_info(ioc, "%s: Successfully set the Diag SBR Reload Bit\n", __func__);
return 0;
}
#ifdef CONFIG_COMPAT
/**
* _ctl_compat_mpt_command - convert 32bit pointers to 64bit.
@ -2719,6 +2769,10 @@ _ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg,
if (_IOC_SIZE(cmd) == sizeof(struct mpt3_addnl_diag_query))
ret = _ctl_addnl_diag_query(ioc, arg);
break;
case MPT3ENABLEDIAGSBRRELOAD:
if (_IOC_SIZE(cmd) == sizeof(struct mpt3_enable_diag_sbr_reload))
ret = _ctl_enable_diag_sbr_reload(ioc, arg);
break;
default:
dctlprintk(ioc,
ioc_info(ioc, "unsupported ioctl opcode(0x%08x)\n",

View File

@ -98,6 +98,8 @@
struct mpt3_diag_read_buffer)
#define MPT3ADDNLDIAGQUERY _IOWR(MPT3_MAGIC_NUMBER, 32, \
struct mpt3_addnl_diag_query)
#define MPT3ENABLEDIAGSBRRELOAD _IOWR(MPT3_MAGIC_NUMBER, 33, \
struct mpt3_enable_diag_sbr_reload)
/* Trace Buffer default UniqueId */
#define MPT2DIAGBUFFUNIQUEID (0x07075900)
@ -448,4 +450,12 @@ struct mpt3_addnl_diag_query {
uint32_t reserved2[2];
};
/**
* struct mpt3_enable_diag_sbr_reload - enable sbr reload
* @hdr - generic header
*/
struct mpt3_enable_diag_sbr_reload {
struct mpt3_ioctl_header hdr;
};
#endif /* MPT3SAS_CTL_H_INCLUDED */

View File

@ -12240,6 +12240,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
/* misc semaphores and spin locks */
mutex_init(&ioc->reset_in_progress_mutex);
mutex_init(&ioc->hostdiag_unlock_mutex);
/* initializing pci_access_mutex lock */
mutex_init(&ioc->pci_access_mutex);
spin_lock_init(&ioc->ioc_reset_in_progress_lock);