aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/host/vmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/host/vmd.c')
-rw-r--r--drivers/pci/host/vmd.c30
1 files changed, 22 insertions, 8 deletions
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 */
50struct vmd_irq { 50struct 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 */
64struct vmd_irq_list { 64struct 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
237static int vmd_msi_prepare(struct irq_domain *domain, struct device *dev, 238static 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
723static 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
717static void vmd_remove(struct pci_dev *dev) 731static 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
731static int vmd_suspend(struct device *dev) 745static 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);