Commit bdf53098 authored by Bjorn Helgaas's avatar Bjorn Helgaas

Merge branch 'pci/host-vmd' into next

* pci/host-vmd:
  x86/PCI: VMD: Move VMD driver to drivers/pci/host
  x86/PCI: VMD: Synchronize with RCU freeing MSI IRQ descs
  x86/PCI: VMD: Eliminate index member from IRQ list
  x86/PCI: VMD: Eliminate vmd_vector member from list type
  x86/PCI: VMD: Convert to use pci_alloc_irq_vectors() API
  x86/PCI: VMD: Allocate IRQ lists with correct MSI-X count
  PCI: Use positive flags in pci_alloc_irq_vectors()
  PCI: Update "pci=resource_alignment" documentation

Conflicts:
	drivers/pci/host/Kconfig
	drivers/pci/host/Makefile
parents 69a06e49 181ffd19
...@@ -94,14 +94,11 @@ has a requirements for a minimum number of vectors the driver can pass a ...@@ -94,14 +94,11 @@ has a requirements for a minimum number of vectors the driver can pass a
min_vecs argument set to this limit, and the PCI core will return -ENOSPC min_vecs argument set to this limit, and the PCI core will return -ENOSPC
if it can't meet the minimum number of vectors. if it can't meet the minimum number of vectors.
The flags argument should normally be set to 0, but can be used to pass the The flags argument is used to specify which type of interrupt can be used
PCI_IRQ_NOMSI and PCI_IRQ_NOMSIX flag in case a device claims to support by the device and the driver (PCI_IRQ_LEGACY, PCI_IRQ_MSI, PCI_IRQ_MSIX).
MSI or MSI-X, but the support is broken, or to pass PCI_IRQ_NOLEGACY in A convenient short-hand (PCI_IRQ_ALL_TYPES) is also available to ask for
case the device does not support legacy interrupt lines. any possible kind of interrupt. If the PCI_IRQ_AFFINITY flag is set,
pci_alloc_irq_vectors() will spread the interrupts around the available CPUs.
By default this function will spread the interrupts around the available
CPUs, but this feature can be disabled by passing the PCI_IRQ_NOAFFINITY
flag.
To get the Linux IRQ numbers passed to request_irq() and free_irq() and the To get the Linux IRQ numbers passed to request_irq() and free_irq() and the
vectors, use the following function: vectors, use the following function:
...@@ -131,7 +128,7 @@ larger than the number supported by the device it will automatically be ...@@ -131,7 +128,7 @@ larger than the number supported by the device it will automatically be
capped to the supported limit, so there is no need to query the number of capped to the supported limit, so there is no need to query the number of
vectors supported beforehand: vectors supported beforehand:
nvec = pci_alloc_irq_vectors(pdev, 1, nvec, 0); nvec = pci_alloc_irq_vectors(pdev, 1, nvec, PCI_IRQ_ALL_TYPES)
if (nvec < 0) if (nvec < 0)
goto out_err; goto out_err;
...@@ -140,7 +137,7 @@ interrupts it can request a particular number of interrupts by passing that ...@@ -140,7 +137,7 @@ interrupts it can request a particular number of interrupts by passing that
number to pci_alloc_irq_vectors() function as both 'min_vecs' and number to pci_alloc_irq_vectors() function as both 'min_vecs' and
'max_vecs' parameters: 'max_vecs' parameters:
ret = pci_alloc_irq_vectors(pdev, nvec, nvec, 0); ret = pci_alloc_irq_vectors(pdev, nvec, nvec, PCI_IRQ_ALL_TYPES);
if (ret < 0) if (ret < 0)
goto out_err; goto out_err;
...@@ -148,15 +145,14 @@ The most notorious example of the request type described above is enabling ...@@ -148,15 +145,14 @@ The most notorious example of the request type described above is enabling
the single MSI mode for a device. It could be done by passing two 1s as the single MSI mode for a device. It could be done by passing two 1s as
'min_vecs' and 'max_vecs': 'min_vecs' and 'max_vecs':
ret = pci_alloc_irq_vectors(pdev, 1, 1, 0); ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
if (ret < 0) if (ret < 0)
goto out_err; goto out_err;
Some devices might not support using legacy line interrupts, in which case Some devices might not support using legacy line interrupts, in which case
the PCI_IRQ_NOLEGACY flag can be used to fail the request if the platform the driver can specify that only MSI or MSI-X is acceptable:
can't provide MSI or MSI-X interrupts:
nvec = pci_alloc_irq_vectors(pdev, 1, nvec, PCI_IRQ_NOLEGACY); nvec = pci_alloc_irq_vectors(pdev, 1, nvec, PCI_IRQ_MSI | PCI_IRQ_MSIX);
if (nvec < 0) if (nvec < 0)
goto out_err; goto out_err;
......
...@@ -3032,6 +3032,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted. ...@@ -3032,6 +3032,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
PAGE_SIZE is used as alignment. PAGE_SIZE is used as alignment.
PCI-PCI bridge can be specified, if resource PCI-PCI bridge can be specified, if resource
windows need to be expanded. windows need to be expanded.
To specify the alignment for several
instances of a device, the PCI vendor,
device, subvendor, and subdevice may be
specified, e.g., 4096@pci:8086:9c22:103c:198f
ecrc= Enable/disable PCIe ECRC (transaction layer ecrc= Enable/disable PCIe ECRC (transaction layer
end-to-end CRC checking). end-to-end CRC checking).
bios: Use BIOS/firmware settings. This is the bios: Use BIOS/firmware settings. This is the
......
...@@ -2744,19 +2744,6 @@ config PMC_ATOM ...@@ -2744,19 +2744,6 @@ config PMC_ATOM
def_bool y def_bool y
depends on PCI depends on PCI
config VMD
depends on PCI_MSI
tristate "Volume Management Device Driver"
default N
---help---
Adds support for the Intel Volume Management Device (VMD). VMD is a
secondary PCI host bridge that allows PCI Express root ports,
and devices attached to them, to be removed from the default
PCI domain and placed within the VMD domain. This provides
more bus resources than are otherwise possible with a
single domain. If you know your system provides one of these and
has devices attached to it, say Y; if you are not sure, say N.
source "net/Kconfig" source "net/Kconfig"
source "drivers/Kconfig" source "drivers/Kconfig"
......
...@@ -23,8 +23,6 @@ obj-y += bus_numa.o ...@@ -23,8 +23,6 @@ obj-y += bus_numa.o
obj-$(CONFIG_AMD_NB) += amd_bus.o obj-$(CONFIG_AMD_NB) += amd_bus.o
obj-$(CONFIG_PCI_CNB20LE_QUIRK) += broadcom_bus.o obj-$(CONFIG_PCI_CNB20LE_QUIRK) += broadcom_bus.o
obj-$(CONFIG_VMD) += vmd.o
ifeq ($(CONFIG_PCI_DEBUG),y) ifeq ($(CONFIG_PCI_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG EXTRA_CFLAGS += -DDEBUG
endif endif
...@@ -285,4 +285,20 @@ config PCIE_ROCKCHIP ...@@ -285,4 +285,20 @@ config PCIE_ROCKCHIP
There is 1 internal PCIe port available to support GEN2 with There is 1 internal PCIe port available to support GEN2 with
4 slots. 4 slots.
config VMD
depends on PCI_MSI && X86_64
tristate "Intel Volume Management Device Driver"
default N
---help---
Adds support for the Intel Volume Management Device (VMD). VMD is a
secondary PCI host bridge that allows PCI Express root ports,
and devices attached to them, to be removed from the default
PCI domain and placed within the VMD domain. This provides
more bus resources than are otherwise possible with a
single domain. If you know your system provides one of these and
has devices attached to it, say Y; if you are not sure, say N.
To compile this driver as a module, choose M here: the
module will be called vmd.
endmenu endmenu
...@@ -32,3 +32,4 @@ obj-$(CONFIG_PCI_HOST_THUNDER_PEM) += pci-thunder-pem.o ...@@ -32,3 +32,4 @@ obj-$(CONFIG_PCI_HOST_THUNDER_PEM) += pci-thunder-pem.o
obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o
obj-$(CONFIG_VMD) += vmd.o
...@@ -56,16 +56,11 @@ struct vmd_irq { ...@@ -56,16 +56,11 @@ struct vmd_irq {
/** /**
* struct vmd_irq_list - list of driver requested IRQs mapping to a VMD vector * struct vmd_irq_list - list of driver requested IRQs mapping to a VMD vector
* @irq_list: the list of irq's the VMD one demuxes to. * @irq_list: the list of irq's the VMD one demuxes to.
* @vmd_vector: the h/w IRQ assigned to the VMD.
* @index: index into the VMD MSI-X table; used for message routing.
* @count: number of child IRQs assigned to this vector; used to track * @count: number of child IRQs assigned to this vector; used to track
* sharing. * sharing.
*/ */
struct vmd_irq_list { struct vmd_irq_list {
struct list_head irq_list; struct list_head irq_list;
struct vmd_dev *vmd;
unsigned int vmd_vector;
unsigned int index;
unsigned int count; unsigned int count;
}; };
...@@ -76,7 +71,6 @@ struct vmd_dev { ...@@ -76,7 +71,6 @@ struct vmd_dev {
char __iomem *cfgbar; char __iomem *cfgbar;
int msix_count; int msix_count;
struct msix_entry *msix_entries;
struct vmd_irq_list *irqs; struct vmd_irq_list *irqs;
struct pci_sysdata sysdata; struct pci_sysdata sysdata;
...@@ -95,6 +89,12 @@ static inline struct vmd_dev *vmd_from_bus(struct pci_bus *bus) ...@@ -95,6 +89,12 @@ static inline struct vmd_dev *vmd_from_bus(struct pci_bus *bus)
return container_of(bus->sysdata, struct vmd_dev, sysdata); return container_of(bus->sysdata, struct vmd_dev, sysdata);
} }
static inline unsigned int index_from_irqs(struct vmd_dev *vmd,
struct vmd_irq_list *irqs)
{
return irqs - vmd->irqs;
}
/* /*
* Drivers managing a device in a VMD domain allocate their own IRQs as before, * Drivers managing a device in a VMD domain allocate their own IRQs as before,
* but the MSI entry for the hardware it's driving will be programmed with a * but the MSI entry for the hardware it's driving will be programmed with a
...@@ -107,9 +107,11 @@ static void vmd_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) ...@@ -107,9 +107,11 @@ static void vmd_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
{ {
struct vmd_irq *vmdirq = data->chip_data; struct vmd_irq *vmdirq = data->chip_data;
struct vmd_irq_list *irq = vmdirq->irq; struct vmd_irq_list *irq = vmdirq->irq;
struct vmd_dev *vmd = irq_data_get_irq_handler_data(data);
msg->address_hi = MSI_ADDR_BASE_HI; msg->address_hi = MSI_ADDR_BASE_HI;
msg->address_lo = MSI_ADDR_BASE_LO | MSI_ADDR_DEST_ID(irq->index); msg->address_lo = MSI_ADDR_BASE_LO |
MSI_ADDR_DEST_ID(index_from_irqs(vmd, irq));
msg->data = 0; msg->data = 0;
} }
...@@ -194,6 +196,7 @@ static int vmd_msi_init(struct irq_domain *domain, struct msi_domain_info *info, ...@@ -194,6 +196,7 @@ static int vmd_msi_init(struct irq_domain *domain, struct msi_domain_info *info,
struct msi_desc *desc = arg->desc; struct msi_desc *desc = arg->desc;
struct vmd_dev *vmd = vmd_from_bus(msi_desc_to_pci_dev(desc)->bus); struct vmd_dev *vmd = vmd_from_bus(msi_desc_to_pci_dev(desc)->bus);
struct vmd_irq *vmdirq = kzalloc(sizeof(*vmdirq), GFP_KERNEL); struct vmd_irq *vmdirq = kzalloc(sizeof(*vmdirq), GFP_KERNEL);
unsigned int index, vector;
if (!vmdirq) if (!vmdirq)
return -ENOMEM; return -ENOMEM;
...@@ -201,9 +204,11 @@ static int vmd_msi_init(struct irq_domain *domain, struct msi_domain_info *info, ...@@ -201,9 +204,11 @@ static int vmd_msi_init(struct irq_domain *domain, struct msi_domain_info *info,
INIT_LIST_HEAD(&vmdirq->node); INIT_LIST_HEAD(&vmdirq->node);
vmdirq->irq = vmd_next_irq(vmd, desc); vmdirq->irq = vmd_next_irq(vmd, desc);
vmdirq->virq = virq; vmdirq->virq = virq;
index = index_from_irqs(vmd, vmdirq->irq);
vector = pci_irq_vector(vmd->dev, index);
irq_domain_set_info(domain, virq, vmdirq->irq->vmd_vector, info->chip, irq_domain_set_info(domain, virq, vector, info->chip, vmdirq,
vmdirq, handle_untracked_irq, vmd, NULL); handle_untracked_irq, vmd, NULL);
return 0; return 0;
} }
...@@ -213,6 +218,8 @@ static void vmd_msi_free(struct irq_domain *domain, ...@@ -213,6 +218,8 @@ static void vmd_msi_free(struct irq_domain *domain,
struct vmd_irq *vmdirq = irq_get_chip_data(virq); struct vmd_irq *vmdirq = irq_get_chip_data(virq);
unsigned long flags; unsigned long flags;
synchronize_rcu();
/* XXX: Potential optimization to rebalance */ /* XXX: Potential optimization to rebalance */
raw_spin_lock_irqsave(&list_lock, flags); raw_spin_lock_irqsave(&list_lock, flags);
vmdirq->irq->count--; vmdirq->irq->count--;
...@@ -672,30 +679,19 @@ static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id) ...@@ -672,30 +679,19 @@ static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id)
if (vmd->msix_count < 0) if (vmd->msix_count < 0)
return -ENODEV; return -ENODEV;
vmd->msix_count = pci_alloc_irq_vectors(dev, 1, vmd->msix_count,
PCI_IRQ_MSIX | PCI_IRQ_AFFINITY);
if (vmd->msix_count < 0)
return vmd->msix_count;
vmd->irqs = devm_kcalloc(&dev->dev, vmd->msix_count, sizeof(*vmd->irqs), vmd->irqs = devm_kcalloc(&dev->dev, vmd->msix_count, sizeof(*vmd->irqs),
GFP_KERNEL); GFP_KERNEL);
if (!vmd->irqs) if (!vmd->irqs)
return -ENOMEM; return -ENOMEM;
vmd->msix_entries = devm_kcalloc(&dev->dev, vmd->msix_count,
sizeof(*vmd->msix_entries),
GFP_KERNEL);
if (!vmd->msix_entries)
return -ENOMEM;
for (i = 0; i < vmd->msix_count; i++)
vmd->msix_entries[i].entry = i;
vmd->msix_count = pci_enable_msix_range(vmd->dev, vmd->msix_entries, 1,
vmd->msix_count);
if (vmd->msix_count < 0)
return vmd->msix_count;
for (i = 0; i < vmd->msix_count; i++) { for (i = 0; i < vmd->msix_count; i++) {
INIT_LIST_HEAD(&vmd->irqs[i].irq_list); INIT_LIST_HEAD(&vmd->irqs[i].irq_list);
vmd->irqs[i].vmd_vector = vmd->msix_entries[i].vector; err = devm_request_irq(&dev->dev, pci_irq_vector(dev, i),
vmd->irqs[i].index = i;
err = devm_request_irq(&dev->dev, vmd->irqs[i].vmd_vector,
vmd_irq, 0, "vmd", &vmd->irqs[i]); vmd_irq, 0, "vmd", &vmd->irqs[i]);
if (err) if (err)
return err; return err;
......
...@@ -1069,7 +1069,7 @@ static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec, ...@@ -1069,7 +1069,7 @@ static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec,
nvec = maxvec; nvec = maxvec;
for (;;) { for (;;) {
if (!(flags & PCI_IRQ_NOAFFINITY)) { if (flags & PCI_IRQ_AFFINITY) {
dev->irq_affinity = irq_create_affinity_mask(&nvec); dev->irq_affinity = irq_create_affinity_mask(&nvec);
if (nvec < minvec) if (nvec < minvec)
return -ENOSPC; return -ENOSPC;
...@@ -1105,7 +1105,7 @@ static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec, ...@@ -1105,7 +1105,7 @@ static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec,
**/ **/
int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec) int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec)
{ {
return __pci_enable_msi_range(dev, minvec, maxvec, PCI_IRQ_NOAFFINITY); return __pci_enable_msi_range(dev, minvec, maxvec, 0);
} }
EXPORT_SYMBOL(pci_enable_msi_range); EXPORT_SYMBOL(pci_enable_msi_range);
...@@ -1120,7 +1120,7 @@ static int __pci_enable_msix_range(struct pci_dev *dev, ...@@ -1120,7 +1120,7 @@ static int __pci_enable_msix_range(struct pci_dev *dev,
return -ERANGE; return -ERANGE;
for (;;) { for (;;) {
if (!(flags & PCI_IRQ_NOAFFINITY)) { if (flags & PCI_IRQ_AFFINITY) {
dev->irq_affinity = irq_create_affinity_mask(&nvec); dev->irq_affinity = irq_create_affinity_mask(&nvec);
if (nvec < minvec) if (nvec < minvec)
return -ENOSPC; return -ENOSPC;
...@@ -1160,8 +1160,7 @@ static int __pci_enable_msix_range(struct pci_dev *dev, ...@@ -1160,8 +1160,7 @@ static int __pci_enable_msix_range(struct pci_dev *dev,
int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries, int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries,
int minvec, int maxvec) int minvec, int maxvec)
{ {
return __pci_enable_msix_range(dev, entries, minvec, maxvec, return __pci_enable_msix_range(dev, entries, minvec, maxvec, 0);
PCI_IRQ_NOAFFINITY);
} }
EXPORT_SYMBOL(pci_enable_msix_range); EXPORT_SYMBOL(pci_enable_msix_range);
...@@ -1187,21 +1186,21 @@ int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs, ...@@ -1187,21 +1186,21 @@ int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs,
{ {
int vecs = -ENOSPC; int vecs = -ENOSPC;
if (!(flags & PCI_IRQ_NOMSIX)) { if (flags & PCI_IRQ_MSIX) {
vecs = __pci_enable_msix_range(dev, NULL, min_vecs, max_vecs, vecs = __pci_enable_msix_range(dev, NULL, min_vecs, max_vecs,
flags); flags);
if (vecs > 0) if (vecs > 0)
return vecs; return vecs;
} }
if (!(flags & PCI_IRQ_NOMSI)) { if (flags & PCI_IRQ_MSI) {
vecs = __pci_enable_msi_range(dev, min_vecs, max_vecs, flags); vecs = __pci_enable_msi_range(dev, min_vecs, max_vecs, flags);
if (vecs > 0) if (vecs > 0)
return vecs; return vecs;
} }
/* use legacy irq if allowed */ /* use legacy irq if allowed */
if (!(flags & PCI_IRQ_NOLEGACY) && min_vecs == 1) if ((flags & PCI_IRQ_LEGACY) && min_vecs == 1)
return 1; return 1;
return vecs; return vecs;
} }
......
...@@ -1264,10 +1264,12 @@ resource_size_t pcibios_iov_resource_alignment(struct pci_dev *dev, int resno); ...@@ -1264,10 +1264,12 @@ resource_size_t pcibios_iov_resource_alignment(struct pci_dev *dev, int resno);
int pci_set_vga_state(struct pci_dev *pdev, bool decode, int pci_set_vga_state(struct pci_dev *pdev, bool decode,
unsigned int command_bits, u32 flags); unsigned int command_bits, u32 flags);
#define PCI_IRQ_NOLEGACY (1 << 0) /* don't use legacy interrupts */ #define PCI_IRQ_LEGACY (1 << 0) /* allow legacy interrupts */
#define PCI_IRQ_NOMSI (1 << 1) /* don't use MSI interrupts */ #define PCI_IRQ_MSI (1 << 1) /* allow MSI interrupts */
#define PCI_IRQ_NOMSIX (1 << 2) /* don't use MSI-X interrupts */ #define PCI_IRQ_MSIX (1 << 2) /* allow MSI-X interrupts */
#define PCI_IRQ_NOAFFINITY (1 << 3) /* don't auto-assign affinity */ #define PCI_IRQ_AFFINITY (1 << 3) /* auto-assign affinity */
#define PCI_IRQ_ALL_TYPES \
(PCI_IRQ_LEGACY | PCI_IRQ_MSI | PCI_IRQ_MSIX)
/* kmem_cache style wrapper around pci_alloc_consistent() */ /* kmem_cache style wrapper around pci_alloc_consistent() */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment