Skip to content

Commit b9db8df

Browse files
committed
PCI/MSI: Provide a sane mechanism for TPH
The PCI/TPH driver fiddles with the MSI-X control word of an active interrupt completely unserialized against concurrent operations issued from the interrupt core. It also brings the PCI/MSI-X internal cached control word out of sync. Provide a function, which has the required serialization and keeps the control word cache in sync. Unfortunately this requires to look up and lock the interrupt descriptor, which should be only done in the interrupt core code. But confining this particular oddity in the PCI/MSI core is the lesser of all evil. A interrupt core implementation would require a larger pile of infrastructure and indirections for dubious value. Signed-off-by: Thomas Gleixner <[email protected]> Acked-by: Bjorn Helgaas <[email protected]> Link: https://lore.kernel.org/all/[email protected]
1 parent 50410ba commit b9db8df

File tree

2 files changed

+56
-0
lines changed

2 files changed

+56
-0
lines changed

drivers/pci/msi/msi.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -916,6 +916,53 @@ void pci_free_msi_irqs(struct pci_dev *dev)
916916
}
917917
}
918918

919+
#ifdef CONFIG_PCIE_TPH
920+
/**
921+
* pci_msix_write_tph_tag - Update the TPH tag for a given MSI-X vector
922+
* @pdev: The PCIe device to update
923+
* @index: The MSI-X index to update
924+
* @tag: The tag to write
925+
*
926+
* Returns: 0 on success, error code on failure
927+
*/
928+
int pci_msix_write_tph_tag(struct pci_dev *pdev, unsigned int index, u16 tag)
929+
{
930+
struct msi_desc *msi_desc;
931+
struct irq_desc *irq_desc;
932+
unsigned int virq;
933+
934+
if (!pdev->msix_enabled)
935+
return -ENXIO;
936+
937+
guard(msi_descs_lock)(&pdev->dev);
938+
virq = msi_get_virq(&pdev->dev, index);
939+
if (!virq)
940+
return -ENXIO;
941+
/*
942+
* This is a horrible hack, but short of implementing a PCI
943+
* specific interrupt chip callback and a huge pile of
944+
* infrastructure, this is the minor nuissance. It provides the
945+
* protection against concurrent operations on this entry and keeps
946+
* the control word cache in sync.
947+
*/
948+
irq_desc = irq_to_desc(virq);
949+
if (!irq_desc)
950+
return -ENXIO;
951+
952+
guard(raw_spinlock_irq)(&irq_desc->lock);
953+
msi_desc = irq_data_get_msi_desc(&irq_desc->irq_data);
954+
if (!msi_desc || msi_desc->pci.msi_attrib.is_virtual)
955+
return -ENXIO;
956+
957+
msi_desc->pci.msix_ctrl &= ~PCI_MSIX_ENTRY_CTRL_ST;
958+
msi_desc->pci.msix_ctrl |= FIELD_PREP(PCI_MSIX_ENTRY_CTRL_ST, tag);
959+
pci_msix_write_vector_ctrl(msi_desc, msi_desc->pci.msix_ctrl);
960+
/* Flush the write */
961+
readl(pci_msix_desc_addr(msi_desc));
962+
return 0;
963+
}
964+
#endif
965+
919966
/* Misc. infrastructure */
920967

921968
struct pci_dev *msi_desc_to_pci_dev(struct msi_desc *desc)

drivers/pci/pci.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,15 @@ int pcim_request_region_exclusive(struct pci_dev *pdev, int bar,
989989
const char *name);
990990
void pcim_release_region(struct pci_dev *pdev, int bar);
991991

992+
#ifdef CONFIG_PCI_MSI
993+
int pci_msix_write_tph_tag(struct pci_dev *pdev, unsigned int index, u16 tag);
994+
#else
995+
static inline int pci_msix_write_tph_tag(struct pci_dev *pdev, unsigned int index, u16 tag)
996+
{
997+
return -ENODEV;
998+
}
999+
#endif
1000+
9921001
/*
9931002
* Config Address for PCI Configuration Mechanism #1
9941003
*

0 commit comments

Comments
 (0)