aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/iommu/dmar.c207
-rw-r--r--drivers/iommu/intel-iommu.c54
-rw-r--r--include/linux/dmar.h24
3 files changed, 283 insertions, 2 deletions
diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c
index 6e4d851991f1..bf6bfd1f69aa 100644
--- a/drivers/iommu/dmar.c
+++ b/drivers/iommu/dmar.c
@@ -194,6 +194,209 @@ void dmar_free_dev_scope(struct pci_dev __rcu ***devices, int *cnt)
194 *cnt = 0; 194 *cnt = 0;
195} 195}
196 196
197/* Optimize out kzalloc()/kfree() for normal cases */
198static char dmar_pci_notify_info_buf[64];
199
200static struct dmar_pci_notify_info *
201dmar_alloc_pci_notify_info(struct pci_dev *dev, unsigned long event)
202{
203 int level = 0;
204 size_t size;
205 struct pci_dev *tmp;
206 struct dmar_pci_notify_info *info;
207
208 BUG_ON(dev->is_virtfn);
209
210 /* Only generate path[] for device addition event */
211 if (event == BUS_NOTIFY_ADD_DEVICE)
212 for (tmp = dev; tmp; tmp = tmp->bus->self)
213 level++;
214
215 size = sizeof(*info) + level * sizeof(struct acpi_dmar_pci_path);
216 if (size <= sizeof(dmar_pci_notify_info_buf)) {
217 info = (struct dmar_pci_notify_info *)dmar_pci_notify_info_buf;
218 } else {
219 info = kzalloc(size, GFP_KERNEL);
220 if (!info) {
221 pr_warn("Out of memory when allocating notify_info "
222 "for %s.\n", pci_name(dev));
223 return NULL;
224 }
225 }
226
227 info->event = event;
228 info->dev = dev;
229 info->seg = pci_domain_nr(dev->bus);
230 info->level = level;
231 if (event == BUS_NOTIFY_ADD_DEVICE) {
232 for (tmp = dev, level--; tmp; tmp = tmp->bus->self) {
233 info->path[level].device = PCI_SLOT(tmp->devfn);
234 info->path[level].function = PCI_FUNC(tmp->devfn);
235 if (pci_is_root_bus(tmp->bus))
236 info->bus = tmp->bus->number;
237 }
238 }
239
240 return info;
241}
242
243static inline void dmar_free_pci_notify_info(struct dmar_pci_notify_info *info)
244{
245 if ((void *)info != dmar_pci_notify_info_buf)
246 kfree(info);
247}
248
249static bool dmar_match_pci_path(struct dmar_pci_notify_info *info, int bus,
250 struct acpi_dmar_pci_path *path, int count)
251{
252 int i;
253
254 if (info->bus != bus)
255 return false;
256 if (info->level != count)
257 return false;
258
259 for (i = 0; i < count; i++) {
260 if (path[i].device != info->path[i].device ||
261 path[i].function != info->path[i].function)
262 return false;
263 }
264
265 return true;
266}
267
268/* Return: > 0 if match found, 0 if no match found, < 0 if error happens */
269int dmar_insert_dev_scope(struct dmar_pci_notify_info *info,
270 void *start, void*end, u16 segment,
271 struct pci_dev __rcu **devices, int devices_cnt)
272{
273 int i, level;
274 struct pci_dev *tmp, *dev = info->dev;
275 struct acpi_dmar_device_scope *scope;
276 struct acpi_dmar_pci_path *path;
277
278 if (segment != info->seg)
279 return 0;
280
281 for (; start < end; start += scope->length) {
282 scope = start;
283 if (scope->entry_type != ACPI_DMAR_SCOPE_TYPE_ENDPOINT &&
284 scope->entry_type != ACPI_DMAR_SCOPE_TYPE_BRIDGE)
285 continue;
286
287 path = (struct acpi_dmar_pci_path *)(scope + 1);
288 level = (scope->length - sizeof(*scope)) / sizeof(*path);
289 if (!dmar_match_pci_path(info, scope->bus, path, level))
290 continue;
291
292 if ((scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT) ^
293 (dev->hdr_type == PCI_HEADER_TYPE_NORMAL)) {
294 pr_warn("Device scope type does not match for %s\n",
295 pci_name(dev));
296 return -EINVAL;
297 }
298
299 for_each_dev_scope(devices, devices_cnt, i, tmp)
300 if (tmp == NULL) {
301 rcu_assign_pointer(devices[i],
302 pci_dev_get(dev));
303 return 1;
304 }
305 BUG_ON(i >= devices_cnt);
306 }
307
308 return 0;
309}
310
311int dmar_remove_dev_scope(struct dmar_pci_notify_info *info, u16 segment,
312 struct pci_dev __rcu **devices, int count)
313{
314 int index;
315 struct pci_dev *tmp;
316
317 if (info->seg != segment)
318 return 0;
319
320 for_each_active_dev_scope(devices, count, index, tmp)
321 if (tmp == info->dev) {
322 rcu_assign_pointer(devices[index], NULL);
323 synchronize_rcu();
324 pci_dev_put(tmp);
325 return 1;
326 }
327
328 return 0;
329}
330
331static int dmar_pci_bus_add_dev(struct dmar_pci_notify_info *info)
332{
333 int ret = 0;
334 struct dmar_drhd_unit *dmaru;
335 struct acpi_dmar_hardware_unit *drhd;
336
337 for_each_drhd_unit(dmaru) {
338 if (dmaru->include_all)
339 continue;
340
341 drhd = container_of(dmaru->hdr,
342 struct acpi_dmar_hardware_unit, header);
343 ret = dmar_insert_dev_scope(info, (void *)(drhd + 1),
344 ((void *)drhd) + drhd->header.length,
345 dmaru->segment,
346 dmaru->devices, dmaru->devices_cnt);
347 if (ret != 0)
348 break;
349 }
350 if (ret >= 0)
351 ret = dmar_iommu_notify_scope_dev(info);
352
353 return ret;
354}
355
356static void dmar_pci_bus_del_dev(struct dmar_pci_notify_info *info)
357{
358 struct dmar_drhd_unit *dmaru;
359
360 for_each_drhd_unit(dmaru)
361 if (dmar_remove_dev_scope(info, dmaru->segment,
362 dmaru->devices, dmaru->devices_cnt))
363 break;
364 dmar_iommu_notify_scope_dev(info);
365}
366
367static int dmar_pci_bus_notifier(struct notifier_block *nb,
368 unsigned long action, void *data)
369{
370 struct pci_dev *pdev = to_pci_dev(data);
371 struct dmar_pci_notify_info *info;
372
373 /* Only care about add/remove events for physical functions */
374 if (pdev->is_virtfn)
375 return NOTIFY_DONE;
376 if (action != BUS_NOTIFY_ADD_DEVICE && action != BUS_NOTIFY_DEL_DEVICE)
377 return NOTIFY_DONE;
378
379 info = dmar_alloc_pci_notify_info(pdev, action);
380 if (!info)
381 return NOTIFY_DONE;
382
383 down_write(&dmar_global_lock);
384 if (action == BUS_NOTIFY_ADD_DEVICE)
385 dmar_pci_bus_add_dev(info);
386 else if (action == BUS_NOTIFY_DEL_DEVICE)
387 dmar_pci_bus_del_dev(info);
388 up_write(&dmar_global_lock);
389
390 dmar_free_pci_notify_info(info);
391
392 return NOTIFY_OK;
393}
394
395static struct notifier_block dmar_pci_bus_nb = {
396 .notifier_call = dmar_pci_bus_notifier,
397 .priority = INT_MIN,
398};
399
197/** 400/**
198 * dmar_parse_one_drhd - parses exactly one DMA remapping hardware definition 401 * dmar_parse_one_drhd - parses exactly one DMA remapping hardware definition
199 * structure which uniquely represent one DMA remapping hardware unit 402 * structure which uniquely represent one DMA remapping hardware unit
@@ -482,6 +685,8 @@ int __init dmar_dev_scope_init(void)
482 if (ret) 685 if (ret)
483 goto fail; 686 goto fail;
484 687
688 bus_register_notifier(&pci_bus_type, &dmar_pci_bus_nb);
689
485 dmar_dev_scope_initialized = 1; 690 dmar_dev_scope_initialized = 1;
486 return 0; 691 return 0;
487 692
@@ -1412,6 +1617,8 @@ static int __init dmar_free_unused_resources(void)
1412 if (irq_remapping_enabled || intel_iommu_enabled) 1617 if (irq_remapping_enabled || intel_iommu_enabled)
1413 return 0; 1618 return 0;
1414 1619
1620 bus_unregister_notifier(&pci_bus_type, &dmar_pci_bus_nb);
1621
1415 down_write(&dmar_global_lock); 1622 down_write(&dmar_global_lock);
1416 list_for_each_entry_safe(dmaru, dmaru_n, &dmar_drhd_units, list) { 1623 list_for_each_entry_safe(dmaru, dmaru_n, &dmar_drhd_units, list) {
1417 list_del(&dmaru->list); 1624 list_del(&dmaru->list);
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index e1679a6fe468..d9c0dc5a5d35 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -3625,6 +3625,60 @@ int __init dmar_parse_rmrr_atsr_dev(void)
3625 return 0; 3625 return 0;
3626} 3626}
3627 3627
3628int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info)
3629{
3630 int ret = 0;
3631 struct dmar_rmrr_unit *rmrru;
3632 struct dmar_atsr_unit *atsru;
3633 struct acpi_dmar_atsr *atsr;
3634 struct acpi_dmar_reserved_memory *rmrr;
3635
3636 if (!intel_iommu_enabled && system_state != SYSTEM_BOOTING)
3637 return 0;
3638
3639 list_for_each_entry(rmrru, &dmar_rmrr_units, list) {
3640 rmrr = container_of(rmrru->hdr,
3641 struct acpi_dmar_reserved_memory, header);
3642 if (info->event == BUS_NOTIFY_ADD_DEVICE) {
3643 ret = dmar_insert_dev_scope(info, (void *)(rmrr + 1),
3644 ((void *)rmrr) + rmrr->header.length,
3645 rmrr->segment, rmrru->devices,
3646 rmrru->devices_cnt);
3647 if (ret > 0)
3648 break;
3649 else if(ret < 0)
3650 return ret;
3651 } else if (info->event == BUS_NOTIFY_DEL_DEVICE) {
3652 if (dmar_remove_dev_scope(info, rmrr->segment,
3653 rmrru->devices, rmrru->devices_cnt))
3654 break;
3655 }
3656 }
3657
3658 list_for_each_entry(atsru, &dmar_atsr_units, list) {
3659 if (atsru->include_all)
3660 continue;
3661
3662 atsr = container_of(atsru->hdr, struct acpi_dmar_atsr, header);
3663 if (info->event == BUS_NOTIFY_ADD_DEVICE) {
3664 ret = dmar_insert_dev_scope(info, (void *)(atsr + 1),
3665 (void *)atsr + atsr->header.length,
3666 atsr->segment, atsru->devices,
3667 atsru->devices_cnt);
3668 if (ret > 0)
3669 break;
3670 else if(ret < 0)
3671 return ret;
3672 } else if (info->event == BUS_NOTIFY_DEL_DEVICE) {
3673 if (dmar_remove_dev_scope(info, atsr->segment,
3674 atsru->devices, atsru->devices_cnt))
3675 break;
3676 }
3677 }
3678
3679 return 0;
3680}
3681
3628/* 3682/*
3629 * Here we only respond to action of unbound device from driver. 3683 * Here we only respond to action of unbound device from driver.
3630 * 3684 *
diff --git a/include/linux/dmar.h b/include/linux/dmar.h
index bedebab934b4..4e196430f1b2 100644
--- a/include/linux/dmar.h
+++ b/include/linux/dmar.h
@@ -50,6 +50,15 @@ struct dmar_drhd_unit {
50 struct intel_iommu *iommu; 50 struct intel_iommu *iommu;
51}; 51};
52 52
53struct dmar_pci_notify_info {
54 struct pci_dev *dev;
55 unsigned long event;
56 int bus;
57 u16 seg;
58 u16 level;
59 struct acpi_dmar_pci_path path[];
60} __attribute__((packed));
61
53extern struct rw_semaphore dmar_global_lock; 62extern struct rw_semaphore dmar_global_lock;
54extern struct list_head dmar_drhd_units; 63extern struct list_head dmar_drhd_units;
55 64
@@ -89,12 +98,18 @@ extern int dmar_parse_dev_scope(void *start, void *end, int *cnt,
89 struct pci_dev ***devices, u16 segment); 98 struct pci_dev ***devices, u16 segment);
90extern void *dmar_alloc_dev_scope(void *start, void *end, int *cnt); 99extern void *dmar_alloc_dev_scope(void *start, void *end, int *cnt);
91extern void dmar_free_dev_scope(struct pci_dev __rcu ***devices, int *cnt); 100extern void dmar_free_dev_scope(struct pci_dev __rcu ***devices, int *cnt);
92extern void dmar_free_dev_scope(struct pci_dev ***devices, int *cnt); 101extern int dmar_insert_dev_scope(struct dmar_pci_notify_info *info,
93 102 void *start, void*end, u16 segment,
103 struct pci_dev __rcu **devices,
104 int devices_cnt);
105extern int dmar_remove_dev_scope(struct dmar_pci_notify_info *info,
106 u16 segment, struct pci_dev __rcu **devices,
107 int count);
94/* Intel IOMMU detection */ 108/* Intel IOMMU detection */
95extern int detect_intel_iommu(void); 109extern int detect_intel_iommu(void);
96extern int enable_drhd_fault_handling(void); 110extern int enable_drhd_fault_handling(void);
97#else 111#else
112struct dmar_pci_notify_info;
98static inline int detect_intel_iommu(void) 113static inline int detect_intel_iommu(void)
99{ 114{
100 return -ENODEV; 115 return -ENODEV;
@@ -161,6 +176,7 @@ extern int iommu_detected, no_iommu;
161extern int dmar_parse_rmrr_atsr_dev(void); 176extern int dmar_parse_rmrr_atsr_dev(void);
162extern int dmar_parse_one_rmrr(struct acpi_dmar_header *header); 177extern int dmar_parse_one_rmrr(struct acpi_dmar_header *header);
163extern int dmar_parse_one_atsr(struct acpi_dmar_header *header); 178extern int dmar_parse_one_atsr(struct acpi_dmar_header *header);
179extern int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info);
164extern int intel_iommu_init(void); 180extern int intel_iommu_init(void);
165#else /* !CONFIG_INTEL_IOMMU: */ 181#else /* !CONFIG_INTEL_IOMMU: */
166static inline int intel_iommu_init(void) { return -ENODEV; } 182static inline int intel_iommu_init(void) { return -ENODEV; }
@@ -176,6 +192,10 @@ static inline int dmar_parse_rmrr_atsr_dev(void)
176{ 192{
177 return 0; 193 return 0;
178} 194}
195static inline int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info)
196{
197 return 0;
198}
179#endif /* CONFIG_INTEL_IOMMU */ 199#endif /* CONFIG_INTEL_IOMMU */
180 200
181#endif /* __DMAR_H__ */ 201#endif /* __DMAR_H__ */