diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2016-12-12 12:25:13 -0500 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2016-12-12 12:25:13 -0500 |
commit | b08d2e61a6f9ebf5210a047868362a5a4ff37144 (patch) | |
tree | 52cfdc7952e50e492af6225c056dd58090eae4dd | |
parent | d34efd22acace472ad33887842117933ee631391 (diff) | |
parent | 42db500a551f97551a901e2258f84a60baf4edfc (diff) |
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()
-rw-r--r-- | drivers/pci/host/Kconfig | 2 | ||||
-rw-r--r-- | drivers/pci/host/vmd.c | 30 |
2 files changed, 23 insertions, 9 deletions
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 117130968b80..898d2c48239c 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig | |||
@@ -288,7 +288,7 @@ config PCIE_ROCKCHIP | |||
288 | 4 slots. | 288 | 4 slots. |
289 | 289 | ||
290 | config VMD | 290 | config VMD |
291 | depends on PCI_MSI && X86_64 | 291 | depends on PCI_MSI && X86_64 && SRCU |
292 | tristate "Intel Volume Management Device Driver" | 292 | tristate "Intel Volume Management Device Driver" |
293 | default N | 293 | default N |
294 | ---help--- | 294 | ---help--- |
diff --git a/drivers/pci/host/vmd.c b/drivers/pci/host/vmd.c index 37e29b580be3..18ef1a93c10a 100644 --- a/drivers/pci/host/vmd.c +++ b/drivers/pci/host/vmd.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/module.h> | 19 | #include <linux/module.h> |
20 | #include <linux/msi.h> | 20 | #include <linux/msi.h> |
21 | #include <linux/pci.h> | 21 | #include <linux/pci.h> |
22 | #include <linux/srcu.h> | ||
22 | #include <linux/rculist.h> | 23 | #include <linux/rculist.h> |
23 | #include <linux/rcupdate.h> | 24 | #include <linux/rcupdate.h> |
24 | 25 | ||
@@ -39,7 +40,6 @@ static DEFINE_RAW_SPINLOCK(list_lock); | |||
39 | /** | 40 | /** |
40 | * struct vmd_irq - private data to map driver IRQ to the VMD shared vector | 41 | * struct vmd_irq - private data to map driver IRQ to the VMD shared vector |
41 | * @node: list item for parent traversal. | 42 | * @node: list item for parent traversal. |
42 | * @rcu: RCU callback item for freeing. | ||
43 | * @irq: back pointer to parent. | 43 | * @irq: back pointer to parent. |
44 | * @enabled: true if driver enabled IRQ | 44 | * @enabled: true if driver enabled IRQ |
45 | * @virq: the virtual IRQ value provided to the requesting driver. | 45 | * @virq: the virtual IRQ value provided to the requesting driver. |
@@ -49,7 +49,6 @@ static DEFINE_RAW_SPINLOCK(list_lock); | |||
49 | */ | 49 | */ |
50 | struct vmd_irq { | 50 | struct vmd_irq { |
51 | struct list_head node; | 51 | struct list_head node; |
52 | struct rcu_head rcu; | ||
53 | struct vmd_irq_list *irq; | 52 | struct vmd_irq_list *irq; |
54 | bool enabled; | 53 | bool enabled; |
55 | unsigned int virq; | 54 | unsigned int virq; |
@@ -58,11 +57,13 @@ struct vmd_irq { | |||
58 | /** | 57 | /** |
59 | * struct vmd_irq_list - list of driver requested IRQs mapping to a VMD vector | 58 | * struct vmd_irq_list - list of driver requested IRQs mapping to a VMD vector |
60 | * @irq_list: the list of irq's the VMD one demuxes to. | 59 | * @irq_list: the list of irq's the VMD one demuxes to. |
60 | * @srcu: SRCU struct for local synchronization. | ||
61 | * @count: number of child IRQs assigned to this vector; used to track | 61 | * @count: number of child IRQs assigned to this vector; used to track |
62 | * sharing. | 62 | * sharing. |
63 | */ | 63 | */ |
64 | struct vmd_irq_list { | 64 | struct vmd_irq_list { |
65 | struct list_head irq_list; | 65 | struct list_head irq_list; |
66 | struct srcu_struct srcu; | ||
66 | unsigned int count; | 67 | unsigned int count; |
67 | }; | 68 | }; |
68 | 69 | ||
@@ -224,14 +225,14 @@ static void vmd_msi_free(struct irq_domain *domain, | |||
224 | struct vmd_irq *vmdirq = irq_get_chip_data(virq); | 225 | struct vmd_irq *vmdirq = irq_get_chip_data(virq); |
225 | unsigned long flags; | 226 | unsigned long flags; |
226 | 227 | ||
227 | synchronize_rcu(); | 228 | synchronize_srcu(&vmdirq->irq->srcu); |
228 | 229 | ||
229 | /* XXX: Potential optimization to rebalance */ | 230 | /* XXX: Potential optimization to rebalance */ |
230 | raw_spin_lock_irqsave(&list_lock, flags); | 231 | raw_spin_lock_irqsave(&list_lock, flags); |
231 | vmdirq->irq->count--; | 232 | vmdirq->irq->count--; |
232 | raw_spin_unlock_irqrestore(&list_lock, flags); | 233 | raw_spin_unlock_irqrestore(&list_lock, flags); |
233 | 234 | ||
234 | kfree_rcu(vmdirq, rcu); | 235 | kfree(vmdirq); |
235 | } | 236 | } |
236 | 237 | ||
237 | static int vmd_msi_prepare(struct irq_domain *domain, struct device *dev, | 238 | 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) | |||
646 | { | 647 | { |
647 | struct vmd_irq_list *irqs = data; | 648 | struct vmd_irq_list *irqs = data; |
648 | struct vmd_irq *vmdirq; | 649 | struct vmd_irq *vmdirq; |
650 | int idx; | ||
649 | 651 | ||
650 | rcu_read_lock(); | 652 | idx = srcu_read_lock(&irqs->srcu); |
651 | list_for_each_entry_rcu(vmdirq, &irqs->irq_list, node) | 653 | list_for_each_entry_rcu(vmdirq, &irqs->irq_list, node) |
652 | generic_handle_irq(vmdirq->virq); | 654 | generic_handle_irq(vmdirq->virq); |
653 | rcu_read_unlock(); | 655 | srcu_read_unlock(&irqs->srcu, idx); |
654 | 656 | ||
655 | return IRQ_HANDLED; | 657 | return IRQ_HANDLED; |
656 | } | 658 | } |
@@ -696,6 +698,10 @@ static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id) | |||
696 | return -ENOMEM; | 698 | return -ENOMEM; |
697 | 699 | ||
698 | for (i = 0; i < vmd->msix_count; i++) { | 700 | for (i = 0; i < vmd->msix_count; i++) { |
701 | err = init_srcu_struct(&vmd->irqs[i].srcu); | ||
702 | if (err) | ||
703 | return err; | ||
704 | |||
699 | INIT_LIST_HEAD(&vmd->irqs[i].irq_list); | 705 | INIT_LIST_HEAD(&vmd->irqs[i].irq_list); |
700 | err = devm_request_irq(&dev->dev, pci_irq_vector(dev, i), | 706 | err = devm_request_irq(&dev->dev, pci_irq_vector(dev, i), |
701 | vmd_irq, 0, "vmd", &vmd->irqs[i]); | 707 | 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) | |||
714 | return 0; | 720 | return 0; |
715 | } | 721 | } |
716 | 722 | ||
723 | static void vmd_cleanup_srcu(struct vmd_dev *vmd) | ||
724 | { | ||
725 | int i; | ||
726 | |||
727 | for (i = 0; i < vmd->msix_count; i++) | ||
728 | cleanup_srcu_struct(&vmd->irqs[i].srcu); | ||
729 | } | ||
730 | |||
717 | static void vmd_remove(struct pci_dev *dev) | 731 | static void vmd_remove(struct pci_dev *dev) |
718 | { | 732 | { |
719 | struct vmd_dev *vmd = pci_get_drvdata(dev); | 733 | struct vmd_dev *vmd = pci_get_drvdata(dev); |
720 | 734 | ||
721 | vmd_detach_resources(vmd); | 735 | vmd_detach_resources(vmd); |
722 | pci_set_drvdata(dev, NULL); | 736 | vmd_cleanup_srcu(vmd); |
723 | sysfs_remove_link(&vmd->dev->dev.kobj, "domain"); | 737 | sysfs_remove_link(&vmd->dev->dev.kobj, "domain"); |
724 | pci_stop_root_bus(vmd->bus); | 738 | pci_stop_root_bus(vmd->bus); |
725 | pci_remove_root_bus(vmd->bus); | 739 | pci_remove_root_bus(vmd->bus); |
@@ -727,7 +741,7 @@ static void vmd_remove(struct pci_dev *dev) | |||
727 | irq_domain_remove(vmd->irq_domain); | 741 | irq_domain_remove(vmd->irq_domain); |
728 | } | 742 | } |
729 | 743 | ||
730 | #ifdef CONFIG_PM | 744 | #ifdef CONFIG_PM_SLEEP |
731 | static int vmd_suspend(struct device *dev) | 745 | static int vmd_suspend(struct device *dev) |
732 | { | 746 | { |
733 | struct pci_dev *pdev = to_pci_dev(dev); | 747 | struct pci_dev *pdev = to_pci_dev(dev); |