]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
PCI/MSI: Protect msi_desc::masked for multi-MSI
authorThomas Gleixner <tglx@linutronix.de>
Thu, 29 Jul 2021 21:51:47 +0000 (23:51 +0200)
committerThomas Gleixner <tglx@linutronix.de>
Tue, 10 Aug 2021 08:59:20 +0000 (10:59 +0200)
Multi-MSI uses a single MSI descriptor and there is a single mask register
when the device supports per vector masking. To avoid reading back the mask
register the value is cached in the MSI descriptor and updates are done by
clearing and setting bits in the cache and writing it to the device.

But nothing protects msi_desc::masked and the mask register from being
modified concurrently on two different CPUs for two different Linux
interrupts which belong to the same multi-MSI descriptor.

Add a lock to struct device and protect any operation on the mask and the
mask register with it.

This makes the update of msi_desc::masked unconditional, but there is no
place which requires a modification of the hardware register without
updating the masked cache.

msi_mask_irq() is now an empty wrapper which will be cleaned up in follow
up changes.

The problem goes way back to the initial support of multi-MSI, but picking
the commit which introduced the mask cache is a valid cut off point
(2.6.30).

Fixes: f2440d9acbe8 ("PCI MSI: Refactor interrupt masking code")
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Marc Zyngier <maz@kernel.org>
Reviewed-by: Marc Zyngier <maz@kernel.org>
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/r/20210729222542.726833414@linutronix.de
drivers/base/core.c
drivers/pci/msi.c
include/linux/device.h
include/linux/msi.h

index f6360490a4a30de4c6454ac7cd1253ee551f9e58..6c0ef9d55a343a463b4d8596e550c036db56593e 100644 (file)
@@ -2837,6 +2837,7 @@ void device_initialize(struct device *dev)
        device_pm_init(dev);
        set_dev_node(dev, -1);
 #ifdef CONFIG_GENERIC_MSI_IRQ
+       raw_spin_lock_init(&dev->msi_lock);
        INIT_LIST_HEAD(&dev->msi_list);
 #endif
        INIT_LIST_HEAD(&dev->links.consumers);
index f0f7026b7ac00f362033463aeb9e7676d2a1ed71..e5e75331b415598d8c6cc192eed777a3734b26a8 100644 (file)
@@ -143,24 +143,25 @@ static inline __attribute_const__ u32 msi_mask(unsigned x)
  * reliably as devices without an INTx disable bit will then generate a
  * level IRQ which will never be cleared.
  */
-u32 __pci_msi_desc_mask_irq(struct msi_desc *desc, u32 mask, u32 flag)
+void __pci_msi_desc_mask_irq(struct msi_desc *desc, u32 mask, u32 flag)
 {
-       u32 mask_bits = desc->masked;
+       raw_spinlock_t *lock = &desc->dev->msi_lock;
+       unsigned long flags;
 
        if (pci_msi_ignore_mask || !desc->msi_attrib.maskbit)
-               return 0;
+               return;
 
-       mask_bits &= ~mask;
-       mask_bits |= flag;
+       raw_spin_lock_irqsave(lock, flags);
+       desc->masked &= ~mask;
+       desc->masked |= flag;
        pci_write_config_dword(msi_desc_to_pci_dev(desc), desc->mask_pos,
-                              mask_bits);
-
-       return mask_bits;
+                              desc->masked);
+       raw_spin_unlock_irqrestore(lock, flags);
 }
 
 static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag)
 {
-       desc->masked = __pci_msi_desc_mask_irq(desc, mask, flag);
+       __pci_msi_desc_mask_irq(desc, mask, flag);
 }
 
 static void __iomem *pci_msix_desc_addr(struct msi_desc *desc)
index 59940f1744c1979add88a25a684ec11ffeafe015..e53aa5065f58147e2b856e8b46411c8d3e0abf0a 100644 (file)
@@ -506,6 +506,7 @@ struct device {
        struct dev_pin_info     *pins;
 #endif
 #ifdef CONFIG_GENERIC_MSI_IRQ
+       raw_spinlock_t          msi_lock;
        struct list_head        msi_list;
 #endif
 #ifdef CONFIG_DMA_OPS
index 6aff469e511d131c37697368410a2a260a1258ac..e8bdcb83172b01acbe5118e2f9a011d9e04e4da8 100644 (file)
@@ -233,7 +233,7 @@ void __pci_read_msi_msg(struct msi_desc *entry, struct msi_msg *msg);
 void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg);
 
 u32 __pci_msix_desc_mask_irq(struct msi_desc *desc, u32 flag);
-u32 __pci_msi_desc_mask_irq(struct msi_desc *desc, u32 mask, u32 flag);
+void __pci_msi_desc_mask_irq(struct msi_desc *desc, u32 mask, u32 flag);
 void pci_msi_mask_irq(struct irq_data *data);
 void pci_msi_unmask_irq(struct irq_data *data);