History

PCI/MSI: Protect msi_desc::masked for multi-MSI

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).

Thomas Gleixner committed 5 years ago

Commit 77e89af1 parent d28d4ad4 files · +13 −10Browse files

Unified · Split

4 files
  1. drivers/base/core.c +1 −0
  2. drivers/pci/msi.c +10 −9
  3. include/linux/device.h +1 −0
  4. include/linux/msi.h +1 −1
drivers/base/core.c+1−0
@@ -2837,6 +2837,7 @@ void device_initialize(struct device *dev) · 2836 unmodified lines
2837 device_pm_init(dev);2837 device_pm_init(dev);
2838 set_dev_node(dev, -1);2838 set_dev_node(dev, -1);
2839#ifdef CONFIG_GENERIC_MSI_IRQ2839#ifdef CONFIG_GENERIC_MSI_IRQ
2840 raw_spin_lock_init(&dev->msi_lock);
2840 INIT_LIST_HEAD(&dev->msi_list);2841 INIT_LIST_HEAD(&dev->msi_list);
2841#endif2842#endif
2842 INIT_LIST_HEAD(&dev->links.consumers);2843 INIT_LIST_HEAD(&dev->links.consumers);
drivers/pci/msi.c+10−9
@@ -143,24 +143,25 @@ static inline __attribute_const__ u32 msi_mask(unsigned x) · 142 unmodified lines
143 * reliably as devices without an INTx disable bit will then generate a143 * reliably as devices without an INTx disable bit will then generate a
144 * level IRQ which will never be cleared.144 * level IRQ which will never be cleared.
145 */145 */
146u32 __pci_msi_desc_mask_irq(struct msi_desc *desc, u32 mask, u32 flag)146void __pci_msi_desc_mask_irq(struct msi_desc *desc, u32 mask, u32 flag)
147{147{
148 u32 mask_bits = desc->masked;148 raw_spinlock_t *lock = &desc->dev->msi_lock;
149 unsigned long flags;
149 150
150 if (pci_msi_ignore_mask || !desc->msi_attrib.maskbit)151 if (pci_msi_ignore_mask || !desc->msi_attrib.maskbit)
151 return 0;152 return;
152 153
153 mask_bits &= ~mask;154 raw_spin_lock_irqsave(lock, flags);
154 mask_bits |= flag;155 desc->masked &= ~mask;
156 desc->masked |= flag;
155 pci_write_config_dword(msi_desc_to_pci_dev(desc), desc->mask_pos,157 pci_write_config_dword(msi_desc_to_pci_dev(desc), desc->mask_pos,
156 mask_bits);158 desc->masked);
157 159 raw_spin_unlock_irqrestore(lock, flags);
158 return mask_bits;
159}160}
160 161
161static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag)162static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag)
162{163{
163 desc->masked = __pci_msi_desc_mask_irq(desc, mask, flag);164 __pci_msi_desc_mask_irq(desc, mask, flag);
164}165}
165 166
166static void __iomem *pci_msix_desc_addr(struct msi_desc *desc)167static void __iomem *pci_msix_desc_addr(struct msi_desc *desc)
include/linux/device.h+1−0
@@ -506,6 +506,7 @@ struct device { · 505 unmodified lines
506 struct dev_pin_info *pins;506 struct dev_pin_info *pins;
507#endif507#endif
508#ifdef CONFIG_GENERIC_MSI_IRQ508#ifdef CONFIG_GENERIC_MSI_IRQ
509 raw_spinlock_t msi_lock;
509 struct list_head msi_list;510 struct list_head msi_list;
510#endif511#endif
511#ifdef CONFIG_DMA_OPS512#ifdef CONFIG_DMA_OPS
include/linux/msi.h+1−1
@@ -233,7 +233,7 @@ void __pci_read_msi_msg(struct msi_desc *entry, struct msi_msg *msg); · 232 unmodified lines
233void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg);233void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg);
234 234
235u32 __pci_msix_desc_mask_irq(struct msi_desc *desc, u32 flag);235u32 __pci_msix_desc_mask_irq(struct msi_desc *desc, u32 flag);
236u32 __pci_msi_desc_mask_irq(struct msi_desc *desc, u32 mask, u32 flag);236void __pci_msi_desc_mask_irq(struct msi_desc *desc, u32 mask, u32 flag);
237void pci_msi_mask_irq(struct irq_data *data);237void pci_msi_mask_irq(struct irq_data *data);
238void pci_msi_unmask_irq(struct irq_data *data);238void pci_msi_unmask_irq(struct irq_data *data);
239 239

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t