Skip to content

Commit 312730c

Browse files
KAGA-KOKOgregkh
authored andcommitted
PCI/MSI: Protect msi_desc::masked for multi-MSI
commit 77e89af upstream. 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: f2440d9 ("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 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 724d0a9 commit 312730c

4 files changed

Lines changed: 13 additions & 10 deletions

File tree

drivers/base/core.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2452,6 +2452,7 @@ void device_initialize(struct device *dev)
24522452
device_pm_init(dev);
24532453
set_dev_node(dev, -1);
24542454
#ifdef CONFIG_GENERIC_MSI_IRQ
2455+
raw_spin_lock_init(&dev->msi_lock);
24552456
INIT_LIST_HEAD(&dev->msi_list);
24562457
#endif
24572458
INIT_LIST_HEAD(&dev->links.consumers);

drivers/pci/msi.c

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -171,24 +171,25 @@ static inline __attribute_const__ u32 msi_mask(unsigned x)
171171
* reliably as devices without an INTx disable bit will then generate a
172172
* level IRQ which will never be cleared.
173173
*/
174-
u32 __pci_msi_desc_mask_irq(struct msi_desc *desc, u32 mask, u32 flag)
174+
void __pci_msi_desc_mask_irq(struct msi_desc *desc, u32 mask, u32 flag)
175175
{
176-
u32 mask_bits = desc->masked;
176+
raw_spinlock_t *lock = &desc->dev->msi_lock;
177+
unsigned long flags;
177178

178179
if (pci_msi_ignore_mask || !desc->msi_attrib.maskbit)
179-
return 0;
180+
return;
180181

181-
mask_bits &= ~mask;
182-
mask_bits |= flag;
182+
raw_spin_lock_irqsave(lock, flags);
183+
desc->masked &= ~mask;
184+
desc->masked |= flag;
183185
pci_write_config_dword(msi_desc_to_pci_dev(desc), desc->mask_pos,
184-
mask_bits);
185-
186-
return mask_bits;
186+
desc->masked);
187+
raw_spin_unlock_irqrestore(lock, flags);
187188
}
188189

189190
static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag)
190191
{
191-
desc->masked = __pci_msi_desc_mask_irq(desc, mask, flag);
192+
__pci_msi_desc_mask_irq(desc, mask, flag);
192193
}
193194

194195
static void __iomem *pci_msix_desc_addr(struct msi_desc *desc)

include/linux/device.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,7 @@ struct device {
497497
struct dev_pin_info *pins;
498498
#endif
499499
#ifdef CONFIG_GENERIC_MSI_IRQ
500+
raw_spinlock_t msi_lock;
500501
struct list_head msi_list;
501502
#endif
502503
#ifdef CONFIG_DMA_OPS

include/linux/msi.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ void __pci_read_msi_msg(struct msi_desc *entry, struct msi_msg *msg);
194194
void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg);
195195

196196
u32 __pci_msix_desc_mask_irq(struct msi_desc *desc, u32 flag);
197-
u32 __pci_msi_desc_mask_irq(struct msi_desc *desc, u32 mask, u32 flag);
197+
void __pci_msi_desc_mask_irq(struct msi_desc *desc, u32 mask, u32 flag);
198198
void pci_msi_mask_irq(struct irq_data *data);
199199
void pci_msi_unmask_irq(struct irq_data *data);
200200

0 commit comments

Comments
 (0)