diff options
author | Jiang Liu <jiang.liu@linux.intel.com> | 2014-01-06 01:18:20 -0500 |
---|---|---|
committer | Joerg Roedel <joro@8bytes.org> | 2014-01-09 06:43:40 -0500 |
commit | a868e6b7b661c3d3e7e681a16d0b205971987c99 (patch) | |
tree | 574ee2bcb16845f467f5892c9ac23805c0efa35c /drivers/iommu | |
parent | b5f36d9e614135470da452f820f161c443d3c83c (diff) |
iommu/vt-d: keep shared resources when failed to initialize iommu devices
Data structure drhd->iommu is shared between DMA remapping driver and
interrupt remapping driver, so DMA remapping driver shouldn't release
drhd->iommu when it failed to initialize IOMMU devices. Otherwise it
may cause invalid memory access to the interrupt remapping driver.
Sample stack dump:
[ 13.315090] BUG: unable to handle kernel paging request at ffffc9000605a088
[ 13.323221] IP: [<ffffffff81461bac>] qi_submit_sync+0x15c/0x400
[ 13.330107] PGD 82f81e067 PUD c2f81e067 PMD 82e846067 PTE 0
[ 13.336818] Oops: 0002 [#1] SMP
[ 13.340757] Modules linked in:
[ 13.344422] CPU: 0 PID: 4 Comm: kworker/0:0 Not tainted 3.13.0-rc1-gerry+ #7
[ 13.352474] Hardware name: Intel Corporation LH Pass ........../SVRBD-ROW_T, BIOS SE5C600.86B.99.99.x059.091020121352 09/10/2012
[ 13.365659] Workqueue: events work_for_cpu_fn
[ 13.370774] task: ffff88042ddf00d0 ti: ffff88042ddee000 task.ti: ffff88042dde e000
[ 13.379389] RIP: 0010:[<ffffffff81461bac>] [<ffffffff81461bac>] qi_submit_sy nc+0x15c/0x400
[ 13.389055] RSP: 0000:ffff88042ddef940 EFLAGS: 00010002
[ 13.395151] RAX: 00000000000005e0 RBX: 0000000000000082 RCX: 0000000200000025
[ 13.403308] RDX: ffffc9000605a000 RSI: 0000000000000010 RDI: ffff88042ddb8610
[ 13.411446] RBP: ffff88042ddef9a0 R08: 00000000000005d0 R09: 0000000000000001
[ 13.419599] R10: 0000000000000000 R11: 000000000000005d R12: 000000000000005c
[ 13.427742] R13: ffff88102d84d300 R14: 0000000000000174 R15: ffff88042ddb4800
[ 13.435877] FS: 0000000000000000(0000) GS:ffff88043de00000(0000) knlGS:00000 00000000000
[ 13.445168] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 13.451749] CR2: ffffc9000605a088 CR3: 0000000001a0b000 CR4: 00000000000407f0
[ 13.459895] Stack:
[ 13.462297] ffff88042ddb85d0 000000000000005d ffff88042ddef9b0 0000000000000 5d0
[ 13.471147] 00000000000005c0 ffff88042ddb8000 000000000000005c 0000000000000 015
[ 13.480001] ffff88042ddb4800 0000000000000282 ffff88042ddefa40 ffff88042ddef ac0
[ 13.488855] Call Trace:
[ 13.491771] [<ffffffff8146848d>] modify_irte+0x9d/0xd0
[ 13.497778] [<ffffffff8146886d>] intel_setup_ioapic_entry+0x10d/0x290
[ 13.505250] [<ffffffff810a92a6>] ? trace_hardirqs_on_caller+0x16/0x1e0
[ 13.512824] [<ffffffff810346b0>] ? default_init_apic_ldr+0x60/0x60
[ 13.519998] [<ffffffff81468be0>] setup_ioapic_remapped_entry+0x20/0x30
[ 13.527566] [<ffffffff8103683a>] io_apic_setup_irq_pin+0x12a/0x2c0
[ 13.534742] [<ffffffff8136673b>] ? acpi_pci_irq_find_prt_entry+0x2b9/0x2d8
[ 13.544102] [<ffffffff81037fd5>] io_apic_setup_irq_pin_once+0x85/0xa0
[ 13.551568] [<ffffffff8103816f>] ? mp_find_ioapic_pin+0x8f/0xf0
[ 13.558434] [<ffffffff81038044>] io_apic_set_pci_routing+0x34/0x70
[ 13.565621] [<ffffffff8102f4cf>] mp_register_gsi+0xaf/0x1c0
[ 13.572111] [<ffffffff8102f5ee>] acpi_register_gsi_ioapic+0xe/0x10
[ 13.579286] [<ffffffff8102f33f>] acpi_register_gsi+0xf/0x20
[ 13.585779] [<ffffffff81366b86>] acpi_pci_irq_enable+0x171/0x1e3
[ 13.592764] [<ffffffff8146d771>] pcibios_enable_device+0x31/0x40
[ 13.599744] [<ffffffff81320e9b>] do_pci_enable_device+0x3b/0x60
[ 13.606633] [<ffffffff81322248>] pci_enable_device_flags+0xc8/0x120
[ 13.613887] [<ffffffff813222f3>] pci_enable_device+0x13/0x20
[ 13.620484] [<ffffffff8132fa7e>] pcie_port_device_register+0x1e/0x510
[ 13.627947] [<ffffffff810a92a6>] ? trace_hardirqs_on_caller+0x16/0x1e0
[ 13.635510] [<ffffffff810a947d>] ? trace_hardirqs_on+0xd/0x10
[ 13.642189] [<ffffffff813302b8>] pcie_portdrv_probe+0x58/0xc0
[ 13.648877] [<ffffffff81323ba5>] local_pci_probe+0x45/0xa0
[ 13.655266] [<ffffffff8106bc44>] work_for_cpu_fn+0x14/0x20
[ 13.661656] [<ffffffff8106fa79>] process_one_work+0x369/0x710
[ 13.668334] [<ffffffff8106fa02>] ? process_one_work+0x2f2/0x710
[ 13.675215] [<ffffffff81071d56>] ? worker_thread+0x46/0x690
[ 13.681714] [<ffffffff81072194>] worker_thread+0x484/0x690
[ 13.688109] [<ffffffff81071d10>] ? cancel_delayed_work_sync+0x20/0x20
[ 13.695576] [<ffffffff81079c60>] kthread+0xf0/0x110
[ 13.701300] [<ffffffff8108e7bf>] ? local_clock+0x3f/0x50
[ 13.707492] [<ffffffff81079b70>] ? kthread_create_on_node+0x250/0x250
[ 13.714959] [<ffffffff81574d2c>] ret_from_fork+0x7c/0xb0
[ 13.721152] [<ffffffff81079b70>] ? kthread_create_on_node+0x250/0x250
Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
Signed-off-by: Joerg Roedel <joro@8bytes.org>
Diffstat (limited to 'drivers/iommu')
-rw-r--r-- | drivers/iommu/dmar.c | 56 | ||||
-rw-r--r-- | drivers/iommu/intel-iommu.c | 13 |
2 files changed, 43 insertions, 26 deletions
diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index ee4cb1906e45..b0df78f9cd28 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c | |||
@@ -53,6 +53,7 @@ struct acpi_table_header * __initdata dmar_tbl; | |||
53 | static acpi_size dmar_tbl_size; | 53 | static acpi_size dmar_tbl_size; |
54 | 54 | ||
55 | static int alloc_iommu(struct dmar_drhd_unit *drhd); | 55 | static int alloc_iommu(struct dmar_drhd_unit *drhd); |
56 | static void free_iommu(struct intel_iommu *iommu); | ||
56 | 57 | ||
57 | static void __init dmar_register_drhd_unit(struct dmar_drhd_unit *drhd) | 58 | static void __init dmar_register_drhd_unit(struct dmar_drhd_unit *drhd) |
58 | { | 59 | { |
@@ -205,25 +206,28 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header) | |||
205 | return 0; | 206 | return 0; |
206 | } | 207 | } |
207 | 208 | ||
209 | static void dmar_free_drhd(struct dmar_drhd_unit *dmaru) | ||
210 | { | ||
211 | if (dmaru->devices && dmaru->devices_cnt) | ||
212 | dmar_free_dev_scope(&dmaru->devices, &dmaru->devices_cnt); | ||
213 | if (dmaru->iommu) | ||
214 | free_iommu(dmaru->iommu); | ||
215 | kfree(dmaru); | ||
216 | } | ||
217 | |||
208 | static int __init dmar_parse_dev(struct dmar_drhd_unit *dmaru) | 218 | static int __init dmar_parse_dev(struct dmar_drhd_unit *dmaru) |
209 | { | 219 | { |
210 | struct acpi_dmar_hardware_unit *drhd; | 220 | struct acpi_dmar_hardware_unit *drhd; |
211 | int ret = 0; | ||
212 | 221 | ||
213 | drhd = (struct acpi_dmar_hardware_unit *) dmaru->hdr; | 222 | drhd = (struct acpi_dmar_hardware_unit *) dmaru->hdr; |
214 | 223 | ||
215 | if (dmaru->include_all) | 224 | if (dmaru->include_all) |
216 | return 0; | 225 | return 0; |
217 | 226 | ||
218 | ret = dmar_parse_dev_scope((void *)(drhd + 1), | 227 | return dmar_parse_dev_scope((void *)(drhd + 1), |
219 | ((void *)drhd) + drhd->header.length, | 228 | ((void *)drhd) + drhd->header.length, |
220 | &dmaru->devices_cnt, &dmaru->devices, | 229 | &dmaru->devices_cnt, &dmaru->devices, |
221 | drhd->segment); | 230 | drhd->segment); |
222 | if (ret) { | ||
223 | list_del(&dmaru->list); | ||
224 | kfree(dmaru); | ||
225 | } | ||
226 | return ret; | ||
227 | } | 231 | } |
228 | 232 | ||
229 | #ifdef CONFIG_ACPI_NUMA | 233 | #ifdef CONFIG_ACPI_NUMA |
@@ -435,7 +439,7 @@ dmar_find_matched_drhd_unit(struct pci_dev *dev) | |||
435 | int __init dmar_dev_scope_init(void) | 439 | int __init dmar_dev_scope_init(void) |
436 | { | 440 | { |
437 | static int dmar_dev_scope_initialized; | 441 | static int dmar_dev_scope_initialized; |
438 | struct dmar_drhd_unit *drhd, *drhd_n; | 442 | struct dmar_drhd_unit *drhd; |
439 | int ret = -ENODEV; | 443 | int ret = -ENODEV; |
440 | 444 | ||
441 | if (dmar_dev_scope_initialized) | 445 | if (dmar_dev_scope_initialized) |
@@ -444,7 +448,7 @@ int __init dmar_dev_scope_init(void) | |||
444 | if (list_empty(&dmar_drhd_units)) | 448 | if (list_empty(&dmar_drhd_units)) |
445 | goto fail; | 449 | goto fail; |
446 | 450 | ||
447 | list_for_each_entry_safe(drhd, drhd_n, &dmar_drhd_units, list) { | 451 | list_for_each_entry(drhd, &dmar_drhd_units, list) { |
448 | ret = dmar_parse_dev(drhd); | 452 | ret = dmar_parse_dev(drhd); |
449 | if (ret) | 453 | if (ret) |
450 | goto fail; | 454 | goto fail; |
@@ -725,12 +729,13 @@ static int alloc_iommu(struct dmar_drhd_unit *drhd) | |||
725 | return err; | 729 | return err; |
726 | } | 730 | } |
727 | 731 | ||
728 | void free_iommu(struct intel_iommu *iommu) | 732 | static void free_iommu(struct intel_iommu *iommu) |
729 | { | 733 | { |
730 | if (!iommu) | 734 | if (iommu->irq) { |
731 | return; | 735 | free_irq(iommu->irq, iommu); |
732 | 736 | irq_set_handler_data(iommu->irq, NULL); | |
733 | free_dmar_iommu(iommu); | 737 | destroy_irq(iommu->irq); |
738 | } | ||
734 | 739 | ||
735 | if (iommu->reg) | 740 | if (iommu->reg) |
736 | unmap_iommu(iommu); | 741 | unmap_iommu(iommu); |
@@ -1368,4 +1373,21 @@ int __init dmar_ir_support(void) | |||
1368 | return dmar->flags & 0x1; | 1373 | return dmar->flags & 0x1; |
1369 | } | 1374 | } |
1370 | 1375 | ||
1376 | static int __init dmar_free_unused_resources(void) | ||
1377 | { | ||
1378 | struct dmar_drhd_unit *dmaru, *dmaru_n; | ||
1379 | |||
1380 | /* DMAR units are in use */ | ||
1381 | if (irq_remapping_enabled || intel_iommu_enabled) | ||
1382 | return 0; | ||
1383 | |||
1384 | list_for_each_entry_safe(dmaru, dmaru_n, &dmar_drhd_units, list) { | ||
1385 | list_del(&dmaru->list); | ||
1386 | dmar_free_drhd(dmaru); | ||
1387 | } | ||
1388 | |||
1389 | return 0; | ||
1390 | } | ||
1391 | |||
1392 | late_initcall(dmar_free_unused_resources); | ||
1371 | IOMMU_INIT_POST(detect_intel_iommu); | 1393 | IOMMU_INIT_POST(detect_intel_iommu); |
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 7a0984d1c8d5..fd9e369a8cf7 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c | |||
@@ -1265,7 +1265,7 @@ static int iommu_init_domains(struct intel_iommu *iommu) | |||
1265 | static void domain_exit(struct dmar_domain *domain); | 1265 | static void domain_exit(struct dmar_domain *domain); |
1266 | static void vm_domain_exit(struct dmar_domain *domain); | 1266 | static void vm_domain_exit(struct dmar_domain *domain); |
1267 | 1267 | ||
1268 | void free_dmar_iommu(struct intel_iommu *iommu) | 1268 | static void free_dmar_iommu(struct intel_iommu *iommu) |
1269 | { | 1269 | { |
1270 | struct dmar_domain *domain; | 1270 | struct dmar_domain *domain; |
1271 | int i; | 1271 | int i; |
@@ -1290,15 +1290,10 @@ void free_dmar_iommu(struct intel_iommu *iommu) | |||
1290 | if (iommu->gcmd & DMA_GCMD_TE) | 1290 | if (iommu->gcmd & DMA_GCMD_TE) |
1291 | iommu_disable_translation(iommu); | 1291 | iommu_disable_translation(iommu); |
1292 | 1292 | ||
1293 | if (iommu->irq) { | ||
1294 | /* This will mask the irq */ | ||
1295 | free_irq(iommu->irq, iommu); | ||
1296 | irq_set_handler_data(iommu->irq, NULL); | ||
1297 | destroy_irq(iommu->irq); | ||
1298 | } | ||
1299 | |||
1300 | kfree(iommu->domains); | 1293 | kfree(iommu->domains); |
1301 | kfree(iommu->domain_ids); | 1294 | kfree(iommu->domain_ids); |
1295 | iommu->domains = NULL; | ||
1296 | iommu->domain_ids = NULL; | ||
1302 | 1297 | ||
1303 | g_iommus[iommu->seq_id] = NULL; | 1298 | g_iommus[iommu->seq_id] = NULL; |
1304 | 1299 | ||
@@ -2627,7 +2622,7 @@ static int __init init_dmars(void) | |||
2627 | return 0; | 2622 | return 0; |
2628 | error: | 2623 | error: |
2629 | for_each_active_iommu(iommu, drhd) | 2624 | for_each_active_iommu(iommu, drhd) |
2630 | free_iommu(iommu); | 2625 | free_dmar_iommu(iommu); |
2631 | kfree(g_iommus); | 2626 | kfree(g_iommus); |
2632 | return ret; | 2627 | return ret; |
2633 | } | 2628 | } |