diff options
-rw-r--r-- | drivers/pci/Kconfig | 4 | ||||
-rw-r--r-- | drivers/pci/Makefile | 1 | ||||
-rw-r--r-- | drivers/pci/ats.c | 155 | ||||
-rw-r--r-- | drivers/pci/iov.c | 142 | ||||
-rw-r--r-- | include/linux/pci-ats.h | 2 |
5 files changed, 162 insertions, 142 deletions
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 0fa466a91bf4..1d8ce8395861 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig | |||
@@ -71,9 +71,13 @@ config HT_IRQ | |||
71 | 71 | ||
72 | If unsure say Y. | 72 | If unsure say Y. |
73 | 73 | ||
74 | config PCI_ATS | ||
75 | bool | ||
76 | |||
74 | config PCI_IOV | 77 | config PCI_IOV |
75 | bool "PCI IOV support" | 78 | bool "PCI IOV support" |
76 | depends on PCI | 79 | depends on PCI |
80 | select PCI_ATS | ||
77 | help | 81 | help |
78 | I/O Virtualization is a PCI feature supported by some devices | 82 | I/O Virtualization is a PCI feature supported by some devices |
79 | which allows them to create virtual devices which share their | 83 | which allows them to create virtual devices which share their |
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 6fadae3ad134..083a49fee56a 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile | |||
@@ -29,6 +29,7 @@ obj-$(CONFIG_PCI_MSI) += msi.o | |||
29 | # Build the Hypertransport interrupt support | 29 | # Build the Hypertransport interrupt support |
30 | obj-$(CONFIG_HT_IRQ) += htirq.o | 30 | obj-$(CONFIG_HT_IRQ) += htirq.o |
31 | 31 | ||
32 | obj-$(CONFIG_PCI_ATS) += ats.o | ||
32 | obj-$(CONFIG_PCI_IOV) += iov.o | 33 | obj-$(CONFIG_PCI_IOV) += iov.o |
33 | 34 | ||
34 | # | 35 | # |
diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c new file mode 100644 index 000000000000..ae4bf87afb09 --- /dev/null +++ b/drivers/pci/ats.c | |||
@@ -0,0 +1,155 @@ | |||
1 | /* | ||
2 | * drivers/pci/ats.c | ||
3 | * | ||
4 | * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com> | ||
5 | * | ||
6 | * PCI Express I/O Virtualization (IOV) support. | ||
7 | * Address Translation Service 1.0 | ||
8 | */ | ||
9 | |||
10 | #include <linux/pci-ats.h> | ||
11 | #include <linux/pci.h> | ||
12 | |||
13 | #include "pci.h" | ||
14 | |||
15 | static int ats_alloc_one(struct pci_dev *dev, int ps) | ||
16 | { | ||
17 | int pos; | ||
18 | u16 cap; | ||
19 | struct pci_ats *ats; | ||
20 | |||
21 | pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS); | ||
22 | if (!pos) | ||
23 | return -ENODEV; | ||
24 | |||
25 | ats = kzalloc(sizeof(*ats), GFP_KERNEL); | ||
26 | if (!ats) | ||
27 | return -ENOMEM; | ||
28 | |||
29 | ats->pos = pos; | ||
30 | ats->stu = ps; | ||
31 | pci_read_config_word(dev, pos + PCI_ATS_CAP, &cap); | ||
32 | ats->qdep = PCI_ATS_CAP_QDEP(cap) ? PCI_ATS_CAP_QDEP(cap) : | ||
33 | PCI_ATS_MAX_QDEP; | ||
34 | dev->ats = ats; | ||
35 | |||
36 | return 0; | ||
37 | } | ||
38 | |||
39 | static void ats_free_one(struct pci_dev *dev) | ||
40 | { | ||
41 | kfree(dev->ats); | ||
42 | dev->ats = NULL; | ||
43 | } | ||
44 | |||
45 | /** | ||
46 | * pci_enable_ats - enable the ATS capability | ||
47 | * @dev: the PCI device | ||
48 | * @ps: the IOMMU page shift | ||
49 | * | ||
50 | * Returns 0 on success, or negative on failure. | ||
51 | */ | ||
52 | int pci_enable_ats(struct pci_dev *dev, int ps) | ||
53 | { | ||
54 | int rc; | ||
55 | u16 ctrl; | ||
56 | |||
57 | BUG_ON(dev->ats && dev->ats->is_enabled); | ||
58 | |||
59 | if (ps < PCI_ATS_MIN_STU) | ||
60 | return -EINVAL; | ||
61 | |||
62 | if (dev->is_physfn || dev->is_virtfn) { | ||
63 | struct pci_dev *pdev = dev->is_physfn ? dev : dev->physfn; | ||
64 | |||
65 | mutex_lock(&pdev->sriov->lock); | ||
66 | if (pdev->ats) | ||
67 | rc = pdev->ats->stu == ps ? 0 : -EINVAL; | ||
68 | else | ||
69 | rc = ats_alloc_one(pdev, ps); | ||
70 | |||
71 | if (!rc) | ||
72 | pdev->ats->ref_cnt++; | ||
73 | mutex_unlock(&pdev->sriov->lock); | ||
74 | if (rc) | ||
75 | return rc; | ||
76 | } | ||
77 | |||
78 | if (!dev->is_physfn) { | ||
79 | rc = ats_alloc_one(dev, ps); | ||
80 | if (rc) | ||
81 | return rc; | ||
82 | } | ||
83 | |||
84 | ctrl = PCI_ATS_CTRL_ENABLE; | ||
85 | if (!dev->is_virtfn) | ||
86 | ctrl |= PCI_ATS_CTRL_STU(ps - PCI_ATS_MIN_STU); | ||
87 | pci_write_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, ctrl); | ||
88 | |||
89 | dev->ats->is_enabled = 1; | ||
90 | |||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * pci_disable_ats - disable the ATS capability | ||
96 | * @dev: the PCI device | ||
97 | */ | ||
98 | void pci_disable_ats(struct pci_dev *dev) | ||
99 | { | ||
100 | u16 ctrl; | ||
101 | |||
102 | BUG_ON(!dev->ats || !dev->ats->is_enabled); | ||
103 | |||
104 | pci_read_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, &ctrl); | ||
105 | ctrl &= ~PCI_ATS_CTRL_ENABLE; | ||
106 | pci_write_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, ctrl); | ||
107 | |||
108 | dev->ats->is_enabled = 0; | ||
109 | |||
110 | if (dev->is_physfn || dev->is_virtfn) { | ||
111 | struct pci_dev *pdev = dev->is_physfn ? dev : dev->physfn; | ||
112 | |||
113 | mutex_lock(&pdev->sriov->lock); | ||
114 | pdev->ats->ref_cnt--; | ||
115 | if (!pdev->ats->ref_cnt) | ||
116 | ats_free_one(pdev); | ||
117 | mutex_unlock(&pdev->sriov->lock); | ||
118 | } | ||
119 | |||
120 | if (!dev->is_physfn) | ||
121 | ats_free_one(dev); | ||
122 | } | ||
123 | |||
124 | /** | ||
125 | * pci_ats_queue_depth - query the ATS Invalidate Queue Depth | ||
126 | * @dev: the PCI device | ||
127 | * | ||
128 | * Returns the queue depth on success, or negative on failure. | ||
129 | * | ||
130 | * The ATS spec uses 0 in the Invalidate Queue Depth field to | ||
131 | * indicate that the function can accept 32 Invalidate Request. | ||
132 | * But here we use the `real' values (i.e. 1~32) for the Queue | ||
133 | * Depth; and 0 indicates the function shares the Queue with | ||
134 | * other functions (doesn't exclusively own a Queue). | ||
135 | */ | ||
136 | int pci_ats_queue_depth(struct pci_dev *dev) | ||
137 | { | ||
138 | int pos; | ||
139 | u16 cap; | ||
140 | |||
141 | if (dev->is_virtfn) | ||
142 | return 0; | ||
143 | |||
144 | if (dev->ats) | ||
145 | return dev->ats->qdep; | ||
146 | |||
147 | pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS); | ||
148 | if (!pos) | ||
149 | return -ENODEV; | ||
150 | |||
151 | pci_read_config_word(dev, pos + PCI_ATS_CAP, &cap); | ||
152 | |||
153 | return PCI_ATS_CAP_QDEP(cap) ? PCI_ATS_CAP_QDEP(cap) : | ||
154 | PCI_ATS_MAX_QDEP; | ||
155 | } | ||
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 42fae4776515..9b4e88c636f8 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c | |||
@@ -722,145 +722,3 @@ int pci_num_vf(struct pci_dev *dev) | |||
722 | return dev->sriov->nr_virtfn; | 722 | return dev->sriov->nr_virtfn; |
723 | } | 723 | } |
724 | EXPORT_SYMBOL_GPL(pci_num_vf); | 724 | EXPORT_SYMBOL_GPL(pci_num_vf); |
725 | |||
726 | static int ats_alloc_one(struct pci_dev *dev, int ps) | ||
727 | { | ||
728 | int pos; | ||
729 | u16 cap; | ||
730 | struct pci_ats *ats; | ||
731 | |||
732 | pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS); | ||
733 | if (!pos) | ||
734 | return -ENODEV; | ||
735 | |||
736 | ats = kzalloc(sizeof(*ats), GFP_KERNEL); | ||
737 | if (!ats) | ||
738 | return -ENOMEM; | ||
739 | |||
740 | ats->pos = pos; | ||
741 | ats->stu = ps; | ||
742 | pci_read_config_word(dev, pos + PCI_ATS_CAP, &cap); | ||
743 | ats->qdep = PCI_ATS_CAP_QDEP(cap) ? PCI_ATS_CAP_QDEP(cap) : | ||
744 | PCI_ATS_MAX_QDEP; | ||
745 | dev->ats = ats; | ||
746 | |||
747 | return 0; | ||
748 | } | ||
749 | |||
750 | static void ats_free_one(struct pci_dev *dev) | ||
751 | { | ||
752 | kfree(dev->ats); | ||
753 | dev->ats = NULL; | ||
754 | } | ||
755 | |||
756 | /** | ||
757 | * pci_enable_ats - enable the ATS capability | ||
758 | * @dev: the PCI device | ||
759 | * @ps: the IOMMU page shift | ||
760 | * | ||
761 | * Returns 0 on success, or negative on failure. | ||
762 | */ | ||
763 | int pci_enable_ats(struct pci_dev *dev, int ps) | ||
764 | { | ||
765 | int rc; | ||
766 | u16 ctrl; | ||
767 | |||
768 | BUG_ON(dev->ats && dev->ats->is_enabled); | ||
769 | |||
770 | if (ps < PCI_ATS_MIN_STU) | ||
771 | return -EINVAL; | ||
772 | |||
773 | if (dev->is_physfn || dev->is_virtfn) { | ||
774 | struct pci_dev *pdev = dev->is_physfn ? dev : dev->physfn; | ||
775 | |||
776 | mutex_lock(&pdev->sriov->lock); | ||
777 | if (pdev->ats) | ||
778 | rc = pdev->ats->stu == ps ? 0 : -EINVAL; | ||
779 | else | ||
780 | rc = ats_alloc_one(pdev, ps); | ||
781 | |||
782 | if (!rc) | ||
783 | pdev->ats->ref_cnt++; | ||
784 | mutex_unlock(&pdev->sriov->lock); | ||
785 | if (rc) | ||
786 | return rc; | ||
787 | } | ||
788 | |||
789 | if (!dev->is_physfn) { | ||
790 | rc = ats_alloc_one(dev, ps); | ||
791 | if (rc) | ||
792 | return rc; | ||
793 | } | ||
794 | |||
795 | ctrl = PCI_ATS_CTRL_ENABLE; | ||
796 | if (!dev->is_virtfn) | ||
797 | ctrl |= PCI_ATS_CTRL_STU(ps - PCI_ATS_MIN_STU); | ||
798 | pci_write_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, ctrl); | ||
799 | |||
800 | dev->ats->is_enabled = 1; | ||
801 | |||
802 | return 0; | ||
803 | } | ||
804 | |||
805 | /** | ||
806 | * pci_disable_ats - disable the ATS capability | ||
807 | * @dev: the PCI device | ||
808 | */ | ||
809 | void pci_disable_ats(struct pci_dev *dev) | ||
810 | { | ||
811 | u16 ctrl; | ||
812 | |||
813 | BUG_ON(!dev->ats || !dev->ats->is_enabled); | ||
814 | |||
815 | pci_read_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, &ctrl); | ||
816 | ctrl &= ~PCI_ATS_CTRL_ENABLE; | ||
817 | pci_write_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, ctrl); | ||
818 | |||
819 | dev->ats->is_enabled = 0; | ||
820 | |||
821 | if (dev->is_physfn || dev->is_virtfn) { | ||
822 | struct pci_dev *pdev = dev->is_physfn ? dev : dev->physfn; | ||
823 | |||
824 | mutex_lock(&pdev->sriov->lock); | ||
825 | pdev->ats->ref_cnt--; | ||
826 | if (!pdev->ats->ref_cnt) | ||
827 | ats_free_one(pdev); | ||
828 | mutex_unlock(&pdev->sriov->lock); | ||
829 | } | ||
830 | |||
831 | if (!dev->is_physfn) | ||
832 | ats_free_one(dev); | ||
833 | } | ||
834 | |||
835 | /** | ||
836 | * pci_ats_queue_depth - query the ATS Invalidate Queue Depth | ||
837 | * @dev: the PCI device | ||
838 | * | ||
839 | * Returns the queue depth on success, or negative on failure. | ||
840 | * | ||
841 | * The ATS spec uses 0 in the Invalidate Queue Depth field to | ||
842 | * indicate that the function can accept 32 Invalidate Request. | ||
843 | * But here we use the `real' values (i.e. 1~32) for the Queue | ||
844 | * Depth; and 0 indicates the function shares the Queue with | ||
845 | * other functions (doesn't exclusively own a Queue). | ||
846 | */ | ||
847 | int pci_ats_queue_depth(struct pci_dev *dev) | ||
848 | { | ||
849 | int pos; | ||
850 | u16 cap; | ||
851 | |||
852 | if (dev->is_virtfn) | ||
853 | return 0; | ||
854 | |||
855 | if (dev->ats) | ||
856 | return dev->ats->qdep; | ||
857 | |||
858 | pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS); | ||
859 | if (!pos) | ||
860 | return -ENODEV; | ||
861 | |||
862 | pci_read_config_word(dev, pos + PCI_ATS_CAP, &cap); | ||
863 | |||
864 | return PCI_ATS_CAP_QDEP(cap) ? PCI_ATS_CAP_QDEP(cap) : | ||
865 | PCI_ATS_MAX_QDEP; | ||
866 | } | ||
diff --git a/include/linux/pci-ats.h b/include/linux/pci-ats.h index 655824fa4c76..4eab42bf2af9 100644 --- a/include/linux/pci-ats.h +++ b/include/linux/pci-ats.h | |||
@@ -1,6 +1,8 @@ | |||
1 | #ifndef LINUX_PCI_ATS_H | 1 | #ifndef LINUX_PCI_ATS_H |
2 | #define LINUX_PCI_ATS_H | 2 | #define LINUX_PCI_ATS_H |
3 | 3 | ||
4 | #include <linux/pci.h> | ||
5 | |||
4 | /* Address Translation Service */ | 6 | /* Address Translation Service */ |
5 | struct pci_ats { | 7 | struct pci_ats { |
6 | int pos; /* capability position */ | 8 | int pos; /* capability position */ |