x86/apic/vector: Provide MSI parent domain
Enable MSI parent domain support in the x86 vector domain and fixup the checks in the iommu implementations to check whether device::msi::domain is the default MSI parent domain. That keeps the existing logic to protect e.g. devices behind VMD working. The interrupt remap PCI/MSI code still works because the underlying vector domain still provides the same functionality. None of the other x86 PCI/MSI, e.g. XEN and HyperV, implementations are affected either. They still work the same way both at the low level and the PCI/MSI implementations they provide. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Kevin Tian <kevin.tian@intel.com> Acked-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20221124232326.034672592@linutronix.de
This commit is contained in:
parent
15c72f824b
commit
b6d5fc3a52
|
@ -62,4 +62,10 @@ typedef struct x86_msi_addr_hi {
|
|||
struct msi_msg;
|
||||
u32 x86_msi_msg_get_destid(struct msi_msg *msg, bool extid);
|
||||
|
||||
#define X86_VECTOR_MSI_FLAGS_SUPPORTED \
|
||||
(MSI_GENERIC_FLAGS_MASK | MSI_FLAG_PCI_MSIX)
|
||||
|
||||
#define X86_VECTOR_MSI_FLAGS_REQUIRED \
|
||||
(MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS)
|
||||
|
||||
#endif /* _ASM_X86_MSI_H */
|
||||
|
|
|
@ -92,6 +92,7 @@ void pcibios_scan_root(int bus);
|
|||
struct irq_routing_table *pcibios_get_irq_routing_table(void);
|
||||
int pcibios_set_irq_routing(struct pci_dev *dev, int pin, int irq);
|
||||
|
||||
bool pci_dev_has_default_msi_parent_domain(struct pci_dev *dev);
|
||||
|
||||
#define HAVE_PCI_MMAP
|
||||
#define arch_can_pci_mmap_wc() pat_enabled()
|
||||
|
|
|
@ -142,67 +142,131 @@ msi_set_affinity(struct irq_data *irqd, const struct cpumask *mask, bool force)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* IRQ Chip for MSI PCI/PCI-X/PCI-Express Devices,
|
||||
* which implement the MSI or MSI-X Capability Structure.
|
||||
/**
|
||||
* pci_dev_has_default_msi_parent_domain - Check whether the device has the default
|
||||
* MSI parent domain associated
|
||||
* @dev: Pointer to the PCI device
|
||||
*/
|
||||
static struct irq_chip pci_msi_controller = {
|
||||
.name = "PCI-MSI",
|
||||
.irq_unmask = pci_msi_unmask_irq,
|
||||
.irq_mask = pci_msi_mask_irq,
|
||||
.irq_ack = irq_chip_ack_parent,
|
||||
.irq_retrigger = irq_chip_retrigger_hierarchy,
|
||||
.irq_set_affinity = msi_set_affinity,
|
||||
.flags = IRQCHIP_SKIP_SET_WAKE |
|
||||
IRQCHIP_AFFINITY_PRE_STARTUP,
|
||||
};
|
||||
|
||||
int pci_msi_prepare(struct irq_domain *domain, struct device *dev, int nvec,
|
||||
msi_alloc_info_t *arg)
|
||||
bool pci_dev_has_default_msi_parent_domain(struct pci_dev *dev)
|
||||
{
|
||||
init_irq_alloc_info(arg, NULL);
|
||||
if (to_pci_dev(dev)->msix_enabled)
|
||||
arg->type = X86_IRQ_ALLOC_TYPE_PCI_MSIX;
|
||||
else
|
||||
arg->type = X86_IRQ_ALLOC_TYPE_PCI_MSI;
|
||||
struct irq_domain *domain = dev_get_msi_domain(&dev->dev);
|
||||
|
||||
return 0;
|
||||
if (!domain)
|
||||
domain = dev_get_msi_domain(&dev->bus->dev);
|
||||
if (!domain)
|
||||
return false;
|
||||
|
||||
return domain == x86_vector_domain;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_msi_prepare);
|
||||
|
||||
static struct msi_domain_ops pci_msi_domain_ops = {
|
||||
.msi_prepare = pci_msi_prepare,
|
||||
};
|
||||
/**
|
||||
* x86_msi_prepare - Setup of msi_alloc_info_t for allocations
|
||||
* @domain: The domain for which this setup happens
|
||||
* @dev: The device for which interrupts are allocated
|
||||
* @nvec: The number of vectors to allocate
|
||||
* @alloc: The allocation info structure to initialize
|
||||
*
|
||||
* This function is to be used for all types of MSI domains above the x86
|
||||
* vector domain and any intermediates. It is always invoked from the
|
||||
* top level interrupt domain. The domain specific allocation
|
||||
* functionality is determined via the @domain's bus token which allows to
|
||||
* map the X86 specific allocation type.
|
||||
*/
|
||||
static int x86_msi_prepare(struct irq_domain *domain, struct device *dev,
|
||||
int nvec, msi_alloc_info_t *alloc)
|
||||
{
|
||||
struct msi_domain_info *info = domain->host_data;
|
||||
|
||||
static struct msi_domain_info pci_msi_domain_info = {
|
||||
.flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
|
||||
MSI_FLAG_PCI_MSIX | MSI_FLAG_NOMASK_QUIRK,
|
||||
init_irq_alloc_info(alloc, NULL);
|
||||
|
||||
.ops = &pci_msi_domain_ops,
|
||||
.chip = &pci_msi_controller,
|
||||
.handler = handle_edge_irq,
|
||||
.handler_name = "edge",
|
||||
switch (info->bus_token) {
|
||||
case DOMAIN_BUS_PCI_DEVICE_MSI:
|
||||
alloc->type = X86_IRQ_ALLOC_TYPE_PCI_MSI;
|
||||
return 0;
|
||||
case DOMAIN_BUS_PCI_DEVICE_MSIX:
|
||||
alloc->type = X86_IRQ_ALLOC_TYPE_PCI_MSIX;
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* x86_init_dev_msi_info - Domain info setup for MSI domains
|
||||
* @dev: The device for which the domain should be created
|
||||
* @domain: The (root) domain providing this callback
|
||||
* @real_parent: The real parent domain of the to initialize domain
|
||||
* @info: The domain info for the to initialize domain
|
||||
*
|
||||
* This function is to be used for all types of MSI domains above the x86
|
||||
* vector domain and any intermediates. The domain specific functionality
|
||||
* is determined via the @real_parent.
|
||||
*/
|
||||
static bool x86_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
|
||||
struct irq_domain *real_parent, struct msi_domain_info *info)
|
||||
{
|
||||
const struct msi_parent_ops *pops = real_parent->msi_parent_ops;
|
||||
|
||||
/* MSI parent domain specific settings */
|
||||
switch (real_parent->bus_token) {
|
||||
case DOMAIN_BUS_ANY:
|
||||
/* Only the vector domain can have the ANY token */
|
||||
if (WARN_ON_ONCE(domain != real_parent))
|
||||
return false;
|
||||
info->chip->irq_set_affinity = msi_set_affinity;
|
||||
/* See msi_set_affinity() for the gory details */
|
||||
info->flags |= MSI_FLAG_NOMASK_QUIRK;
|
||||
break;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Is the target supported? */
|
||||
switch(info->bus_token) {
|
||||
case DOMAIN_BUS_PCI_DEVICE_MSI:
|
||||
case DOMAIN_BUS_PCI_DEVICE_MSIX:
|
||||
break;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mask out the domain specific MSI feature flags which are not
|
||||
* supported by the real parent.
|
||||
*/
|
||||
info->flags &= pops->supported_flags;
|
||||
/* Enforce the required flags */
|
||||
info->flags |= X86_VECTOR_MSI_FLAGS_REQUIRED;
|
||||
|
||||
/* This is always invoked from the top level MSI domain! */
|
||||
info->ops->msi_prepare = x86_msi_prepare;
|
||||
|
||||
info->chip->irq_ack = irq_chip_ack_parent;
|
||||
info->chip->irq_retrigger = irq_chip_retrigger_hierarchy;
|
||||
info->chip->flags |= IRQCHIP_SKIP_SET_WAKE |
|
||||
IRQCHIP_AFFINITY_PRE_STARTUP;
|
||||
|
||||
info->handler = handle_edge_irq;
|
||||
info->handler_name = "edge";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct msi_parent_ops x86_vector_msi_parent_ops = {
|
||||
.supported_flags = X86_VECTOR_MSI_FLAGS_SUPPORTED,
|
||||
.init_dev_msi_info = x86_init_dev_msi_info,
|
||||
};
|
||||
|
||||
struct irq_domain * __init native_create_pci_msi_domain(void)
|
||||
{
|
||||
struct fwnode_handle *fn;
|
||||
struct irq_domain *d;
|
||||
|
||||
if (disable_apic)
|
||||
return NULL;
|
||||
|
||||
fn = irq_domain_alloc_named_fwnode("PCI-MSI");
|
||||
if (!fn)
|
||||
return NULL;
|
||||
|
||||
d = pci_msi_create_irq_domain(fn, &pci_msi_domain_info,
|
||||
x86_vector_domain);
|
||||
if (!d) {
|
||||
irq_domain_free_fwnode(fn);
|
||||
pr_warn("Failed to initialize PCI-MSI irqdomain.\n");
|
||||
}
|
||||
return d;
|
||||
x86_vector_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT;
|
||||
x86_vector_domain->msi_parent_ops = &x86_vector_msi_parent_ops;
|
||||
return x86_vector_domain;
|
||||
}
|
||||
|
||||
void __init x86_create_pci_msi_domain(void)
|
||||
|
@ -210,7 +274,25 @@ void __init x86_create_pci_msi_domain(void)
|
|||
x86_pci_msi_default_domain = x86_init.irqs.create_pci_msi_domain();
|
||||
}
|
||||
|
||||
/* Keep around for hyperV and the remap code below */
|
||||
int pci_msi_prepare(struct irq_domain *domain, struct device *dev, int nvec,
|
||||
msi_alloc_info_t *arg)
|
||||
{
|
||||
init_irq_alloc_info(arg, NULL);
|
||||
|
||||
if (to_pci_dev(dev)->msix_enabled)
|
||||
arg->type = X86_IRQ_ALLOC_TYPE_PCI_MSIX;
|
||||
else
|
||||
arg->type = X86_IRQ_ALLOC_TYPE_PCI_MSI;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_msi_prepare);
|
||||
|
||||
#ifdef CONFIG_IRQ_REMAP
|
||||
static struct msi_domain_ops pci_msi_domain_ops = {
|
||||
.msi_prepare = pci_msi_prepare,
|
||||
};
|
||||
|
||||
static struct irq_chip pci_msi_ir_controller = {
|
||||
.name = "IR-PCI-MSI",
|
||||
.irq_unmask = pci_msi_unmask_irq,
|
||||
|
|
|
@ -812,7 +812,7 @@ static void
|
|||
amd_iommu_set_pci_msi_domain(struct device *dev, struct amd_iommu *iommu)
|
||||
{
|
||||
if (!irq_remapping_enabled || !dev_is_pci(dev) ||
|
||||
pci_dev_has_special_msi_domain(to_pci_dev(dev)))
|
||||
!pci_dev_has_default_msi_parent_domain(to_pci_dev(dev)))
|
||||
return;
|
||||
|
||||
dev_set_msi_domain(dev, iommu->msi_domain);
|
||||
|
|
|
@ -1107,7 +1107,7 @@ error:
|
|||
*/
|
||||
void intel_irq_remap_add_device(struct dmar_pci_notify_info *info)
|
||||
{
|
||||
if (!irq_remapping_enabled || pci_dev_has_special_msi_domain(info->dev))
|
||||
if (!irq_remapping_enabled || !pci_dev_has_default_msi_parent_domain(info->dev))
|
||||
return;
|
||||
|
||||
dev_set_msi_domain(&info->dev->dev, map_dev_to_ir(info->dev));
|
||||
|
|
Loading…
Reference in New Issue