Merge branch 'pci/host-vmd' into next

* pci/host-vmd:
  PCI: vmd: Fix suspend handlers defined-but-not-used warning
  PCI: vmd: Use SRCU as a local RCU to prevent delaying global RCU
  PCI: vmd: Remove unnecessary pci_set_drvdata()
This commit is contained in:
Bjorn Helgaas 2016-12-12 11:25:13 -06:00
commit b08d2e61a6
2 changed files with 23 additions and 9 deletions

View File

@ -288,7 +288,7 @@ config PCIE_ROCKCHIP
4 slots. 4 slots.
config VMD config VMD
depends on PCI_MSI && X86_64 depends on PCI_MSI && X86_64 && SRCU
tristate "Intel Volume Management Device Driver" tristate "Intel Volume Management Device Driver"
default N default N
---help--- ---help---

View File

@ -19,6 +19,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/msi.h> #include <linux/msi.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/srcu.h>
#include <linux/rculist.h> #include <linux/rculist.h>
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
@ -39,7 +40,6 @@ static DEFINE_RAW_SPINLOCK(list_lock);
/** /**
* struct vmd_irq - private data to map driver IRQ to the VMD shared vector * struct vmd_irq - private data to map driver IRQ to the VMD shared vector
* @node: list item for parent traversal. * @node: list item for parent traversal.
* @rcu: RCU callback item for freeing.
* @irq: back pointer to parent. * @irq: back pointer to parent.
* @enabled: true if driver enabled IRQ * @enabled: true if driver enabled IRQ
* @virq: the virtual IRQ value provided to the requesting driver. * @virq: the virtual IRQ value provided to the requesting driver.
@ -49,7 +49,6 @@ static DEFINE_RAW_SPINLOCK(list_lock);
*/ */
struct vmd_irq { struct vmd_irq {
struct list_head node; struct list_head node;
struct rcu_head rcu;
struct vmd_irq_list *irq; struct vmd_irq_list *irq;
bool enabled; bool enabled;
unsigned int virq; unsigned int virq;
@ -58,11 +57,13 @@ 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.
* @srcu: SRCU struct for local synchronization.
* @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 srcu_struct srcu;
unsigned int count; unsigned int count;
}; };
@ -224,14 +225,14 @@ 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(); synchronize_srcu(&vmdirq->irq->srcu);
/* 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--;
raw_spin_unlock_irqrestore(&list_lock, flags); raw_spin_unlock_irqrestore(&list_lock, flags);
kfree_rcu(vmdirq, rcu); kfree(vmdirq);
} }
static int vmd_msi_prepare(struct irq_domain *domain, struct device *dev, static int vmd_msi_prepare(struct irq_domain *domain, struct device *dev,
@ -646,11 +647,12 @@ static irqreturn_t vmd_irq(int irq, void *data)
{ {
struct vmd_irq_list *irqs = data; struct vmd_irq_list *irqs = data;
struct vmd_irq *vmdirq; struct vmd_irq *vmdirq;
int idx;
rcu_read_lock(); idx = srcu_read_lock(&irqs->srcu);
list_for_each_entry_rcu(vmdirq, &irqs->irq_list, node) list_for_each_entry_rcu(vmdirq, &irqs->irq_list, node)
generic_handle_irq(vmdirq->virq); generic_handle_irq(vmdirq->virq);
rcu_read_unlock(); srcu_read_unlock(&irqs->srcu, idx);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
@ -696,6 +698,10 @@ static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id)
return -ENOMEM; return -ENOMEM;
for (i = 0; i < vmd->msix_count; i++) { for (i = 0; i < vmd->msix_count; i++) {
err = init_srcu_struct(&vmd->irqs[i].srcu);
if (err)
return err;
INIT_LIST_HEAD(&vmd->irqs[i].irq_list); INIT_LIST_HEAD(&vmd->irqs[i].irq_list);
err = devm_request_irq(&dev->dev, pci_irq_vector(dev, i), err = devm_request_irq(&dev->dev, pci_irq_vector(dev, i),
vmd_irq, 0, "vmd", &vmd->irqs[i]); vmd_irq, 0, "vmd", &vmd->irqs[i]);
@ -714,12 +720,20 @@ static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id)
return 0; return 0;
} }
static void vmd_cleanup_srcu(struct vmd_dev *vmd)
{
int i;
for (i = 0; i < vmd->msix_count; i++)
cleanup_srcu_struct(&vmd->irqs[i].srcu);
}
static void vmd_remove(struct pci_dev *dev) static void vmd_remove(struct pci_dev *dev)
{ {
struct vmd_dev *vmd = pci_get_drvdata(dev); struct vmd_dev *vmd = pci_get_drvdata(dev);
vmd_detach_resources(vmd); vmd_detach_resources(vmd);
pci_set_drvdata(dev, NULL); vmd_cleanup_srcu(vmd);
sysfs_remove_link(&vmd->dev->dev.kobj, "domain"); sysfs_remove_link(&vmd->dev->dev.kobj, "domain");
pci_stop_root_bus(vmd->bus); pci_stop_root_bus(vmd->bus);
pci_remove_root_bus(vmd->bus); pci_remove_root_bus(vmd->bus);
@ -727,7 +741,7 @@ static void vmd_remove(struct pci_dev *dev)
irq_domain_remove(vmd->irq_domain); irq_domain_remove(vmd->irq_domain);
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM_SLEEP
static int vmd_suspend(struct device *dev) static int vmd_suspend(struct device *dev)
{ {
struct pci_dev *pdev = to_pci_dev(dev); struct pci_dev *pdev = to_pci_dev(dev);