diff options
author | David Woodhouse <David.Woodhouse@intel.com> | 2009-04-03 20:45:37 -0400 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2009-04-04 05:43:31 -0400 |
commit | 276dbf997043cbf38f0087624e0f9c51742c8885 (patch) | |
tree | eface2519a6ad4c25c2864ee1ee69361ea3f594c /drivers/pci | |
parent | 924b6231edfaf1e764ffb4f97ea382bf4facff58 (diff) |
intel-iommu: Handle PCI domains appropriately.
We were comparing {bus,devfn} and assuming that a match meant it was the
same device. It doesn't -- the same {bus,devfn} can exist in
multiple PCI domains. Include domain number in device identification
(and call it 'segment' in most places, because there's already a lot of
references to 'domain' which means something else, and this code is
infected with ACPI thinking already).
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/dmar.c | 1 | ||||
-rw-r--r-- | drivers/pci/intel-iommu.c | 76 |
2 files changed, 49 insertions, 28 deletions
diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 3fbe6af7ad7..25a00ce4f24 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c | |||
@@ -180,6 +180,7 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header) | |||
180 | dmaru->hdr = header; | 180 | dmaru->hdr = header; |
181 | drhd = (struct acpi_dmar_hardware_unit *)header; | 181 | drhd = (struct acpi_dmar_hardware_unit *)header; |
182 | dmaru->reg_base_addr = drhd->address; | 182 | dmaru->reg_base_addr = drhd->address; |
183 | dmaru->segment = drhd->segment; | ||
183 | dmaru->include_all = drhd->flags & 0x1; /* BIT0: INCLUDE_ALL */ | 184 | dmaru->include_all = drhd->flags & 0x1; /* BIT0: INCLUDE_ALL */ |
184 | 185 | ||
185 | ret = alloc_iommu(dmaru); | 186 | ret = alloc_iommu(dmaru); |
diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 6262c198e56..fd7472f2867 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c | |||
@@ -248,7 +248,8 @@ struct dmar_domain { | |||
248 | struct device_domain_info { | 248 | struct device_domain_info { |
249 | struct list_head link; /* link to domain siblings */ | 249 | struct list_head link; /* link to domain siblings */ |
250 | struct list_head global; /* link to global list */ | 250 | struct list_head global; /* link to global list */ |
251 | u8 bus; /* PCI bus numer */ | 251 | int segment; /* PCI domain */ |
252 | u8 bus; /* PCI bus number */ | ||
252 | u8 devfn; /* PCI devfn number */ | 253 | u8 devfn; /* PCI devfn number */ |
253 | struct pci_dev *dev; /* it's NULL for PCIE-to-PCI bridge */ | 254 | struct pci_dev *dev; /* it's NULL for PCIE-to-PCI bridge */ |
254 | struct dmar_domain *domain; /* pointer to domain */ | 255 | struct dmar_domain *domain; /* pointer to domain */ |
@@ -468,7 +469,7 @@ static void domain_update_iommu_cap(struct dmar_domain *domain) | |||
468 | domain_update_iommu_snooping(domain); | 469 | domain_update_iommu_snooping(domain); |
469 | } | 470 | } |
470 | 471 | ||
471 | static struct intel_iommu *device_to_iommu(u8 bus, u8 devfn) | 472 | static struct intel_iommu *device_to_iommu(int segment, u8 bus, u8 devfn) |
472 | { | 473 | { |
473 | struct dmar_drhd_unit *drhd = NULL; | 474 | struct dmar_drhd_unit *drhd = NULL; |
474 | int i; | 475 | int i; |
@@ -476,6 +477,8 @@ static struct intel_iommu *device_to_iommu(u8 bus, u8 devfn) | |||
476 | for_each_drhd_unit(drhd) { | 477 | for_each_drhd_unit(drhd) { |
477 | if (drhd->ignored) | 478 | if (drhd->ignored) |
478 | continue; | 479 | continue; |
480 | if (segment != drhd->segment) | ||
481 | continue; | ||
479 | 482 | ||
480 | for (i = 0; i < drhd->devices_cnt; i++) { | 483 | for (i = 0; i < drhd->devices_cnt; i++) { |
481 | if (drhd->devices[i] && | 484 | if (drhd->devices[i] && |
@@ -1318,7 +1321,7 @@ static void domain_exit(struct dmar_domain *domain) | |||
1318 | } | 1321 | } |
1319 | 1322 | ||
1320 | static int domain_context_mapping_one(struct dmar_domain *domain, | 1323 | static int domain_context_mapping_one(struct dmar_domain *domain, |
1321 | u8 bus, u8 devfn) | 1324 | int segment, u8 bus, u8 devfn) |
1322 | { | 1325 | { |
1323 | struct context_entry *context; | 1326 | struct context_entry *context; |
1324 | unsigned long flags; | 1327 | unsigned long flags; |
@@ -1333,7 +1336,7 @@ static int domain_context_mapping_one(struct dmar_domain *domain, | |||
1333 | bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); | 1336 | bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); |
1334 | BUG_ON(!domain->pgd); | 1337 | BUG_ON(!domain->pgd); |
1335 | 1338 | ||
1336 | iommu = device_to_iommu(bus, devfn); | 1339 | iommu = device_to_iommu(segment, bus, devfn); |
1337 | if (!iommu) | 1340 | if (!iommu) |
1338 | return -ENODEV; | 1341 | return -ENODEV; |
1339 | 1342 | ||
@@ -1423,8 +1426,8 @@ domain_context_mapping(struct dmar_domain *domain, struct pci_dev *pdev) | |||
1423 | int ret; | 1426 | int ret; |
1424 | struct pci_dev *tmp, *parent; | 1427 | struct pci_dev *tmp, *parent; |
1425 | 1428 | ||
1426 | ret = domain_context_mapping_one(domain, pdev->bus->number, | 1429 | ret = domain_context_mapping_one(domain, pci_domain_nr(pdev->bus), |
1427 | pdev->devfn); | 1430 | pdev->bus->number, pdev->devfn); |
1428 | if (ret) | 1431 | if (ret) |
1429 | return ret; | 1432 | return ret; |
1430 | 1433 | ||
@@ -1435,18 +1438,23 @@ domain_context_mapping(struct dmar_domain *domain, struct pci_dev *pdev) | |||
1435 | /* Secondary interface's bus number and devfn 0 */ | 1438 | /* Secondary interface's bus number and devfn 0 */ |
1436 | parent = pdev->bus->self; | 1439 | parent = pdev->bus->self; |
1437 | while (parent != tmp) { | 1440 | while (parent != tmp) { |
1438 | ret = domain_context_mapping_one(domain, parent->bus->number, | 1441 | ret = domain_context_mapping_one(domain, |
1439 | parent->devfn); | 1442 | pci_domain_nr(parent->bus), |
1443 | parent->bus->number, | ||
1444 | parent->devfn); | ||
1440 | if (ret) | 1445 | if (ret) |
1441 | return ret; | 1446 | return ret; |
1442 | parent = parent->bus->self; | 1447 | parent = parent->bus->self; |
1443 | } | 1448 | } |
1444 | if (tmp->is_pcie) /* this is a PCIE-to-PCI bridge */ | 1449 | if (tmp->is_pcie) /* this is a PCIE-to-PCI bridge */ |
1445 | return domain_context_mapping_one(domain, | 1450 | return domain_context_mapping_one(domain, |
1446 | tmp->subordinate->number, 0); | 1451 | pci_domain_nr(tmp->subordinate), |
1452 | tmp->subordinate->number, 0); | ||
1447 | else /* this is a legacy PCI bridge */ | 1453 | else /* this is a legacy PCI bridge */ |
1448 | return domain_context_mapping_one(domain, | 1454 | return domain_context_mapping_one(domain, |
1449 | tmp->bus->number, tmp->devfn); | 1455 | pci_domain_nr(tmp->bus), |
1456 | tmp->bus->number, | ||
1457 | tmp->devfn); | ||
1450 | } | 1458 | } |
1451 | 1459 | ||
1452 | static int domain_context_mapped(struct pci_dev *pdev) | 1460 | static int domain_context_mapped(struct pci_dev *pdev) |
@@ -1455,12 +1463,12 @@ static int domain_context_mapped(struct pci_dev *pdev) | |||
1455 | struct pci_dev *tmp, *parent; | 1463 | struct pci_dev *tmp, *parent; |
1456 | struct intel_iommu *iommu; | 1464 | struct intel_iommu *iommu; |
1457 | 1465 | ||
1458 | iommu = device_to_iommu(pdev->bus->number, pdev->devfn); | 1466 | iommu = device_to_iommu(pci_domain_nr(pdev->bus), pdev->bus->number, |
1467 | pdev->devfn); | ||
1459 | if (!iommu) | 1468 | if (!iommu) |
1460 | return -ENODEV; | 1469 | return -ENODEV; |
1461 | 1470 | ||
1462 | ret = device_context_mapped(iommu, | 1471 | ret = device_context_mapped(iommu, pdev->bus->number, pdev->devfn); |
1463 | pdev->bus->number, pdev->devfn); | ||
1464 | if (!ret) | 1472 | if (!ret) |
1465 | return ret; | 1473 | return ret; |
1466 | /* dependent device mapping */ | 1474 | /* dependent device mapping */ |
@@ -1471,17 +1479,17 @@ static int domain_context_mapped(struct pci_dev *pdev) | |||
1471 | parent = pdev->bus->self; | 1479 | parent = pdev->bus->self; |
1472 | while (parent != tmp) { | 1480 | while (parent != tmp) { |
1473 | ret = device_context_mapped(iommu, parent->bus->number, | 1481 | ret = device_context_mapped(iommu, parent->bus->number, |
1474 | parent->devfn); | 1482 | parent->devfn); |
1475 | if (!ret) | 1483 | if (!ret) |
1476 | return ret; | 1484 | return ret; |
1477 | parent = parent->bus->self; | 1485 | parent = parent->bus->self; |
1478 | } | 1486 | } |
1479 | if (tmp->is_pcie) | 1487 | if (tmp->is_pcie) |
1480 | return device_context_mapped(iommu, | 1488 | return device_context_mapped(iommu, tmp->subordinate->number, |
1481 | tmp->subordinate->number, 0); | 1489 | 0); |
1482 | else | 1490 | else |
1483 | return device_context_mapped(iommu, | 1491 | return device_context_mapped(iommu, tmp->bus->number, |
1484 | tmp->bus->number, tmp->devfn); | 1492 | tmp->devfn); |
1485 | } | 1493 | } |
1486 | 1494 | ||
1487 | static int | 1495 | static int |
@@ -1548,7 +1556,7 @@ static void domain_remove_dev_info(struct dmar_domain *domain) | |||
1548 | info->dev->dev.archdata.iommu = NULL; | 1556 | info->dev->dev.archdata.iommu = NULL; |
1549 | spin_unlock_irqrestore(&device_domain_lock, flags); | 1557 | spin_unlock_irqrestore(&device_domain_lock, flags); |
1550 | 1558 | ||
1551 | iommu = device_to_iommu(info->bus, info->devfn); | 1559 | iommu = device_to_iommu(info->segment, info->bus, info->devfn); |
1552 | iommu_detach_dev(iommu, info->bus, info->devfn); | 1560 | iommu_detach_dev(iommu, info->bus, info->devfn); |
1553 | free_devinfo_mem(info); | 1561 | free_devinfo_mem(info); |
1554 | 1562 | ||
@@ -1583,11 +1591,14 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw) | |||
1583 | struct pci_dev *dev_tmp; | 1591 | struct pci_dev *dev_tmp; |
1584 | unsigned long flags; | 1592 | unsigned long flags; |
1585 | int bus = 0, devfn = 0; | 1593 | int bus = 0, devfn = 0; |
1594 | int segment; | ||
1586 | 1595 | ||
1587 | domain = find_domain(pdev); | 1596 | domain = find_domain(pdev); |
1588 | if (domain) | 1597 | if (domain) |
1589 | return domain; | 1598 | return domain; |
1590 | 1599 | ||
1600 | segment = pci_domain_nr(pdev->bus); | ||
1601 | |||
1591 | dev_tmp = pci_find_upstream_pcie_bridge(pdev); | 1602 | dev_tmp = pci_find_upstream_pcie_bridge(pdev); |
1592 | if (dev_tmp) { | 1603 | if (dev_tmp) { |
1593 | if (dev_tmp->is_pcie) { | 1604 | if (dev_tmp->is_pcie) { |
@@ -1599,7 +1610,8 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw) | |||
1599 | } | 1610 | } |
1600 | spin_lock_irqsave(&device_domain_lock, flags); | 1611 | spin_lock_irqsave(&device_domain_lock, flags); |
1601 | list_for_each_entry(info, &device_domain_list, global) { | 1612 | list_for_each_entry(info, &device_domain_list, global) { |
1602 | if (info->bus == bus && info->devfn == devfn) { | 1613 | if (info->segment == segment && |
1614 | info->bus == bus && info->devfn == devfn) { | ||
1603 | found = info->domain; | 1615 | found = info->domain; |
1604 | break; | 1616 | break; |
1605 | } | 1617 | } |
@@ -1637,6 +1649,7 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw) | |||
1637 | domain_exit(domain); | 1649 | domain_exit(domain); |
1638 | goto error; | 1650 | goto error; |
1639 | } | 1651 | } |
1652 | info->segment = segment; | ||
1640 | info->bus = bus; | 1653 | info->bus = bus; |
1641 | info->devfn = devfn; | 1654 | info->devfn = devfn; |
1642 | info->dev = NULL; | 1655 | info->dev = NULL; |
@@ -1648,7 +1661,8 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw) | |||
1648 | found = NULL; | 1661 | found = NULL; |
1649 | spin_lock_irqsave(&device_domain_lock, flags); | 1662 | spin_lock_irqsave(&device_domain_lock, flags); |
1650 | list_for_each_entry(tmp, &device_domain_list, global) { | 1663 | list_for_each_entry(tmp, &device_domain_list, global) { |
1651 | if (tmp->bus == bus && tmp->devfn == devfn) { | 1664 | if (tmp->segment == segment && |
1665 | tmp->bus == bus && tmp->devfn == devfn) { | ||
1652 | found = tmp->domain; | 1666 | found = tmp->domain; |
1653 | break; | 1667 | break; |
1654 | } | 1668 | } |
@@ -1668,6 +1682,7 @@ found_domain: | |||
1668 | info = alloc_devinfo_mem(); | 1682 | info = alloc_devinfo_mem(); |
1669 | if (!info) | 1683 | if (!info) |
1670 | goto error; | 1684 | goto error; |
1685 | info->segment = segment; | ||
1671 | info->bus = pdev->bus->number; | 1686 | info->bus = pdev->bus->number; |
1672 | info->devfn = pdev->devfn; | 1687 | info->devfn = pdev->devfn; |
1673 | info->dev = pdev; | 1688 | info->dev = pdev; |
@@ -2808,6 +2823,7 @@ static int vm_domain_add_dev_info(struct dmar_domain *domain, | |||
2808 | if (!info) | 2823 | if (!info) |
2809 | return -ENOMEM; | 2824 | return -ENOMEM; |
2810 | 2825 | ||
2826 | info->segment = pci_domain_nr(pdev->bus); | ||
2811 | info->bus = pdev->bus->number; | 2827 | info->bus = pdev->bus->number; |
2812 | info->devfn = pdev->devfn; | 2828 | info->devfn = pdev->devfn; |
2813 | info->dev = pdev; | 2829 | info->dev = pdev; |
@@ -2837,15 +2853,15 @@ static void iommu_detach_dependent_devices(struct intel_iommu *iommu, | |||
2837 | parent = pdev->bus->self; | 2853 | parent = pdev->bus->self; |
2838 | while (parent != tmp) { | 2854 | while (parent != tmp) { |
2839 | iommu_detach_dev(iommu, parent->bus->number, | 2855 | iommu_detach_dev(iommu, parent->bus->number, |
2840 | parent->devfn); | 2856 | parent->devfn); |
2841 | parent = parent->bus->self; | 2857 | parent = parent->bus->self; |
2842 | } | 2858 | } |
2843 | if (tmp->is_pcie) /* this is a PCIE-to-PCI bridge */ | 2859 | if (tmp->is_pcie) /* this is a PCIE-to-PCI bridge */ |
2844 | iommu_detach_dev(iommu, | 2860 | iommu_detach_dev(iommu, |
2845 | tmp->subordinate->number, 0); | 2861 | tmp->subordinate->number, 0); |
2846 | else /* this is a legacy PCI bridge */ | 2862 | else /* this is a legacy PCI bridge */ |
2847 | iommu_detach_dev(iommu, | 2863 | iommu_detach_dev(iommu, tmp->bus->number, |
2848 | tmp->bus->number, tmp->devfn); | 2864 | tmp->devfn); |
2849 | } | 2865 | } |
2850 | } | 2866 | } |
2851 | 2867 | ||
@@ -2858,13 +2874,15 @@ static void vm_domain_remove_one_dev_info(struct dmar_domain *domain, | |||
2858 | int found = 0; | 2874 | int found = 0; |
2859 | struct list_head *entry, *tmp; | 2875 | struct list_head *entry, *tmp; |
2860 | 2876 | ||
2861 | iommu = device_to_iommu(pdev->bus->number, pdev->devfn); | 2877 | iommu = device_to_iommu(pci_domain_nr(pdev->bus), pdev->bus->number, |
2878 | pdev->devfn); | ||
2862 | if (!iommu) | 2879 | if (!iommu) |
2863 | return; | 2880 | return; |
2864 | 2881 | ||
2865 | spin_lock_irqsave(&device_domain_lock, flags); | 2882 | spin_lock_irqsave(&device_domain_lock, flags); |
2866 | list_for_each_safe(entry, tmp, &domain->devices) { | 2883 | list_for_each_safe(entry, tmp, &domain->devices) { |
2867 | info = list_entry(entry, struct device_domain_info, link); | 2884 | info = list_entry(entry, struct device_domain_info, link); |
2885 | /* No need to compare PCI domain; it has to be the same */ | ||
2868 | if (info->bus == pdev->bus->number && | 2886 | if (info->bus == pdev->bus->number && |
2869 | info->devfn == pdev->devfn) { | 2887 | info->devfn == pdev->devfn) { |
2870 | list_del(&info->link); | 2888 | list_del(&info->link); |
@@ -2889,7 +2907,8 @@ static void vm_domain_remove_one_dev_info(struct dmar_domain *domain, | |||
2889 | * owned by this domain, clear this iommu in iommu_bmp | 2907 | * owned by this domain, clear this iommu in iommu_bmp |
2890 | * update iommu count and coherency | 2908 | * update iommu count and coherency |
2891 | */ | 2909 | */ |
2892 | if (device_to_iommu(info->bus, info->devfn) == iommu) | 2910 | if (iommu == device_to_iommu(info->segment, info->bus, |
2911 | info->devfn)) | ||
2893 | found = 1; | 2912 | found = 1; |
2894 | } | 2913 | } |
2895 | 2914 | ||
@@ -2922,7 +2941,7 @@ static void vm_domain_remove_all_dev_info(struct dmar_domain *domain) | |||
2922 | 2941 | ||
2923 | spin_unlock_irqrestore(&device_domain_lock, flags1); | 2942 | spin_unlock_irqrestore(&device_domain_lock, flags1); |
2924 | 2943 | ||
2925 | iommu = device_to_iommu(info->bus, info->devfn); | 2944 | iommu = device_to_iommu(info->segment, info->bus, info->devfn); |
2926 | iommu_detach_dev(iommu, info->bus, info->devfn); | 2945 | iommu_detach_dev(iommu, info->bus, info->devfn); |
2927 | iommu_detach_dependent_devices(iommu, info->dev); | 2946 | iommu_detach_dependent_devices(iommu, info->dev); |
2928 | 2947 | ||
@@ -3110,7 +3129,8 @@ static int intel_iommu_attach_device(struct iommu_domain *domain, | |||
3110 | } | 3129 | } |
3111 | } | 3130 | } |
3112 | 3131 | ||
3113 | iommu = device_to_iommu(pdev->bus->number, pdev->devfn); | 3132 | iommu = device_to_iommu(pci_domain_nr(pdev->bus), pdev->bus->number, |
3133 | pdev->devfn); | ||
3114 | if (!iommu) | 3134 | if (!iommu) |
3115 | return -ENODEV; | 3135 | return -ENODEV; |
3116 | 3136 | ||