History

iommu/vt-d: Fix RID2PASID setup/teardown failure

The IOMMU driver shares the pasid table for PCI alias devices. When the RID2PASID entry of the shared pasid table has been filled by the first device, the subsequent device will encounter the "DMAR: Setup RID2PASID failed" failure as the pasid entry has already been marked as present. As the result, the IOMMU probing process will be aborted. On the contrary, when any alias device is hot-removed from the system, for example, by writing to /sys/bus/pci/devices/.../remove, the shared RID2PASID will be cleared without any notifications to other devices. As the result, any DMAs from those rest devices are blocked. Sharing pasid table among PCI alias devices could save two memory pages for devices underneath the PCIe-to-PCI bridges. Anyway, considering that those devices are rare on modern platforms that support VT-d in scalable mode and the saved memory is negligible, it's reasonable to remove this part of immature code to make the driver feasible and stable.

Lu Baolu committed 4 years ago

Commit 4140d771 parent 316f92a4 files · +3 −94Browse files

Unified · Split

4 files
  1. drivers/iommu/intel/iommu.c +0 −24
  2. drivers/iommu/intel/pasid.c +3 −66
  3. drivers/iommu/intel/pasid.h +0 −1
  4. include/linux/intel-iommu.h +0 −3
drivers/iommu/intel/iommu.c+0−24
@@ -320,30 +320,6 @@ EXPORT_SYMBOL_GPL(intel_iommu_gfx_mapped); · 319 unmodified lines
320DEFINE_SPINLOCK(device_domain_lock);320DEFINE_SPINLOCK(device_domain_lock);
321static LIST_HEAD(device_domain_list);321static LIST_HEAD(device_domain_list);
322 322
323/*
324 * Iterate over elements in device_domain_list and call the specified
325 * callback @fn against each element.
326 */
327int for_each_device_domain(int (*fn)(struct device_domain_info *info,
328 void *data), void *data)
329{
330 int ret = 0;
331 unsigned long flags;
332 struct device_domain_info *info;
333
334 spin_lock_irqsave(&device_domain_lock, flags);
335 list_for_each_entry(info, &device_domain_list, global) {
336 ret = fn(info, data);
337 if (ret) {
338 spin_unlock_irqrestore(&device_domain_lock, flags);
339 return ret;
340 }
341 }
342 spin_unlock_irqrestore(&device_domain_lock, flags);
343
344 return 0;
345}
346
347const struct iommu_ops intel_iommu_ops;323const struct iommu_ops intel_iommu_ops;
348 324
349static bool translation_pre_enabled(struct intel_iommu *iommu)325static bool translation_pre_enabled(struct intel_iommu *iommu)
drivers/iommu/intel/pasid.c+3−66
@@ -86,54 +86,6 @@ void vcmd_free_pasid(struct intel_iommu *iommu, u32 pasid) · 85 unmodified lines
86/*86/*
87 * Per device pasid table management:87 * Per device pasid table management:
88 */88 */
89static inline void
90device_attach_pasid_table(struct device_domain_info *info,
91 struct pasid_table *pasid_table)
92{
93 info->pasid_table = pasid_table;
94 list_add(&info->table, &pasid_table->dev);
95}
96
97static inline void
98device_detach_pasid_table(struct device_domain_info *info,
99 struct pasid_table *pasid_table)
100{
101 info->pasid_table = NULL;
102 list_del(&info->table);
103}
104
105struct pasid_table_opaque {
106 struct pasid_table **pasid_table;
107 int segment;
108 int bus;
109 int devfn;
110};
111
112static int search_pasid_table(struct device_domain_info *info, void *opaque)
113{
114 struct pasid_table_opaque *data = opaque;
115
116 if (info->iommu->segment == data->segment &&
117 info->bus == data->bus &&
118 info->devfn == data->devfn &&
119 info->pasid_table) {
120 *data->pasid_table = info->pasid_table;
121 return 1;
122 }
123
124 return 0;
125}
126
127static int get_alias_pasid_table(struct pci_dev *pdev, u16 alias, void *opaque)
128{
129 struct pasid_table_opaque *data = opaque;
130
131 data->segment = pci_domain_nr(pdev->bus);
132 data->bus = PCI_BUS_NUM(alias);
133 data->devfn = alias & 0xff;
134
135 return for_each_device_domain(&search_pasid_table, data);
136}
137 89
138/*90/*
139 * Allocate a pasid table for @dev. It should be called in a91 * Allocate a pasid table for @dev. It should be called in a
@@ -143,28 +95,18 @@ int intel_pasid_alloc_table(struct device *dev) · 3 unmodified lines
143{95{
144 struct device_domain_info *info;96 struct device_domain_info *info;
145 struct pasid_table *pasid_table;97 struct pasid_table *pasid_table;
146 struct pasid_table_opaque data;
147 struct page *pages;98 struct page *pages;
148 u32 max_pasid = 0;99 u32 max_pasid = 0;
149 int ret, order;100 int order, size;
150 int size;
151 101
152 might_sleep();102 might_sleep();
153 info = dev_iommu_priv_get(dev);103 info = dev_iommu_priv_get(dev);
154 if (WARN_ON(!info || !dev_is_pci(dev) || info->pasid_table))104 if (WARN_ON(!info || !dev_is_pci(dev) || info->pasid_table))
155 return -EINVAL;105 return -EINVAL;
156 106
157 /* DMA alias device already has a pasid table, use it: */
158 data.pasid_table = &pasid_table;
159 ret = pci_for_each_dma_alias(to_pci_dev(dev),
160 &get_alias_pasid_table, &data);
161 if (ret)
162 goto attach_out;
163
164 pasid_table = kzalloc(sizeof(*pasid_table), GFP_KERNEL);107 pasid_table = kzalloc(sizeof(*pasid_table), GFP_KERNEL);
165 if (!pasid_table)108 if (!pasid_table)
166 return -ENOMEM;109 return -ENOMEM;
167 INIT_LIST_HEAD(&pasid_table->dev);
168 110
169 if (info->pasid_supported)111 if (info->pasid_supported)
170 max_pasid = min_t(u32, pci_max_pasids(to_pci_dev(dev)),112 max_pasid = min_t(u32, pci_max_pasids(to_pci_dev(dev)),
@@ -182,9 +124,7 @@ int intel_pasid_alloc_table(struct device *dev) · 11 unmodified lines
182 pasid_table->table = page_address(pages);124 pasid_table->table = page_address(pages);
183 pasid_table->order = order;125 pasid_table->order = order;
184 pasid_table->max_pasid = 1 << (order + PAGE_SHIFT + 3);126 pasid_table->max_pasid = 1 << (order + PAGE_SHIFT + 3);
185 127 info->pasid_table = pasid_table;
186attach_out:
187 device_attach_pasid_table(info, pasid_table);
188 128
189 return 0;129 return 0;
190}130}
@@ -202,10 +142,7 @@ void intel_pasid_free_table(struct device *dev) · 11 unmodified lines
202 return;142 return;
203 143
204 pasid_table = info->pasid_table;144 pasid_table = info->pasid_table;
205 device_detach_pasid_table(info, pasid_table);145 info->pasid_table = NULL;
206
207 if (!list_empty(&pasid_table->dev))
208 return;
209 146
210 /* Free scalable mode PASID directory tables: */147 /* Free scalable mode PASID directory tables: */
211 dir = pasid_table->table;148 dir = pasid_table->table;
drivers/iommu/intel/pasid.h+0−1
@@ -74,7 +74,6 @@ struct pasid_table { · 73 unmodified lines
74 void *table; /* pasid table pointer */74 void *table; /* pasid table pointer */
75 int order; /* page order of pasid table */75 int order; /* page order of pasid table */
76 u32 max_pasid; /* max pasid */76 u32 max_pasid; /* max pasid */
77 struct list_head dev; /* device list */
78};77};
79 78
80/* Get PRESENT bit of a PASID directory entry. */79/* Get PRESENT bit of a PASID directory entry. */
include/linux/intel-iommu.h+0−3
@@ -612,7 +612,6 @@ struct intel_iommu { · 611 unmodified lines
612struct device_domain_info {612struct device_domain_info {
613 struct list_head link; /* link to domain siblings */613 struct list_head link; /* link to domain siblings */
614 struct list_head global; /* link to global list */614 struct list_head global; /* link to global list */
615 struct list_head table; /* link to pasid table */
616 u32 segment; /* PCI segment number */615 u32 segment; /* PCI segment number */
617 u8 bus; /* PCI bus number */616 u8 bus; /* PCI bus number */
618 u8 devfn; /* PCI devfn number */617 u8 devfn; /* PCI devfn number */
@@ -729,8 +728,6 @@ extern int dmar_ir_support(void); · 110 unmodified lines
729void *alloc_pgtable_page(int node);728void *alloc_pgtable_page(int node);
730void free_pgtable_page(void *vaddr);729void free_pgtable_page(void *vaddr);
731struct intel_iommu *domain_get_iommu(struct dmar_domain *domain);730struct intel_iommu *domain_get_iommu(struct dmar_domain *domain);
732int for_each_device_domain(int (*fn)(struct device_domain_info *info,
733 void *data), void *data);
734void iommu_flush_write_buffer(struct intel_iommu *iommu);731void iommu_flush_write_buffer(struct intel_iommu *iommu);
735int intel_iommu_enable_pasid(struct intel_iommu *iommu, struct device *dev);732int intel_iommu_enable_pasid(struct intel_iommu *iommu, struct device *dev);
736struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devfn);733struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devfn);