scsi: mpt3sas: Reload SBR without rebooting HBA
authorRanjan Kumar <ranjan.kumar@broadcom.com>
Thu, 28 Dec 2023 11:48:09 +0000 (17:18 +0530)
committerMartin K. Petersen <martin.petersen@oracle.com>
Thu, 25 Jan 2024 02:40:26 +0000 (21:40 -0500)
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>
drivers/scsi/mpt3sas/mpt3sas_base.c
drivers/scsi/mpt3sas/mpt3sas_base.h
drivers/scsi/mpt3sas/mpt3sas_ctl.c
drivers/scsi/mpt3sas/mpt3sas_ctl.h
drivers/scsi/mpt3sas/mpt3sas_scsih.c

index 8761bc58d965f0f6eb6776a4272ca856e8724463..fc8c45e15235571bacff394a1e05dcb80cd53125 100644 (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
@@ -7914,26 +7914,22 @@ mpt3sas_base_validate_event_type(struct MPT3SAS_ADAPTER *ioc, u32 *event_type)
 }
 
 /**
- * _base_diag_reset - the "big hammer" start of day reset
- * @ioc: per adapter object
- *
- * Return: 0 for success, non-zero for failure.
- */
-static int
-_base_diag_reset(struct MPT3SAS_ADAPTER *ioc)
-{
-       u32 host_diagnostic;
-       u32 ioc_state;
-       u32 count;
-       u32 hcb_size;
-
-       ioc_info(ioc, "sending diag reset !!\n");
-
-       pci_cfg_access_lock(ioc->pdev);
+* 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.
+*/
 
-       drsprintk(ioc, ioc_info(ioc, "clear interrupts\n"));
+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
@@ -7952,30 +7948,67 @@ _base_diag_reset(struct MPT3SAS_ADAPTER *ioc)
 
                if (count++ > 20) {
                        ioc_info(ioc,
-                           "Stop writing magic sequence after 20 retries\n");
+                                   "Stop writing magic sequence after 20 retries\n");
                        _base_dump_reg_set(ioc);
-                       goto out;
+                       return -EFAULT;
                }
 
-               host_diagnostic = ioc->base_readl_ext_retry(&ioc->chip->HostDiagnostic);
+               *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));
+                            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);
+       } while ((*host_diagnostic & MPI2_DIAG_DIAG_WRITE_ENABLE) == 0);
+       return 0;
+}
 
-       hcb_size = ioc->base_readl(&ioc->chip->HCBSize);
+/**
+ * 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
+ *
+ * Return: 0 for success, non-zero for failure.
+ */
+static int
+_base_diag_reset(struct MPT3SAS_ADAPTER *ioc)
+{
+       u32 host_diagnostic;
+       u32 ioc_state;
+       u32 count;
+       u32 hcb_size;
+
+       ioc_info(ioc, "sending diag reset !!\n");
+
+       pci_cfg_access_lock(ioc->pdev);
+
+       drsprintk(ioc, ioc_info(ioc, "clear interrupts\n"));
+
+       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;
 }
 
index 6d0bc8c6670028d621271190ac6e38a7a79a4fe6..de60ef8a79089899e3a6a967a5e59d8f625415a9 100644 (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,
index 147cb7088d55f4aaa4eb823b894a8a67dd22bf84..1c9fd26195b81230cda66851bc44f26c86d52415 100644 (file)
@@ -2543,6 +2543,56 @@ out:
        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",
index 8f6ffb40261c9a6f5f0eafc5a880177212338f9f..171709e910066a0ce7cb4e7332aad8096a05607c 100644 (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 */
index 51b5788da040ac79125eaf027048b85dca8e7e2a..ef8ee93005eae6db5123d369b9e583fed391b20d 100644 (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);