Skip to content

Commit d5124a9

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 6552e90 commit d5124a9

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
@@ -913,6 +913,53 @@ void pci_free_msi_irqs(struct pci_dev *dev)
913913
}
914914
}
915915

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

918965
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
@@ -1064,6 +1064,15 @@ int pcim_request_region_exclusive(struct pci_dev *pdev, int bar,
10641064
const char *name);
10651065
void pcim_release_region(struct pci_dev *pdev, int bar);
10661066

1067+
#ifdef CONFIG_PCI_MSI
1068+
int pci_msix_write_tph_tag(struct pci_dev *pdev, unsigned int index, u16 tag);
1069+
#else
1070+
static inline int pci_msix_write_tph_tag(struct pci_dev *pdev, unsigned int index, u16 tag)
1071+
{
1072+
return -ENODEV;
1073+
}
1074+
#endif
1075+
10671076
/*
10681077
* Config Address for PCI Configuration Mechanism #1
10691078
*

0 commit comments

Comments
 (0)