]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
scsi: mpi3mr: Serialize admin queue BAR writes on 32-bit systems
authorRanjan Kumar <ranjan.kumar@broadcom.com>
Fri, 22 Aug 2025 14:59:42 +0000 (10:59 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 28 Aug 2025 14:28:44 +0000 (16:28 +0200)
[ Upstream commit c91e140c82eb58724c435f623702e51cc7896646 ]

On 32-bit systems, 64-bit BAR writes to admin queue registers are
performed as two 32-bit writes. Without locking, this can cause partial
writes when accessed concurrently.

Updated per-queue spinlocks is used to serialize these writes and prevent
race conditions.

Fixes: 824a156633df ("scsi: mpi3mr: Base driver code")
Cc: stable@vger.kernel.org
Signed-off-by: Ranjan Kumar <ranjan.kumar@broadcom.com>
Link: https://lore.kernel.org/r/20250627194539.48851-4-ranjan.kumar@broadcom.com
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/scsi/mpi3mr/mpi3mr.h
drivers/scsi/mpi3mr/mpi3mr_fw.c
drivers/scsi/mpi3mr/mpi3mr_os.c

index 9a896ff66e9f7a3660604c53b3409e5544e51733..90da783457fbfff5725eafcefae6b3a4c47f3840 100644 (file)
@@ -1025,6 +1025,8 @@ struct scmd_priv {
  * @logdata_buf: Circular buffer to store log data entries
  * @logdata_buf_idx: Index of entry in buffer to store
  * @logdata_entry_sz: log data entry size
+ * @adm_req_q_bar_writeq_lock: Admin request queue lock
+ * @adm_reply_q_bar_writeq_lock: Admin reply queue lock
  * @pend_large_data_sz: Counter to track pending large data
  * @io_throttle_data_length: I/O size to track in 512b blocks
  * @io_throttle_high: I/O size to start throttle in 512b blocks
@@ -1207,6 +1209,8 @@ struct mpi3mr_ioc {
        u8 *logdata_buf;
        u16 logdata_buf_idx;
        u16 logdata_entry_sz;
+       spinlock_t adm_req_q_bar_writeq_lock;
+       spinlock_t adm_reply_q_bar_writeq_lock;
 
        atomic_t pend_large_data_sz;
        u32 io_throttle_data_length;
index 4dae1f65aa6513b6868e6359ae611532e89e821a..b03e4b8cb67d662b3a0c64237124ef12ea53312d 100644 (file)
@@ -23,17 +23,22 @@ module_param(poll_queues, int, 0444);
 MODULE_PARM_DESC(poll_queues, "Number of queues for io_uring poll mode. (Range 1 - 126)");
 
 #if defined(writeq) && defined(CONFIG_64BIT)
-static inline void mpi3mr_writeq(__u64 b, void __iomem *addr)
+static inline void mpi3mr_writeq(__u64 b, void __iomem *addr,
+       spinlock_t *write_queue_lock)
 {
        writeq(b, addr);
 }
 #else
-static inline void mpi3mr_writeq(__u64 b, void __iomem *addr)
+static inline void mpi3mr_writeq(__u64 b, void __iomem *addr,
+       spinlock_t *write_queue_lock)
 {
        __u64 data_out = b;
+       unsigned long flags;
 
+       spin_lock_irqsave(write_queue_lock, flags);
        writel((u32)(data_out), addr);
        writel((u32)(data_out >> 32), (addr + 4));
+       spin_unlock_irqrestore(write_queue_lock, flags);
 }
 #endif
 
@@ -2666,9 +2671,11 @@ static int mpi3mr_setup_admin_qpair(struct mpi3mr_ioc *mrioc)
            (mrioc->num_admin_req);
        writel(num_admin_entries, &mrioc->sysif_regs->admin_queue_num_entries);
        mpi3mr_writeq(mrioc->admin_req_dma,
-           &mrioc->sysif_regs->admin_request_queue_address);
+               &mrioc->sysif_regs->admin_request_queue_address,
+               &mrioc->adm_req_q_bar_writeq_lock);
        mpi3mr_writeq(mrioc->admin_reply_dma,
-           &mrioc->sysif_regs->admin_reply_queue_address);
+               &mrioc->sysif_regs->admin_reply_queue_address,
+               &mrioc->adm_reply_q_bar_writeq_lock);
        writel(mrioc->admin_req_pi, &mrioc->sysif_regs->admin_request_queue_pi);
        writel(mrioc->admin_reply_ci, &mrioc->sysif_regs->admin_reply_queue_ci);
        return retval;
index 7417c8b74d9a659483f38832a93dd620766b4e1b..1bf3572c7aac44a4c131de039e7cf23c4c6aceb7 100644 (file)
@@ -5102,6 +5102,8 @@ mpi3mr_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        spin_lock_init(&mrioc->tgtdev_lock);
        spin_lock_init(&mrioc->watchdog_lock);
        spin_lock_init(&mrioc->chain_buf_lock);
+       spin_lock_init(&mrioc->adm_req_q_bar_writeq_lock);
+       spin_lock_init(&mrioc->adm_reply_q_bar_writeq_lock);
        spin_lock_init(&mrioc->sas_node_lock);
 
        INIT_LIST_HEAD(&mrioc->fwevt_list);