Skip to content

Commit ee81ee8

Browse files
Jon DerrickLorenzo Pieralisi
authored andcommitted
PCI: vmd: Disable MSI-X remapping when possible
VMD will retransmit child device MSI-X using its own MSI-X table and requester-id. This limits the number of MSI-X available to the whole child device domain to the number of VMD MSI-X interrupts. Some VMD devices have a mode where this remapping can be disabled, allowing child device interrupts to bypass processing with the VMD MSI-X domain interrupt handler and going straight the child device interrupt handler, allowing for better performance and scaling. The requester-id still gets changed to the VMD endpoint's requester-id, and the interrupt remapping handlers have been updated to properly set IRTE for child device interrupts to the VMD endpoint's context. Some VMD platforms have existing production BIOS which rely on MSI-X remapping and won't explicitly program the MSI-X remapping bit. This re-enables MSI-X remapping on unload. Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jon Derrick <[email protected]> Signed-off-by: Lorenzo Pieralisi <[email protected]> Reviewed-by: Krzysztof Wilczyński <[email protected]> Acked-by: Joerg Roedel <[email protected]>
1 parent 9b4a824 commit ee81ee8

File tree

1 file changed

+51
-12
lines changed
  • drivers/pci/controller

1 file changed

+51
-12
lines changed

drivers/pci/controller/vmd.c

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#define BUS_RESTRICT_CAP(vmcap) (vmcap & 0x1)
2929
#define PCI_REG_VMCONFIG 0x44
3030
#define BUS_RESTRICT_CFG(vmcfg) ((vmcfg >> 8) & 0x3)
31+
#define VMCONFIG_MSI_REMAP 0x2
3132
#define PCI_REG_VMLOCK 0x70
3233
#define MB2_SHADOW_EN(vmlock) (vmlock & 0x2)
3334

@@ -59,6 +60,13 @@ enum vmd_features {
5960
* be used for MSI remapping
6061
*/
6162
VMD_FEAT_OFFSET_FIRST_VECTOR = (1 << 3),
63+
64+
/*
65+
* Device can bypass remapping MSI-X transactions into its MSI-X table,
66+
* avoiding the requirement of a VMD MSI domain for child device
67+
* interrupt handling.
68+
*/
69+
VMD_FEAT_CAN_BYPASS_MSI_REMAP = (1 << 4),
6270
};
6371

6472
/*
@@ -306,6 +314,16 @@ static struct msi_domain_info vmd_msi_domain_info = {
306314
.chip = &vmd_msi_controller,
307315
};
308316

317+
static void vmd_set_msi_remapping(struct vmd_dev *vmd, bool enable)
318+
{
319+
u16 reg;
320+
321+
pci_read_config_word(vmd->dev, PCI_REG_VMCONFIG, &reg);
322+
reg = enable ? (reg & ~VMCONFIG_MSI_REMAP) :
323+
(reg | VMCONFIG_MSI_REMAP);
324+
pci_write_config_word(vmd->dev, PCI_REG_VMCONFIG, reg);
325+
}
326+
309327
static int vmd_create_irq_domain(struct vmd_dev *vmd)
310328
{
311329
struct fwnode_handle *fn;
@@ -325,6 +343,13 @@ static int vmd_create_irq_domain(struct vmd_dev *vmd)
325343

326344
static void vmd_remove_irq_domain(struct vmd_dev *vmd)
327345
{
346+
/*
347+
* Some production BIOS won't enable remapping between soft reboots.
348+
* Ensure remapping is restored before unloading the driver.
349+
*/
350+
if (!vmd->msix_count)
351+
vmd_set_msi_remapping(vmd, true);
352+
328353
if (vmd->irq_domain) {
329354
struct fwnode_handle *fn = vmd->irq_domain->fwnode;
330355

@@ -679,15 +704,32 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
679704

680705
sd->node = pcibus_to_node(vmd->dev->bus);
681706

682-
ret = vmd_create_irq_domain(vmd);
683-
if (ret)
684-
return ret;
685-
686707
/*
687-
* Override the irq domain bus token so the domain can be distinguished
688-
* from a regular PCI/MSI domain.
708+
* Currently MSI remapping must be enabled in guest passthrough mode
709+
* due to some missing interrupt remapping plumbing. This is probably
710+
* acceptable because the guest is usually CPU-limited and MSI
711+
* remapping doesn't become a performance bottleneck.
689712
*/
690-
irq_domain_update_bus_token(vmd->irq_domain, DOMAIN_BUS_VMD_MSI);
713+
if (!(features & VMD_FEAT_CAN_BYPASS_MSI_REMAP) ||
714+
offset[0] || offset[1]) {
715+
ret = vmd_alloc_irqs(vmd);
716+
if (ret)
717+
return ret;
718+
719+
vmd_set_msi_remapping(vmd, true);
720+
721+
ret = vmd_create_irq_domain(vmd);
722+
if (ret)
723+
return ret;
724+
725+
/*
726+
* Override the IRQ domain bus token so the domain can be
727+
* distinguished from a regular PCI/MSI domain.
728+
*/
729+
irq_domain_update_bus_token(vmd->irq_domain, DOMAIN_BUS_VMD_MSI);
730+
} else {
731+
vmd_set_msi_remapping(vmd, false);
732+
}
691733

692734
pci_add_resource(&resources, &vmd->resources[0]);
693735
pci_add_resource_offset(&resources, &vmd->resources[1], offset[0]);
@@ -753,10 +795,6 @@ static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id)
753795
if (features & VMD_FEAT_OFFSET_FIRST_VECTOR)
754796
vmd->first_vec = 1;
755797

756-
err = vmd_alloc_irqs(vmd);
757-
if (err)
758-
return err;
759-
760798
spin_lock_init(&vmd->cfg_lock);
761799
pci_set_drvdata(dev, vmd);
762800
err = vmd_enable_domain(vmd, features);
@@ -825,7 +863,8 @@ static const struct pci_device_id vmd_ids[] = {
825863
.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP,},
826864
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_28C0),
827865
.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW |
828-
VMD_FEAT_HAS_BUS_RESTRICTIONS,},
866+
VMD_FEAT_HAS_BUS_RESTRICTIONS |
867+
VMD_FEAT_CAN_BYPASS_MSI_REMAP,},
829868
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x467f),
830869
.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP |
831870
VMD_FEAT_HAS_BUS_RESTRICTIONS |

0 commit comments

Comments
 (0)