Re: [PATCH v11 20/27] iommu/exynos: allow having multiple System MMUs for a master H/W
On Tue, 22 Apr 2014 18:53:51 +0530, Shaik Ameer Basha wrote: > Hi KyongHo Cho, > > > > On Fri, Mar 14, 2014 at 10:40 AM, Cho KyongHo wrote: > > Some master device descriptor like fimc-is which is an abstraction > > of very complex H/W may have multiple System MMUs. For those devices, > > the design of the link between System MMU and its master H/W is needed > > to be reconsidered. > > > > A link structure, sysmmu_list_data is introduced that provides a link > > to master H/W and that has a pointer to the device descriptor of a > > System MMU. Given a device descriptor of a master H/W, it is possible > > to traverse all System MMUs that must be controlled along with the > > master H/W. > > > > Signed-off-by: Cho KyongHo > > --- > > drivers/iommu/exynos-iommu.c | 534 > > ++ > > 1 file changed, 333 insertions(+), 201 deletions(-) > > > > diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c > > index 84ba29a..7489343 100644 > > --- a/drivers/iommu/exynos-iommu.c > > +++ b/drivers/iommu/exynos-iommu.c > > @@ -128,6 +128,10 @@ > > #define __master_clk_disable(data) __clk_gate_ctrl(data, clk_master, > > dis) > > > > [snip] > > > +static int __init __sysmmu_init_master(struct device *dev) > > +{ > > + int ret; > > + int i = 0; > > + struct device_node *node; > > + > > + while ((node = of_parse_phandle(dev->of_node, "mmu-masters", i++))) > > { > > struct platform_device *master = > > of_find_device_by_node(node); > > + struct exynos_iommu_owner *owner; > > + struct sysmmu_list_data *list_data; > > > > if (!master) { > > dev_err(dev, "%s: mmu-master '%s' not found\n", > > __func__, node->name); > > - return -EINVAL; > > + ret = -EINVAL; > > + goto err; > > } > > > > - if (master->dev.archdata.iommu != NULL) { > > - dev_err(dev, "%s: '%s' is master of other MMU\n", > > - __func__, node->name); > > - return -EINVAL; > > + owner = master->dev.archdata.iommu; > > + if (!owner) { > > + owner = devm_kzalloc(dev, sizeof(*owner), > > GFP_KERNEL); > > + if (!owner) { > > + dev_err(dev, > > + "%s: Failed to allocate owner structure\n", > > + __func__); > > + ret = -ENOMEM; > > + goto err; > > + } > > + > > + INIT_LIST_HEAD(&owner->mmu_list); > > + INIT_LIST_HEAD(&owner->client); > > + owner->dev = &master->dev; > > + spin_lock_init(&owner->lock); > > + > > + master->dev.archdata.iommu = owner; > > } > > > > + list_data = devm_kzalloc(dev, sizeof(*list_data), > > GFP_KERNEL); > > + if (!list_data) { > > + dev_err(dev, > > + "%s: Failed to allocate sysmmu_list_data\n", > > + __func__); > > + ret = -ENOMEM; > > + goto err; > > + } > > + > > + INIT_LIST_HEAD(&list_data->entry); > > + list_data->sysmmu = dev; > > + > > /* > > -* archdata.iommu will be initialized with > > exynos_iommu_client > > -* in sysmmu_hook_driver_register(). > > +* System MMUs are attached in the order of the presence > > +* in device tree > > */ > > - master->dev.archdata.iommu = dev; > > + list_add_tail(&list_data->entry, &owner->mmu_list); > > } > > > > - data->sysmmu = dev; > > - rwlock_init(&data->lock); > > + return 0; > > +err: > > + while ((node = of_parse_phandle(dev->of_node, "mmu-masters", i++))) > > { > > Don't we need to reinitialize variable 'i' here before using? > i = 0; > Oh. You are right. Thanks. > > > > > + struct platform_device *master = > > of_find_device_by_node(node); > > + struct exynos_iommu_owner *owner; > > + struct sysmmu_list_data *list_data; > > > > - platform_set_drvdata(pdev, data); > > + if (!master) > > + continue; > > > > - pm_runtime_enable(dev); > > - data->runtime_active = !pm_runtime_enabled(dev); > > + owner = master->dev.archdata.iommu; > > + if (!owner) > > + continue; > > > > - dev_dbg(dev, "Probed and initialized\n"); > > - return 0; > > +
[PATCH] iommu/amd: Fix interrupt remapping for aliased devices
An apparent cut and paste error prevents the correct flags from being set on the alias device resulting in MSI on conventional PCI devices failing to work. This also produces error events from the IOMMU like: AMD-Vi: Event logged [INVALID_DEVICE_REQUEST device=00:14.4 address=0x00fdf800 flags=0x0a00] Where 14.4 is a PCIe-to-PCI bridge with a device behind it trying to use MSI interrupts. Signed-off-by: Alex Williamson Cc: sta...@vger.kernel.org --- drivers/iommu/amd_iommu.c |2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 5113dce..6c0cca5 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -4004,7 +4004,7 @@ static struct irq_remap_table *get_irq_table(u16 devid, bool ioapic) iommu_flush_dte(iommu, devid); if (devid != alias) { irq_lookup_table[alias] = table; - set_dte_irq_entry(devid, table); + set_dte_irq_entry(alias, table); iommu_flush_dte(iommu, alias); } ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [Patch Part3 V1 17/22] pci, ACPI, iommu: enhance pci_root to support DMAR device hotplug
On Tuesday, April 22, 2014 03:07:28 PM Jiang Liu wrote: > Finally enhance pci_root driver to support DMAR device hotplug when > hot-plugging PCI host bridges. > > Signed-off-by: Jiang Liu > --- > drivers/acpi/pci_root.c | 16 ++-- > 1 file changed, 14 insertions(+), 2 deletions(-) > > diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c > index d388f13d48b4..aa8f549869f3 100644 > --- a/drivers/acpi/pci_root.c > +++ b/drivers/acpi/pci_root.c > @@ -33,6 +33,7 @@ > #include > #include > #include > +#include > #include > #include > #include/* for acpi_hest_init() */ > @@ -511,6 +512,7 @@ static int acpi_pci_root_add(struct acpi_device *device, > struct acpi_pci_root *root; > acpi_handle handle = device->handle; > int no_aspm = 0, clear_aspm = 0; > + bool hotadd = (system_state != SYSTEM_BOOTING); The parens are not necessary in the above instruction. > root = kzalloc(sizeof(struct acpi_pci_root), GFP_KERNEL); > if (!root) > @@ -557,6 +559,11 @@ static int acpi_pci_root_add(struct acpi_device *device, > strcpy(acpi_device_class(device), ACPI_PCI_ROOT_CLASS); > device->driver_data = root; > > + if (hotadd && dmar_device_hotplug(handle, true)) { > + result = -ENXIO; > + goto end; > + } > + > pr_info(PREFIX "%s [%s] (domain %04x %pR)\n", > acpi_device_name(device), acpi_device_bid(device), > root->segment, &root->secondary); > @@ -583,7 +590,7 @@ static int acpi_pci_root_add(struct acpi_device *device, > root->segment, (unsigned int)root->secondary.start); > device->driver_data = NULL; > result = -ENODEV; > - goto end; > + goto remove_dmar; > } > > if (clear_aspm) { > @@ -597,7 +604,7 @@ static int acpi_pci_root_add(struct acpi_device *device, > if (device->wakeup.flags.run_wake) > device_set_run_wake(root->bus->bridge, true); > > - if (system_state != SYSTEM_BOOTING) { > + if (hotadd) { > pcibios_resource_survey_bus(root->bus); > pci_assign_unassigned_root_bus_resources(root->bus); > } > @@ -607,6 +614,9 @@ static int acpi_pci_root_add(struct acpi_device *device, > pci_unlock_rescan_remove(); > return 1; > > +remove_dmar: > + if (hotadd) > + dmar_device_hotplug(handle, false); I suppose that works if dmar_device_hotplug() returned false before? > end: > kfree(root); > return result; > @@ -625,6 +635,8 @@ static void acpi_pci_root_remove(struct acpi_device > *device) > > pci_remove_root_bus(root->bus); > > + dmar_device_hotplug(device->handle, false); > + > pci_unlock_rescan_remove(); > > kfree(root); > -- I speak only for myself. Rafael J. Wysocki, Intel Open Source Technology Center. ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v11 20/27] iommu/exynos: allow having multiple System MMUs for a master H/W
Hi KyongHo Cho, On Fri, Mar 14, 2014 at 10:40 AM, Cho KyongHo wrote: > Some master device descriptor like fimc-is which is an abstraction > of very complex H/W may have multiple System MMUs. For those devices, > the design of the link between System MMU and its master H/W is needed > to be reconsidered. > > A link structure, sysmmu_list_data is introduced that provides a link > to master H/W and that has a pointer to the device descriptor of a > System MMU. Given a device descriptor of a master H/W, it is possible > to traverse all System MMUs that must be controlled along with the > master H/W. > > Signed-off-by: Cho KyongHo > --- > drivers/iommu/exynos-iommu.c | 534 > ++ > 1 file changed, 333 insertions(+), 201 deletions(-) > > diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c > index 84ba29a..7489343 100644 > --- a/drivers/iommu/exynos-iommu.c > +++ b/drivers/iommu/exynos-iommu.c > @@ -128,6 +128,10 @@ > #define __master_clk_disable(data) __clk_gate_ctrl(data, clk_master, dis) > [snip] > +static int __init __sysmmu_init_master(struct device *dev) > +{ > + int ret; > + int i = 0; > + struct device_node *node; > + > + while ((node = of_parse_phandle(dev->of_node, "mmu-masters", i++))) { > struct platform_device *master = of_find_device_by_node(node); > + struct exynos_iommu_owner *owner; > + struct sysmmu_list_data *list_data; > > if (!master) { > dev_err(dev, "%s: mmu-master '%s' not found\n", > __func__, node->name); > - return -EINVAL; > + ret = -EINVAL; > + goto err; > } > > - if (master->dev.archdata.iommu != NULL) { > - dev_err(dev, "%s: '%s' is master of other MMU\n", > - __func__, node->name); > - return -EINVAL; > + owner = master->dev.archdata.iommu; > + if (!owner) { > + owner = devm_kzalloc(dev, sizeof(*owner), GFP_KERNEL); > + if (!owner) { > + dev_err(dev, > + "%s: Failed to allocate owner structure\n", > + __func__); > + ret = -ENOMEM; > + goto err; > + } > + > + INIT_LIST_HEAD(&owner->mmu_list); > + INIT_LIST_HEAD(&owner->client); > + owner->dev = &master->dev; > + spin_lock_init(&owner->lock); > + > + master->dev.archdata.iommu = owner; > } > > + list_data = devm_kzalloc(dev, sizeof(*list_data), GFP_KERNEL); > + if (!list_data) { > + dev_err(dev, > + "%s: Failed to allocate sysmmu_list_data\n", > + __func__); > + ret = -ENOMEM; > + goto err; > + } > + > + INIT_LIST_HEAD(&list_data->entry); > + list_data->sysmmu = dev; > + > /* > -* archdata.iommu will be initialized with exynos_iommu_client > -* in sysmmu_hook_driver_register(). > +* System MMUs are attached in the order of the presence > +* in device tree > */ > - master->dev.archdata.iommu = dev; > + list_add_tail(&list_data->entry, &owner->mmu_list); > } > > - data->sysmmu = dev; > - rwlock_init(&data->lock); > + return 0; > +err: > + while ((node = of_parse_phandle(dev->of_node, "mmu-masters", i++))) { Don't we need to reinitialize variable 'i' here before using? i = 0; Regards, Shaik Ameer Basha > + struct platform_device *master = of_find_device_by_node(node); > + struct exynos_iommu_owner *owner; > + struct sysmmu_list_data *list_data; > > - platform_set_drvdata(pdev, data); > + if (!master) > + continue; > > - pm_runtime_enable(dev); > - data->runtime_active = !pm_runtime_enabled(dev); > + owner = master->dev.archdata.iommu; > + if (!owner) > + continue; > > - dev_dbg(dev, "Probed and initialized\n"); > - return 0; > + for_each_sysmmu_list(owner->dev, list_data) { > + if (list_data->sysmmu == dev) { > + list_del(&list_data->entry); > + kfree(list_data); > + break; > + } > + } > + } > + > + return ret; > } >
Re: [PATCH v11 12/27] ARM: dts: Add description of System MMU of Exynos SoCs
On Sun, 20 Apr 2014 15:25:59 +0530, Shaik Ameer Basha wrote: > Hi KyongHo Cho, > > Please find the comments inline. > > On Fri, Mar 14, 2014 at 10:36 AM, Cho KyongHo wrote: > > This patch adds dts entries for the System MMU devices found on > > Exynos4 and Exynos5 SoC series and the System MMU binding > > documentation. > > > > CC: Rob Herring > > CC: Sylwester Nawrocki > > Signed-off-by: Cho KyongHo > > --- > > .../bindings/iommu/samsung,exynos4210-sysmmu.txt | 86 +++ > > arch/arm/boot/dts/exynos4.dtsi | 107 > > arch/arm/boot/dts/exynos4210.dtsi | 23 +- > > arch/arm/boot/dts/exynos4x12.dtsi | 77 +- > > arch/arm/boot/dts/exynos5250.dtsi | 266 > > +++- > > arch/arm/boot/dts/exynos5420.dtsi | 205 ++- > > 6 files changed, 758 insertions(+), 6 deletions(-) > > create mode 100644 > > Documentation/devicetree/bindings/iommu/samsung,exynos4210-sysmmu.txt > > > > diff --git > > a/Documentation/devicetree/bindings/iommu/samsung,exynos4210-sysmmu.txt > > b/Documentation/devicetree/bindings/iommu/samsung,exynos4210-sysmmu.txt > > new file mode 100644 > > index 000..e4417bb > > --- /dev/null > > +++ b/Documentation/devicetree/bindings/iommu/samsung,exynos4210-sysmmu.txt > > @@ -0,0 +1,86 @@ > > +Samsung Exynos IOMMU H/W, System MMU (System Memory Management Unit) > > + > > +Samsung's Exynos architecture contains System MMUs that enables scattered > > +physical memory chunks visible as a contiguous region to DMA-capable > > peripheral > > +devices like MFC, FIMC, FIMD, GScaler, FIMC-IS and so forth. > > + > > [snip] > > > > diff --git a/arch/arm/boot/dts/exynos5250.dtsi > > b/arch/arm/boot/dts/exynos5250.dtsi > > index 8f6300f..df336ea 100644 > > --- a/arch/arm/boot/dts/exynos5250.dtsi > > +++ b/arch/arm/boot/dts/exynos5250.dtsi > > @@ -80,6 +80,16 @@ > > reg = <0x10044040 0x20>; > > }; > > > > + pd_isp: isp-power-domain@0x10044020 { > > + compatible = "samsung,exynos4210-pd"; > > + reg = <0x10044020 0x20>; > > + }; > > + > > + pd_disp1: disp1-power-domain@0x100440A0 { > > + compatible = "samsung,exynos4210-pd"; > > + reg = <0x100440A0 0x20>; > > + }; > > + > > clock: clock-controller@1001 { > > compatible = "samsung,exynos5250-clock"; > > reg = <0x1001 0x3>; > > @@ -679,7 +689,7 @@ > > "sclk_hdmiphy", "mout_hdmi"; > > }; > > > > - mixer { > > + mixer: mixer { > > compatible = "samsung,exynos5250-mixer"; > > reg = <0x1445 0x1>; > > interrupts = <0 94 0>; > > @@ -700,7 +710,7 @@ > > phy-names = "dp"; > > }; > > > > - fimd@1440 { > > + fimd: fimd@1440 { > > clocks = <&clock 133>, <&clock 339>; > > clock-names = "sclk_fimd", "fimd"; > > }; > > @@ -715,4 +725,256 @@ > > io-channel-ranges; > > status = "disabled"; > > }; > > + > > + sysmmu_g2d: sysmmu@10A6 { > > + compatible = "samsung,sysmmu-v1"; > > + reg = <0x10A6 0x1000>; > > + interrupt-parent = <&combiner>; > > + interrupts = <24 5>; > > + clock-names = "sysmmu"; > > + clocks = <&clock 361>; > > + }; > > + > > + sysmmu_mfc_r: sysmmu@1120 { > > + compatible = "samsung,sysmmu-v2"; > > + reg = <0x1120 0x1000>; > > + interrupt-parent = <&combiner>; > > + interrupts = <6 2>; > > + clock-names = "sysmmu", "master"; > > + clocks = <&clock 268>, <&clock 266>; > > Add mmu-masters... > mmu-masters = <&mfc>; > Ok. > > + samsung,power-domain = <&pd_mfc>; > > + }; > > + > > + sysmmu_mfc_l: sysmmu@1121 { > > + compatible = "samsung,sysmmu-v2"; > > + reg = <0x1121 0x1000>; > > + interrupt-parent = <&combiner>; > > + interrupts = <8 5>; > > + clock-names = "sysmmu", "master"; > > + clocks = <&clock 267>, <&clock 266>; > > Add mmu-masters... > mmu-masters = <&mfc>; > OK. > > > + samsung,power-domain = <&pd_mfc>; > > + }; > > + > > + sysmmu_rotator: sysmmu@11D4 { > > + compatible = "samsung,sysmmu-v1"; > > + reg = <0x11D4 0x1000>; > > + interrupt-parent = <&combiner>; > > + interrupts = <4 0>; > > + clock-names = "sysmmu"; > > + clocks = <&clock 272>; > > + }; > > + > > [snip] > > > }; > > diff --git a/arch/arm/boot/dts/exynos5420.dtsi > > b/arch/arm
Re: [PATCH 0/9] Renesas ipmmu-vmsa: Miscellaneous cleanups and fixes
Hi Will, On Tuesday 22 April 2014 12:34:23 Will Deacon wrote: > On Mon, Apr 21, 2014 at 03:13:00PM +0100, Laurent Pinchart wrote: > > Hello, > > Hi Laurent, > > > This patch set cleans up and fixes small issues in the ipmmu-vmsa driver. > > The patches are based on top of "[PATCH v3] iommu: Add driver for Renesas > > VMSA-compatible IPMMU" that adds the ipmmu-vmsa driver. > > > > The most interesting part of this series is the rewrite of the page table > > management code. The IOMMU core guarantees that the map and unmap > > operations will always be called only with page sizes advertised by the > > driver. We can use that assumption to remove loops of PGD and PMD > > entries, simplifying the code. > > Hmm, interesting. We still have to handle the case where a mapping created > with one page-size could be unmapped with another though (in particular, > unmapping part of the range). Correct. I've implemented that in patch 9/9. Note that the patch also frees pages use for page directory entries when they're not needed anymore, instead of just marking them as invalid. That's something you probably should do in the arm-smmu driver as well. > > Will, would it make sense to perform the same cleanup for the arm-smmu > > driver, or is there a reason to keep loops over PGD and PMD entries ? > > Removing them makes the implementation of 68kB and 2MB pages easier. > > Is this an assumption that's relied on by other IOMMU drivers? It certainly > makes mapping of large ranges less efficient than it could be, so I'm more > inclined to set all the bits > PAGE_SIZE in pgsize_bitmap if it's only used > to determine the granularity at which map/unmap are called (which is > unrelated to what the hardware can actually do). I haven't checked all the other IOMMU drivers, but at least the OMAP IOMMU driver relies on the same assumption. Splitting map/unmap operations in page size chunks inside the IOMMU core might indeed have a negative performance impact due to locking, but I'm not sure it would be noticeable. -- Regards, Laurent Pinchart ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH 0/9] Renesas ipmmu-vmsa: Miscellaneous cleanups and fixes
On Mon, Apr 21, 2014 at 03:13:00PM +0100, Laurent Pinchart wrote: > Hello, Hi Laurent, > This patch set cleans up and fixes small issues in the ipmmu-vmsa driver. The > patches are based on top of "[PATCH v3] iommu: Add driver for Renesas > VMSA-compatible IPMMU" that adds the ipmmu-vmsa driver. > > The most interesting part of this series is the rewrite of the page table > management code. The IOMMU core guarantees that the map and unmap operations > will always be called only with page sizes advertised by the driver. We can > use that assumption to remove loops of PGD and PMD entries, simplifying the > code. Hmm, interesting. We still have to handle the case where a mapping created with one page-size could be unmapped with another though (in particular, unmapping part of the range). > Will, would it make sense to perform the same cleanup for the arm-smmu driver, > or is there a reason to keep loops over PGD and PMD entries ? Removing them > makes the implementation of 68kB and 2MB pages easier. Is this an assumption that's relied on by other IOMMU drivers? It certainly makes mapping of large ranges less efficient than it could be, so I'm more inclined to set all the bits > PAGE_SIZE in pgsize_bitmap if it's only used to determine the granularity at which map/unmap are called (which is unrelated to what the hardware can actually do). Will ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH] iommu/arm-smmu: fix incorrect use of S2CR_TYPE_SHIFT
On Tue, Apr 22, 2014 at 11:27:42AM +0100, Kefeng Wang wrote: > On 04/22 18:00, Will Deacon wrote: > > On Fri, Apr 18, 2014 at 03:31:17AM +0100, Kefeng Wang wrote: > >> On 04/18 10:07, Kefeng Wang wrote: > >>> There is already S2CR_TYPE_TRANS in S2CR_TYPE_TRANS macro, > ^^^ > >>> so drop the second shift. > >> > >> Typo issue, please use following patch. > > > > They look the same to me -- which one should I take? > > > > Also, since S2CR_TYPE_TRANS is 0x0, this fix isn't critical, so I'll include > > it in my updates pull for 3.16. > > OK, please use second one, thanks. Ok, thanks. Will ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH] iommu/arm-smmu: fix incorrect use of S2CR_TYPE_SHIFT
On 04/22 18:00, Will Deacon wrote: > On Fri, Apr 18, 2014 at 03:31:17AM +0100, Kefeng Wang wrote: >> On 04/18 10:07, Kefeng Wang wrote: >>> There is already S2CR_TYPE_TRANS in S2CR_TYPE_TRANS macro, ^^^ >>> so drop the second shift. >> >> Typo issue, please use following patch. > > They look the same to me -- which one should I take? > > Also, since S2CR_TYPE_TRANS is 0x0, this fix isn't critical, so I'll include > it in my updates pull for 3.16. OK, please use second one, thanks. Kefeng > > Cheers, > > Will > >>> Signed-off-by: Kefeng Wang >>> --- >>> drivers/iommu/arm-smmu.c |2 +- >>> 1 files changed, 1 insertions(+), 1 deletions(-) >>> >>> diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c >>> index 8b89e33..96755ec 100644 >>> --- a/drivers/iommu/arm-smmu.c >>> +++ b/drivers/iommu/arm-smmu.c >>> @@ -1167,7 +1167,7 @@ static int arm_smmu_domain_add_master(struct >>> arm_smmu_domain *smmu_domain, >>> for (i = 0; i < master->num_streamids; ++i) { >>> u32 idx, s2cr; >>> idx = master->smrs ? master->smrs[i].idx : master->streamids[i]; >>> - s2cr = (S2CR_TYPE_TRANS << S2CR_TYPE_SHIFT) | >>> + s2cr = S2CR_TYPE_TRANS | >>>(smmu_domain->root_cfg.cbndx << S2CR_CBNDX_SHIFT); >>> writel_relaxed(s2cr, gr0_base + ARM_SMMU_GR0_S2CR(idx)); >>> } >>> >> >> >> From bfcdbee6f5e71c561dfddf8751c4eabdca1e3a56 Mon Sep 17 00:00:00 2001 >> From: Kefeng Wang >> Date: Fri, 18 Apr 2014 10:20:48 +0800 >> Subject: [PATCH] iommu/arm-smmu: fix incorrect use of S2CR_TYPE_SHIFT >> >> There is already S2CR_TYPE_SHIFT in S2CR_TYPE_TRANS macro, >> so drop the second shift. >> >> Signed-off-by: Kefeng Wang >> --- >> drivers/iommu/arm-smmu.c |2 +- >> 1 files changed, 1 insertions(+), 1 deletions(-) >> >> diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c >> index 8b89e33..96755ec 100644 >> --- a/drivers/iommu/arm-smmu.c >> +++ b/drivers/iommu/arm-smmu.c >> @@ -1167,7 +1167,7 @@ static int arm_smmu_domain_add_master(struct >> arm_smmu_domain *smmu_domain, >> for (i = 0; i < master->num_streamids; ++i) { >> u32 idx, s2cr; >> idx = master->smrs ? master->smrs[i].idx : master->streamids[i]; >> -s2cr = (S2CR_TYPE_TRANS << S2CR_TYPE_SHIFT) | >> +s2cr = S2CR_TYPE_TRANS | >> (smmu_domain->root_cfg.cbndx << S2CR_CBNDX_SHIFT); >> writel_relaxed(s2cr, gr0_base + ARM_SMMU_GR0_S2CR(idx)); >> } >> -- >> 1.7.1 >> >> >> > > . > ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH] iommu/arm-smmu: fix incorrect use of S2CR_TYPE_SHIFT
On Fri, Apr 18, 2014 at 03:31:17AM +0100, Kefeng Wang wrote: > On 04/18 10:07, Kefeng Wang wrote: > > There is already S2CR_TYPE_TRANS in S2CR_TYPE_TRANS macro, > > so drop the second shift. > > Typo issue, please use following patch. They look the same to me -- which one should I take? Also, since S2CR_TYPE_TRANS is 0x0, this fix isn't critical, so I'll include it in my updates pull for 3.16. Cheers, Will > > Signed-off-by: Kefeng Wang > > --- > > drivers/iommu/arm-smmu.c |2 +- > > 1 files changed, 1 insertions(+), 1 deletions(-) > > > > diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c > > index 8b89e33..96755ec 100644 > > --- a/drivers/iommu/arm-smmu.c > > +++ b/drivers/iommu/arm-smmu.c > > @@ -1167,7 +1167,7 @@ static int arm_smmu_domain_add_master(struct > > arm_smmu_domain *smmu_domain, > > for (i = 0; i < master->num_streamids; ++i) { > > u32 idx, s2cr; > > idx = master->smrs ? master->smrs[i].idx : master->streamids[i]; > > - s2cr = (S2CR_TYPE_TRANS << S2CR_TYPE_SHIFT) | > > + s2cr = S2CR_TYPE_TRANS | > >(smmu_domain->root_cfg.cbndx << S2CR_CBNDX_SHIFT); > > writel_relaxed(s2cr, gr0_base + ARM_SMMU_GR0_S2CR(idx)); > > } > > > > > From bfcdbee6f5e71c561dfddf8751c4eabdca1e3a56 Mon Sep 17 00:00:00 2001 > From: Kefeng Wang > Date: Fri, 18 Apr 2014 10:20:48 +0800 > Subject: [PATCH] iommu/arm-smmu: fix incorrect use of S2CR_TYPE_SHIFT > > There is already S2CR_TYPE_SHIFT in S2CR_TYPE_TRANS macro, > so drop the second shift. > > Signed-off-by: Kefeng Wang > --- > drivers/iommu/arm-smmu.c |2 +- > 1 files changed, 1 insertions(+), 1 deletions(-) > > diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c > index 8b89e33..96755ec 100644 > --- a/drivers/iommu/arm-smmu.c > +++ b/drivers/iommu/arm-smmu.c > @@ -1167,7 +1167,7 @@ static int arm_smmu_domain_add_master(struct > arm_smmu_domain *smmu_domain, > for (i = 0; i < master->num_streamids; ++i) { > u32 idx, s2cr; > idx = master->smrs ? master->smrs[i].idx : master->streamids[i]; > - s2cr = (S2CR_TYPE_TRANS << S2CR_TYPE_SHIFT) | > + s2cr = S2CR_TYPE_TRANS | > (smmu_domain->root_cfg.cbndx << S2CR_CBNDX_SHIFT); > writel_relaxed(s2cr, gr0_base + ARM_SMMU_GR0_S2CR(idx)); > } > -- > 1.7.1 > > > ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH] arm: dma-mapping: Fix mapping size value
Hello, On 2014-04-21 08:47, Ritesh Harjani wrote: 68efd7d2fb("arm: dma-mapping: remove order parameter from arm_iommu_create_mapping()") is causing kernel panic because it wrongly sets the value of mapping->size: Unable to handle kernel NULL pointer dereference at virtual address 00a0 pgd = e7a84000 [00a0] *pgd= ... PC is at bitmap_clear+0x48/0xd0 LR is at __iommu_remove_mapping+0x130/0x164 Fix it by correcting mapping->size value. Signed-off-by: Ritesh Harjani Acked-by: Laurent Pinchart Thanks for spotting this issue! I'm really sorry for introducing it. I will push it to the fixes branch asap. --- arch/arm/mm/dma-mapping.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index f62aa06..6b00be1 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -1963,8 +1963,8 @@ arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, size_t size) mapping->nr_bitmaps = 1; mapping->extensions = extensions; mapping->base = base; - mapping->size = bitmap_size << PAGE_SHIFT; mapping->bits = BITS_PER_BYTE * bitmap_size; + mapping->size = mapping->bits << PAGE_SHIFT; spin_lock_init(&mapping->lock); Best regards -- Marek Szyprowski, PhD Samsung R&D Institute Poland ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH] arm: dma-mapping: Fix mapping size value
On Mon, Apr 21, 2014 at 07:47:27AM +0100, Ritesh Harjani wrote: > 68efd7d2fb("arm: dma-mapping: remove order parameter from > arm_iommu_create_mapping()") is causing kernel panic > because it wrongly sets the value of mapping->size: > > Unable to handle kernel NULL pointer dereference at virtual > address 00a0 > pgd = e7a84000 > [00a0] *pgd= > ... > PC is at bitmap_clear+0x48/0xd0 > LR is at __iommu_remove_mapping+0x130/0x164 > > Fix it by correcting mapping->size value. > > Signed-off-by: Ritesh Harjani > Acked-by: Laurent Pinchart > --- > arch/arm/mm/dma-mapping.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c > index f62aa06..6b00be1 100644 > --- a/arch/arm/mm/dma-mapping.c > +++ b/arch/arm/mm/dma-mapping.c > @@ -1963,8 +1963,8 @@ arm_iommu_create_mapping(struct bus_type *bus, > dma_addr_t base, size_t size) > mapping->nr_bitmaps = 1; > mapping->extensions = extensions; > mapping->base = base; > - mapping->size = bitmap_size << PAGE_SHIFT; > mapping->bits = BITS_PER_BYTE * bitmap_size; > + mapping->size = mapping->bits << PAGE_SHIFT; Ok, but given that mapping->size is derived from mapping->bits, do we really need both of these fields in struct dma_iommu_mapping? Will ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [Patch Part3 V1 19/22] iommu/vt-d: simplify intel_unmap_sg() and kill duplicated code
On 2014/4/22 15:38, David Woodhouse wrote: > On Tue, 2014-04-22 at 15:07 +0800, Jiang Liu wrote: >> + size_t size = 0; >> +#if 0 >> + /* current the third argument of intel_unmap_page is unsued */ >> + int i; >> + struct scatterlist *sg; >> >> - freelist = domain_unmap(domain, start_pfn, last_pfn); >> + for_each_sg(sglist, sg, nelems, i) >> + size += sg->length; >> +#endif > > Please don't do that. If you want to rely on the fact that > intel_unmap_page() currently just uses the size from the IOVA it finds, > then split it out into a separate function which explicitly does > precisely that. And call that new function from both intel_unmap_sg() > and intel_unmap_page(). Good suggestion, will change to follow that way in next version. ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [Patch Part3 V1 19/22] iommu/vt-d: simplify intel_unmap_sg() and kill duplicated code
On Tue, 2014-04-22 at 15:07 +0800, Jiang Liu wrote: > + size_t size = 0; > +#if 0 > + /* current the third argument of intel_unmap_page is unsued */ > + int i; > + struct scatterlist *sg; > > - freelist = domain_unmap(domain, start_pfn, last_pfn); > + for_each_sg(sglist, sg, nelems, i) > + size += sg->length; > +#endif Please don't do that. If you want to rely on the fact that intel_unmap_page() currently just uses the size from the IOVA it finds, then split it out into a separate function which explicitly does precisely that. And call that new function from both intel_unmap_sg() and intel_unmap_page(). -- David WoodhouseOpen Source Technology Centre david.woodho...@intel.com Intel Corporation smime.p7s Description: S/MIME cryptographic signature ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[RFC Patch Part3 V1 00/22] Enable Intel DMAR device hotplug
When hot plugging a descrete IOH or a physical processor with embedded IIO, we need to handle DMAR(or IOMMU) unit in the PCIe host bridge if DMAR is in use. This patch set tries to enhance current DMAR/IOMMU/IR drivers to support hotplug and is based on latest mainstream kernel v3.15-rc2-36-gc089b229dfdd. Patch 1-9 are bugfixes and code improvement for current drivers. Patch 10-13 enhances DMAR framework to support hotplug Patch 14 enhances Intel interrupt remapping driver to support hotplug Patch 15 enhances error handling in Intel IR driver Patch 16 enhance Intel IOMMU to support hotplug Patch 17 enhance ACPI pci_root driver to handle DMAR units Patch 18-22 are bugfixes and code improvement again This patch set has been tested on Intel development machine. Appreciate any comments and tests. Best Regards! Jiang Liu (22): iommu/vt-d: match segment number when searching for dev_iotlb capable devices iommu/vt-d: use correct domain id to flush virtual machine domains iommu/vt-d: introduce helper functions to improve code readability iommu/vt-d: introduce helper functions to make code symmetric for readability iommu/vt-d: only dynamically allocate domain id for virtual domains iommu/vt-d: fix possible invalid memory access caused by free_dmar_iommu() iommu/vt-d: avoid freeing virtual machine domain in free_dmar_iommu() iommu/VT-d: simplify include/linux/dmar.h iommu/vt-d: change iommu_enable/disable_translation to return void iommu/vt-d: dynamically allocate and free seq_id for DMAR units IOMMU/vt-d: introduce helper function dmar_walk_resources() iommu/vt-d: implement DMAR unit hotplug framework iommu/vt-d: search _DSM method for DMAR hotplug iommu/vt-d: enhance intel_irq_remapping driver to support DMAR unit hotplug iommu/vt-d: enhance error recovery in function intel_enable_irq_remapping() iommu/vt-d: enhance intel-iommu driver to support DMAR unit hotplug pci, ACPI, iommu: enhance pci_root to support DMAR device hotplug iommu/vt-d: update proximity information when a new node with memory available iommu/vt-d: simplify intel_unmap_sg() and kill duplicated code iommu/vt-d: introduce helper domain_pfn_within_range() to simplify code iommu/vt-d: introduce helper function iova_size() to improve code readability iommu/vt-d: fix bug in computing domain's iommu_snooping flag drivers/acpi/pci_root.c | 16 +- drivers/iommu/dmar.c| 568 ++-- drivers/iommu/intel-iommu.c | 711 +-- drivers/iommu/intel_irq_remapping.c | 233 +--- include/linux/dmar.h| 80 ++-- include/linux/intel-iommu.h |1 + include/linux/iova.h|5 + 7 files changed, 1133 insertions(+), 481 deletions(-) -- 1.7.10.4 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[Patch Part3 V1 22/22] iommu/vt-d: fix bug in computing domain's iommu_snooping flag
IOMMU units may dynamically attached to/detached from domains, so we should scan all active IOMMU units when computing iommu_snooping flag for a domain instead of only scanning IOMMU units associated with the domain. Also check snooping and superpage capabilities when hot-adding DMAR units. Signed-off-by: Jiang Liu --- drivers/iommu/intel-iommu.c | 54 --- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 6d5b94aaac6e..9c39cab62cc1 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -626,50 +626,56 @@ static void domain_update_iommu_coherency(struct dmar_domain *domain) rcu_read_unlock(); } -static void domain_update_iommu_snooping(struct dmar_domain *domain) +static int domain_update_iommu_snooping(struct intel_iommu *skip) { - int i; - - domain->iommu_snooping = 1; + struct dmar_drhd_unit *drhd; + struct intel_iommu *iommu; + int ret = 1; - for_each_set_bit(i, domain->iommu_bmp, g_num_of_iommus) { - if (!ecap_sc_support(g_iommus[i]->ecap)) { - domain->iommu_snooping = 0; - break; + rcu_read_lock(); + for_each_active_iommu(iommu, drhd) { + if (iommu != skip) { + if (!ecap_sc_support(iommu->ecap)) { + ret = 0; + break; + } } } + rcu_read_unlock(); + + return ret; } -static void domain_update_iommu_superpage(struct dmar_domain *domain) +static int domain_update_iommu_superpage(struct intel_iommu *skip) { struct dmar_drhd_unit *drhd; - struct intel_iommu *iommu = NULL; + struct intel_iommu *iommu; int mask = 0xf; if (!intel_iommu_superpage) { - domain->iommu_superpage = 0; - return; + return 0; } /* set iommu_superpage to the smallest common denominator */ rcu_read_lock(); for_each_active_iommu(iommu, drhd) { - mask &= cap_super_page_val(iommu->cap); - if (!mask) { - break; + if (iommu != skip) { + mask &= cap_super_page_val(iommu->cap); + if (!mask) + break; } } rcu_read_unlock(); - domain->iommu_superpage = fls(mask); + return fls(mask); } /* Some capabilities may be different across iommus */ static void domain_update_iommu_cap(struct dmar_domain *domain) { domain_update_iommu_coherency(domain); - domain_update_iommu_snooping(domain); - domain_update_iommu_superpage(domain); + domain->iommu_snooping = domain_update_iommu_snooping(NULL); + domain->iommu_superpage = domain_update_iommu_superpage(NULL); } static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devfn) @@ -3793,6 +3799,18 @@ static int intel_iommu_add(struct dmar_drhd_unit *dmaru) iommu->name); return -ENXIO; } + if (!ecap_sc_support(iommu->ecap) && + domain_update_iommu_snooping(iommu)) { + pr_warn("IOMMU: %s doesn't support snooping.\n", + iommu->name); + return -ENXIO; + } + if ((cap_super_page_val(iommu->cap) & + (1 << domain_update_iommu_superpage(iommu))) == 0) { + pr_warn("IOMMU: %s doesn't support large page.\n", + iommu->name); + return -ENXIO; + } /* * Disable translation if already enabled prior to OS handover. -- 1.7.10.4 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[Patch Part3 V1 21/22] iommu/vt-d: introduce helper function iova_size() to improve code readability
Signed-off-by: Jiang Liu --- drivers/iommu/intel-iommu.c |7 +++ include/linux/iova.h|5 + 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index c4bca2d814c8..6d5b94aaac6e 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -3114,10 +3114,10 @@ static void flush_unmaps(void) /* On real hardware multiple invalidations are expensive */ if (cap_caching_mode(iommu->cap)) iommu_flush_iotlb_psi(iommu, domain->id, - iova->pfn_lo, iova->pfn_hi - iova->pfn_lo + 1, + iova->pfn_lo, iova_size(iova), !deferred_flush[i].freelist[j], 0); else { - mask = ilog2(mm_to_dma_pfn(iova->pfn_hi - iova->pfn_lo + 1)); + mask = ilog2(mm_to_dma_pfn(iova_size(iova))); iommu_flush_dev_iotlb(deferred_flush[i].domain[j], (uint64_t)iova->pfn_lo << PAGE_SHIFT, mask); } @@ -4061,8 +4061,7 @@ static int intel_iommu_memory_notifier(struct notifier_block *nb, rcu_read_lock(); for_each_active_iommu(iommu, drhd) iommu_flush_iotlb_psi(iommu, si_domain->id, - iova->pfn_lo, - iova->pfn_hi - iova->pfn_lo + 1, + iova->pfn_lo, iova_size(iova), !freelist, 0); rcu_read_unlock(); dma_free_pagelist(freelist); diff --git a/include/linux/iova.h b/include/linux/iova.h index 3277f4711349..19e81d5ccb6d 100644 --- a/include/linux/iova.h +++ b/include/linux/iova.h @@ -34,6 +34,11 @@ struct iova_domain { unsigned long dma_32bit_pfn; }; +static inline unsigned long iova_size(struct iova *iova) +{ + return iova->pfn_hi - iova->pfn_lo + 1; +} + struct iova *alloc_iova_mem(void); void free_iova_mem(struct iova *iova); void free_iova(struct iova_domain *iovad, unsigned long pfn); -- 1.7.10.4 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[Patch Part3 V1 18/22] iommu/vt-d: update proximity information when a new node with memory available
If node associated with a DMAR unit has no memory available, it uses -1 as default NUMA node id. So when memory hot-addition adds new memory to an empty NUMA node, we should update the NUMA node id associated with the DMAR unit. This will help to optomize memory allocation. Signed-off-by: Jiang Liu --- drivers/iommu/dmar.c| 50 --- include/linux/intel-iommu.h |1 + 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index 7a7383858c34..d607691a6a80 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -444,6 +445,7 @@ static int dmar_parse_one_rhsa(struct acpi_dmar_header *header, void *arg) if (!node_online(node)) node = -1; drhd->iommu->node = node; + drhd->iommu->proximity_domain = rhsa->proximity_domain; return 0; } } @@ -458,8 +460,48 @@ static int dmar_parse_one_rhsa(struct acpi_dmar_header *header, void *arg) return 0; } + +static int dmar_memory_notifier(struct notifier_block *nb, + unsigned long val, void *v) +{ + struct memory_notify *mhp = v; + struct dmar_drhd_unit *drhd; + struct intel_iommu *iommu; + int nid; + + if (val != MEM_ONLINE) + return NOTIFY_OK; + + if (mhp->status_change_nid < 0) + return NOTIFY_OK; + + down_read(&dmar_global_lock); + for_each_iommu(iommu, drhd) { + if (iommu->node != -1) + continue; + nid = acpi_map_pxm_to_node(iommu->proximity_domain); + if (nid == mhp->status_change_nid) + iommu->node = nid; + } + up_read(&dmar_global_lock); + + return NOTIFY_OK; +} + +static struct notifier_block dmar_memory_nb = { + .notifier_call = dmar_memory_notifier, +}; + +static void dmar_register_memory_notifier(void) +{ + register_memory_notifier(&dmar_memory_nb); +} #else #definedmar_parse_one_rhsa dmar_res_noop + +static void dmar_register_memory_notifier(void) +{ +} #endif static void __init @@ -1715,12 +1757,14 @@ static inline bool dmar_in_use(void) return irq_remapping_enabled || intel_iommu_enabled; } -static int __init dmar_free_unused_resources(void) +static int __init dmar_late_init(void) { struct dmar_drhd_unit *dmaru, *dmaru_n; - if (dmar_in_use()) + if (dmar_in_use()) { + dmar_register_memory_notifier(); return 0; + } if (dmar_dev_scope_status != 1 && !list_empty(&dmar_drhd_units)) bus_unregister_notifier(&pci_bus_type, &dmar_pci_bus_nb); @@ -1735,7 +1779,7 @@ static int __init dmar_free_unused_resources(void) return 0; } -late_initcall(dmar_free_unused_resources); +late_initcall(dmar_late_init); IOMMU_INIT_POST(detect_intel_iommu); /* diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h index 0a2da5188217..eea52ccdbd8e 100644 --- a/include/linux/intel-iommu.h +++ b/include/linux/intel-iommu.h @@ -337,6 +337,7 @@ struct intel_iommu { struct ir_table *ir_table; /* Interrupt remapping info */ #endif int node; + int proximity_domain; }; static inline void __iommu_flush_cache( -- 1.7.10.4 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[Patch Part3 V1 14/22] iommu/vt-d: enhance intel_irq_remapping driver to support DMAR unit hotplug
Implement required callback functions for intel_irq_remapping driver to support DMAR unit hotplug. Signed-off-by: Jiang Liu --- drivers/iommu/intel_irq_remapping.c | 222 ++- 1 file changed, 169 insertions(+), 53 deletions(-) diff --git a/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c index aa309a8e4d3a..9f1c86566452 100644 --- a/drivers/iommu/intel_irq_remapping.c +++ b/drivers/iommu/intel_irq_remapping.c @@ -36,7 +36,6 @@ struct hpet_scope { static struct ioapic_scope ir_ioapic[MAX_IO_APICS]; static struct hpet_scope ir_hpet[MAX_HPET_TBS]; -static int ir_ioapic_num, ir_hpet_num; /* * Lock ordering: @@ -320,7 +319,7 @@ static int set_ioapic_sid(struct irte *irte, int apic) down_read(&dmar_global_lock); for (i = 0; i < MAX_IO_APICS; i++) { - if (ir_ioapic[i].id == apic) { + if (ir_ioapic[i].iommu && ir_ioapic[i].id == apic) { sid = (ir_ioapic[i].bus << 8) | ir_ioapic[i].devfn; break; } @@ -347,7 +346,7 @@ static int set_hpet_sid(struct irte *irte, u8 id) down_read(&dmar_global_lock); for (i = 0; i < MAX_HPET_TBS; i++) { - if (ir_hpet[i].id == id) { + if (ir_hpet[i].iommu && ir_hpet[i].id == id) { sid = (ir_hpet[i].bus << 8) | ir_hpet[i].devfn; break; } @@ -446,17 +445,17 @@ static void iommu_set_irq_remapping(struct intel_iommu *iommu, int mode) raw_spin_unlock_irqrestore(&iommu->register_lock, flags); } - -static int intel_setup_irq_remapping(struct intel_iommu *iommu, int mode) +static int intel_setup_irq_remapping(struct intel_iommu *iommu) { struct ir_table *ir_table; struct page *pages; unsigned long *bitmap; - ir_table = iommu->ir_table = kzalloc(sizeof(struct ir_table), -GFP_ATOMIC); + if (iommu->ir_table) + return 0; - if (!iommu->ir_table) + ir_table = kzalloc(sizeof(struct ir_table), GFP_ATOMIC); + if (!ir_table) return -ENOMEM; pages = alloc_pages_node(iommu->node, GFP_ATOMIC | __GFP_ZERO, @@ -465,7 +464,7 @@ static int intel_setup_irq_remapping(struct intel_iommu *iommu, int mode) if (!pages) { pr_err("IR%d: failed to allocate pages of order %d\n", iommu->seq_id, INTR_REMAP_PAGE_ORDER); - kfree(iommu->ir_table); + kfree(ir_table); return -ENOMEM; } @@ -480,11 +479,22 @@ static int intel_setup_irq_remapping(struct intel_iommu *iommu, int mode) ir_table->base = page_address(pages); ir_table->bitmap = bitmap; + iommu->ir_table = ir_table; - iommu_set_irq_remapping(iommu, mode); return 0; } +static void intel_teardown_irq_remapping(struct intel_iommu *iommu) +{ + if (iommu && iommu->ir_table) { + free_pages((unsigned long)iommu->ir_table->base, + INTR_REMAP_PAGE_ORDER); + kfree(iommu->ir_table->bitmap); + kfree(iommu->ir_table); + iommu->ir_table = NULL; + } +} + /* * Disable Interrupt Remapping. */ @@ -639,9 +649,10 @@ static int __init intel_enable_irq_remapping(void) if (!ecap_ir_support(iommu->ecap)) continue; - if (intel_setup_irq_remapping(iommu, eim)) + if (intel_setup_irq_remapping(iommu)) goto error; + iommu_set_irq_remapping(iommu, eim); setup = 1; } @@ -672,12 +683,13 @@ error: return -1; } -static void ir_parse_one_hpet_scope(struct acpi_dmar_device_scope *scope, - struct intel_iommu *iommu) +static int ir_parse_one_hpet_scope(struct acpi_dmar_device_scope *scope, + struct intel_iommu *iommu, + struct acpi_dmar_hardware_unit *drhd) { struct acpi_dmar_pci_path *path; u8 bus; - int count; + int count, free = -1; bus = scope->bus; path = (struct acpi_dmar_pci_path *)(scope + 1); @@ -693,19 +705,36 @@ static void ir_parse_one_hpet_scope(struct acpi_dmar_device_scope *scope, PCI_SECONDARY_BUS); path++; } - ir_hpet[ir_hpet_num].bus = bus; - ir_hpet[ir_hpet_num].devfn = PCI_DEVFN(path->device, path->function); - ir_hpet[ir_hpet_num].iommu = iommu; - ir_hpet[ir_hpet_num].id= scope->enumeration_id; - ir_hpet_num++; + + for (count = 0; count < MAX_HPET_TBS; count++) { + if (ir_hpet[count].iommu == iommu && + ir_hpet[count].id == scope->enumeration_id) + return 0; +
[Patch Part3 V1 11/22] IOMMU/vt-d: introduce helper function dmar_walk_resources()
Introduce helper function dmar_walk_resources to walk resource entries in DMAR table and ACPI buffer object returned by ACPI _DSM method for IOMMU hot-plug. Signed-off-by: Jiang Liu --- drivers/iommu/dmar.c| 207 +++ drivers/iommu/intel-iommu.c |4 +- include/linux/dmar.h| 19 ++-- 3 files changed, 121 insertions(+), 109 deletions(-) diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index 3e4168cddc64..d09ee0d55946 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -43,6 +43,14 @@ #include "irq_remapping.h" +typedef int (*dmar_res_handler_t)(struct acpi_dmar_header *, void *); +struct dmar_res_callback { + dmar_res_handler_t cb[ACPI_DMAR_TYPE_RESERVED]; + void*arg[ACPI_DMAR_TYPE_RESERVED]; + boolignore_unhandled; + boolprint_entry; +}; + /* * Assumptions: * 1) The hotplug framework guarentees that DMAR unit will be hot-added @@ -333,7 +341,7 @@ static struct notifier_block dmar_pci_bus_nb = { * present in the platform */ static int __init -dmar_parse_one_drhd(struct acpi_dmar_header *header) +dmar_parse_one_drhd(struct acpi_dmar_header *header, void *arg) { struct acpi_dmar_hardware_unit *drhd; struct dmar_drhd_unit *dmaru; @@ -364,6 +372,10 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header) return ret; } dmar_register_drhd_unit(dmaru); + + if (arg) + (*(int *)arg)++; + return 0; } @@ -376,7 +388,7 @@ static void dmar_free_drhd(struct dmar_drhd_unit *dmaru) kfree(dmaru); } -static int __init dmar_parse_one_andd(struct acpi_dmar_header *header) +static int __init dmar_parse_one_andd(struct acpi_dmar_header *header, void *arg) { struct acpi_dmar_andd *andd = (void *)header; @@ -398,7 +410,7 @@ static int __init dmar_parse_one_andd(struct acpi_dmar_header *header) #ifdef CONFIG_ACPI_NUMA static int __init -dmar_parse_one_rhsa(struct acpi_dmar_header *header) +dmar_parse_one_rhsa(struct acpi_dmar_header *header, void *arg) { struct acpi_dmar_rhsa *rhsa; struct dmar_drhd_unit *drhd; @@ -425,6 +437,8 @@ dmar_parse_one_rhsa(struct acpi_dmar_header *header) return 0; } +#else +#definedmar_parse_one_rhsa dmar_res_noop #endif static void __init @@ -486,6 +500,52 @@ static int __init dmar_table_detect(void) return (ACPI_SUCCESS(status) ? 1 : 0); } +static int dmar_walk_resources(struct acpi_dmar_header *start, size_t len, + struct dmar_res_callback *cb) +{ + int ret = 0; + struct acpi_dmar_header *iter, *next; + struct acpi_dmar_header *end = ((void *)start) + len; + + for (iter = start; iter < end && ret == 0; iter = next) { + next = (void *)iter + iter->length; + if (iter->length == 0) { + /* Avoid looping forever on bad ACPI tables */ + pr_debug(FW_BUG "Invalid 0-length structure\n"); + break; + } else if (next > end) { + /* Avoid passing table end */ + pr_warn(FW_BUG "record passes table end\n"); + ret = -EINVAL; + break; + } + + if (cb->print_entry) + dmar_table_print_dmar_entry(iter); + + if (iter->type >= ACPI_DMAR_TYPE_RESERVED) { + /* continue for forward compatibility */ + pr_debug("Unknown DMAR structure type %d\n", +iter->type); + } else if (cb->cb[iter->type]) { + ret = cb->cb[iter->type](iter, cb->arg[iter->type]); + } else if (!cb->ignore_unhandled) { + pr_warn("No handler for DMAR structure type %d\n", + iter->type); + ret = -EINVAL; + } + } + + return ret; +} + +static inline int dmar_walk_dmar_table(struct acpi_table_dmar *dmar, + struct dmar_res_callback *cb) +{ + return dmar_walk_resources((struct acpi_dmar_header *)(dmar + 1), + dmar->header.length - sizeof(*dmar), cb); +} + /** * parse_dmar_table - parses the DMA reporting table */ @@ -493,9 +553,18 @@ static int __init parse_dmar_table(void) { struct acpi_table_dmar *dmar; - struct acpi_dmar_header *entry_header; int ret = 0; int drhd_count = 0; + struct dmar_res_callback cb = { + .print_entry = true, + .ignore_unhandled = true, + .arg[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &drhd_count, + .cb[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &dmar_parse_one_drhd, + .cb[ACPI_DMAR_TYPE_RESERVED_ME
[Patch Part3 V1 10/22] iommu/vt-d: dynamically allocate and free seq_id for DMAR units
Introduce functions to support dynamic IOMMU seq_id allocating and releasing, which will be used to support DMAR hotplug. Also rename IOMMU_UNITS_SUPPORTED as DMAR_UNITS_SUPPORTED. Signed-off-by: Jiang Liu --- drivers/iommu/dmar.c| 40 ++-- drivers/iommu/intel-iommu.c | 13 +++-- include/linux/dmar.h|6 ++ 3 files changed, 43 insertions(+), 16 deletions(-) diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index 39f8b717fe84..3e4168cddc64 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -61,6 +61,7 @@ LIST_HEAD(dmar_drhd_units); struct acpi_table_header * __initdata dmar_tbl; static acpi_size dmar_tbl_size; static int dmar_dev_scope_status = 1; +static unsigned long dmar_seq_ids[BITS_TO_LONGS(DMAR_UNITS_SUPPORTED)]; static int alloc_iommu(struct dmar_drhd_unit *drhd); static void free_iommu(struct intel_iommu *iommu); @@ -914,11 +915,32 @@ out: return err; } +static int dmar_alloc_seq_id(struct intel_iommu *iommu) +{ + iommu->seq_id = find_first_zero_bit(dmar_seq_ids, + DMAR_UNITS_SUPPORTED); + if (iommu->seq_id >= DMAR_UNITS_SUPPORTED) { + iommu->seq_id = -1; + } else { + set_bit(iommu->seq_id, dmar_seq_ids); + sprintf(iommu->name, "dmar%d", iommu->seq_id); + } + + return iommu->seq_id; +} + +static void dmar_free_seq_id(struct intel_iommu *iommu) +{ + if (iommu->seq_id >= 0) { + clear_bit(iommu->seq_id, dmar_seq_ids); + iommu->seq_id = -1; + } +} + static int alloc_iommu(struct dmar_drhd_unit *drhd) { struct intel_iommu *iommu; u32 ver, sts; - static int iommu_allocated = 0; int agaw = 0; int msagaw = 0; int err; @@ -932,13 +954,16 @@ static int alloc_iommu(struct dmar_drhd_unit *drhd) if (!iommu) return -ENOMEM; - iommu->seq_id = iommu_allocated++; - sprintf (iommu->name, "dmar%d", iommu->seq_id); + if (dmar_alloc_seq_id(iommu) < 0) { + pr_err("IOMMU: failed to allocate seq_id\n"); + err = -ENOSPC; + goto error; + } err = map_iommu(iommu, drhd->reg_base_addr); if (err) { pr_err("IOMMU: failed to map %s\n", iommu->name); - goto error; + goto error_free_seq_id; } err = -EINVAL; @@ -982,9 +1007,11 @@ static int alloc_iommu(struct dmar_drhd_unit *drhd) drhd->iommu = iommu; return 0; - err_unmap: +err_unmap: unmap_iommu(iommu); - error: +error_free_seq_id: + dmar_free_seq_id(iommu); +error: kfree(iommu); return err; } @@ -1006,6 +1033,7 @@ static void free_iommu(struct intel_iommu *iommu) if (iommu->reg) unmap_iommu(iommu); + dmar_free_seq_id(iommu); kfree(iommu); } diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 75d7c5e3f77d..92d7b276edb3 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -328,17 +328,10 @@ static int hw_pass_through = 1; /* si_domain contains mulitple devices */ #define DOMAIN_FLAG_STATIC_IDENTITY(1 << 1) -/* define the limit of IOMMUs supported in each domain */ -#ifdef CONFIG_X86 -# define IOMMU_UNITS_SUPPORTED MAX_IO_APICS -#else -# define IOMMU_UNITS_SUPPORTED 64 -#endif - struct dmar_domain { int id; /* domain id */ int nid;/* node id */ - DECLARE_BITMAP(iommu_bmp, IOMMU_UNITS_SUPPORTED); + DECLARE_BITMAP(iommu_bmp, DMAR_UNITS_SUPPORTED); /* bitmap of iommus this domain uses*/ struct list_head devices; /* all devices' list */ @@ -2707,12 +2700,12 @@ static int __init init_dmars(void) * threaded kernel __init code path all other access are read * only */ - if (g_num_of_iommus < IOMMU_UNITS_SUPPORTED) { + if (g_num_of_iommus < DMAR_UNITS_SUPPORTED) { g_num_of_iommus++; continue; } printk_once(KERN_ERR "intel-iommu: exceeded %d IOMMUs\n", - IOMMU_UNITS_SUPPORTED); + DMAR_UNITS_SUPPORTED); } g_iommus = kcalloc(g_num_of_iommus, sizeof(struct intel_iommu *), diff --git a/include/linux/dmar.h b/include/linux/dmar.h index 1deece46a0ca..4aa8de0d1b97 100644 --- a/include/linux/dmar.h +++ b/include/linux/dmar.h @@ -30,6 +30,12 @@ struct acpi_dmar_header; +#ifdef CONFIG_X86 +# define DMAR_UNITS_SUPPORTEDMAX_IO_APICS +#else +# define DMAR_UNITS_SUPPORTED64 +#endif + /* DMAR Flags */ #define DMAR_INTR_REMAP0x1 #define DMAR_X2APIC_OPT_OUT0x2 -- 1.7.10
[Patch Part3 V1 19/22] iommu/vt-d: simplify intel_unmap_sg() and kill duplicated code
Simplify intel_unmap_sg() by calling intel_unmap_page() and kill duplicated code, no functionality changes. Signed-off-by: Jiang Liu --- drivers/iommu/intel-iommu.c | 63 +-- 1 file changed, 18 insertions(+), 45 deletions(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index f2143b59ad68..4738e64fe097 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -979,6 +979,8 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain, BUG_ON(addr_width < BITS_PER_LONG && last_pfn >> addr_width); BUG_ON(start_pfn > last_pfn); + dma_pte_clear_range(domain, start_pfn, last_pfn); + /* We don't need lock here; nobody else touches the iova range */ dma_pte_free_level(domain, agaw_to_level(domain->agaw), domain->pgd, 0, start_pfn, last_pfn); @@ -2031,12 +2033,14 @@ static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn, /* It is large page*/ if (largepage_lvl > 1) { pteval |= DMA_PTE_LARGE_PAGE; - /* Ensure that old small page tables are removed to make room - for superpage, if they exist. */ - dma_pte_clear_range(domain, iov_pfn, - iov_pfn + lvl_to_nr_pages(largepage_lvl) - 1); + lvl_pages = lvl_to_nr_pages(largepage_lvl); + /* +* Ensure that old small page tables are +* removed to make room for superpage, +* if they exist. +*/ dma_pte_free_pagetable(domain, iov_pfn, - iov_pfn + lvl_to_nr_pages(largepage_lvl) - 1); + iov_pfn + lvl_pages - 1); } else { pteval &= ~(uint64_t)DMA_PTE_LARGE_PAGE; } @@ -3256,43 +3260,17 @@ static void intel_unmap_sg(struct device *dev, struct scatterlist *sglist, int nelems, enum dma_data_direction dir, struct dma_attrs *attrs) { - struct dmar_domain *domain; - unsigned long start_pfn, last_pfn; - struct iova *iova; - struct intel_iommu *iommu; - struct page *freelist; - - if (iommu_no_mapping(dev)) - return; - - domain = find_domain(dev); - BUG_ON(!domain); - - iommu = domain_get_iommu(domain); - - iova = find_iova(&domain->iovad, IOVA_PFN(sglist[0].dma_address)); - if (WARN_ONCE(!iova, "Driver unmaps unmatched sglist at PFN %llx\n", - (unsigned long long)sglist[0].dma_address)) - return; - - start_pfn = mm_to_dma_pfn(iova->pfn_lo); - last_pfn = mm_to_dma_pfn(iova->pfn_hi + 1) - 1; + size_t size = 0; +#if 0 + /* current the third argument of intel_unmap_page is unsued */ + int i; + struct scatterlist *sg; - freelist = domain_unmap(domain, start_pfn, last_pfn); + for_each_sg(sglist, sg, nelems, i) + size += sg->length; +#endif - if (intel_iommu_strict) { - iommu_flush_iotlb_psi(iommu, domain->id, start_pfn, - last_pfn - start_pfn + 1, !freelist, 0); - /* free iova */ - __free_iova(&domain->iovad, iova); - dma_free_pagelist(freelist); - } else { - add_unmap(domain, iova, freelist); - /* -* queue up the release of the unmap to save the 1/6th of the -* cpu used up by the iotlb flush operation... -*/ - } + intel_unmap_page(dev, sglist[0].dma_address, size, dir, attrs); } static int intel_nontranslate_map_sg(struct device *hddev, @@ -3356,13 +3334,8 @@ static int intel_map_sg(struct device *dev, struct scatterlist *sglist, int nele ret = domain_sg_mapping(domain, start_vpfn, sglist, size, prot); if (unlikely(ret)) { - /* clear the page */ - dma_pte_clear_range(domain, start_vpfn, - start_vpfn + size - 1); - /* free page tables */ dma_pte_free_pagetable(domain, start_vpfn, start_vpfn + size - 1); - /* free iova */ __free_iova(&domain->iovad, iova); return 0; } -- 1.7.10.4 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[Patch Part3 V1 17/22] pci, ACPI, iommu: enhance pci_root to support DMAR device hotplug
Finally enhance pci_root driver to support DMAR device hotplug when hot-plugging PCI host bridges. Signed-off-by: Jiang Liu --- drivers/acpi/pci_root.c | 16 ++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index d388f13d48b4..aa8f549869f3 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include /* for acpi_hest_init() */ @@ -511,6 +512,7 @@ static int acpi_pci_root_add(struct acpi_device *device, struct acpi_pci_root *root; acpi_handle handle = device->handle; int no_aspm = 0, clear_aspm = 0; + bool hotadd = (system_state != SYSTEM_BOOTING); root = kzalloc(sizeof(struct acpi_pci_root), GFP_KERNEL); if (!root) @@ -557,6 +559,11 @@ static int acpi_pci_root_add(struct acpi_device *device, strcpy(acpi_device_class(device), ACPI_PCI_ROOT_CLASS); device->driver_data = root; + if (hotadd && dmar_device_hotplug(handle, true)) { + result = -ENXIO; + goto end; + } + pr_info(PREFIX "%s [%s] (domain %04x %pR)\n", acpi_device_name(device), acpi_device_bid(device), root->segment, &root->secondary); @@ -583,7 +590,7 @@ static int acpi_pci_root_add(struct acpi_device *device, root->segment, (unsigned int)root->secondary.start); device->driver_data = NULL; result = -ENODEV; - goto end; + goto remove_dmar; } if (clear_aspm) { @@ -597,7 +604,7 @@ static int acpi_pci_root_add(struct acpi_device *device, if (device->wakeup.flags.run_wake) device_set_run_wake(root->bus->bridge, true); - if (system_state != SYSTEM_BOOTING) { + if (hotadd) { pcibios_resource_survey_bus(root->bus); pci_assign_unassigned_root_bus_resources(root->bus); } @@ -607,6 +614,9 @@ static int acpi_pci_root_add(struct acpi_device *device, pci_unlock_rescan_remove(); return 1; +remove_dmar: + if (hotadd) + dmar_device_hotplug(handle, false); end: kfree(root); return result; @@ -625,6 +635,8 @@ static void acpi_pci_root_remove(struct acpi_device *device) pci_remove_root_bus(root->bus); + dmar_device_hotplug(device->handle, false); + pci_unlock_rescan_remove(); kfree(root); -- 1.7.10.4 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[Patch Part3 V1 20/22] iommu/vt-d: introduce helper domain_pfn_within_range() to simplify code
Introduce helper function domain_pfn_within_range() to simplify code and improve readability. Signed-off-by: Jiang Liu --- drivers/iommu/intel-iommu.c | 30 -- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 4738e64fe097..c4bca2d814c8 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -542,6 +542,14 @@ static inline int domain_type_is_vm_or_si(struct dmar_domain *domain) DOMAIN_FLAG_STATIC_IDENTITY); } +static inline int domain_pfn_supported(struct dmar_domain *domain, + unsigned long pfn) +{ + int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT; + + return !(addr_width < BITS_PER_LONG && pfn >> addr_width); +} + static int __iommu_calculate_agaw(struct intel_iommu *iommu, int max_gaw) { unsigned long sagaw; @@ -815,14 +823,13 @@ out: static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain, unsigned long pfn, int *target_level) { - int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT; struct dma_pte *parent, *pte = NULL; int level = agaw_to_level(domain->agaw); int offset; BUG_ON(!domain->pgd); - if (addr_width < BITS_PER_LONG && pfn >> addr_width) + if (!domain_pfn_supported(domain, pfn)) /* Address beyond IOMMU's addressing capabilities. */ return NULL; @@ -907,12 +914,11 @@ static void dma_pte_clear_range(struct dmar_domain *domain, unsigned long start_pfn, unsigned long last_pfn) { - int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT; unsigned int large_page = 1; struct dma_pte *first_pte, *pte; - BUG_ON(addr_width < BITS_PER_LONG && start_pfn >> addr_width); - BUG_ON(addr_width < BITS_PER_LONG && last_pfn >> addr_width); + BUG_ON(!domain_pfn_supported(domain, start_pfn)); + BUG_ON(!domain_pfn_supported(domain, last_pfn)); BUG_ON(start_pfn > last_pfn); /* we don't need lock here; nobody else touches the iova range */ @@ -973,10 +979,8 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain, unsigned long start_pfn, unsigned long last_pfn) { - int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT; - - BUG_ON(addr_width < BITS_PER_LONG && start_pfn >> addr_width); - BUG_ON(addr_width < BITS_PER_LONG && last_pfn >> addr_width); + BUG_ON(!domain_pfn_supported(domain, start_pfn)); + BUG_ON(!domain_pfn_supported(domain, last_pfn)); BUG_ON(start_pfn > last_pfn); dma_pte_clear_range(domain, start_pfn, last_pfn); @@ -1078,11 +1082,10 @@ struct page *domain_unmap(struct dmar_domain *domain, unsigned long start_pfn, unsigned long last_pfn) { - int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT; struct page *freelist = NULL; - BUG_ON(addr_width < BITS_PER_LONG && start_pfn >> addr_width); - BUG_ON(addr_width < BITS_PER_LONG && last_pfn >> addr_width); + BUG_ON(!domain_pfn_supported(domain, start_pfn)); + BUG_ON(!domain_pfn_supported(domain, last_pfn)); BUG_ON(start_pfn > last_pfn); /* we don't need lock here; nobody else touches the iova range */ @@ -1994,12 +1997,11 @@ static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn, { struct dma_pte *first_pte = NULL, *pte = NULL; phys_addr_t uninitialized_var(pteval); - int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT; unsigned long sg_res; unsigned int largepage_lvl = 0; unsigned long lvl_pages = 0; - BUG_ON(addr_width < BITS_PER_LONG && (iov_pfn + nr_pages - 1) >> addr_width); + BUG_ON(!domain_pfn_supported(domain, iov_pfn + nr_pages - 1)); if ((prot & (DMA_PTE_READ|DMA_PTE_WRITE)) == 0) return -EINVAL; -- 1.7.10.4 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[Patch Part3 V1 16/22] iommu/vt-d: enhance intel-iommu driver to support DMAR unit hotplug
Implement required callback functions for intel-iommu driver to support DMAR unit hotplug. Signed-off-by: Jiang Liu --- drivers/iommu/intel-iommu.c | 194 +++ 1 file changed, 139 insertions(+), 55 deletions(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 33e636c246a6..f2143b59ad68 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -1116,8 +1116,11 @@ static int iommu_alloc_root_entry(struct intel_iommu *iommu) unsigned long flags; root = (struct root_entry *)alloc_pgtable_page(iommu->node); - if (!root) + if (!root) { + pr_err("IOMMU: allocating root entry for %s failed\n", + iommu->name); return -ENOMEM; + } __iommu_flush_cache(iommu, root, ROOT_SIZE); @@ -1457,7 +1460,7 @@ static int iommu_init_domains(struct intel_iommu *iommu) return 0; } -static void free_dmar_iommu(struct intel_iommu *iommu) +static void disable_dmar_iommu(struct intel_iommu *iommu) { struct dmar_domain *domain; int i; @@ -1481,11 +1484,16 @@ static void free_dmar_iommu(struct intel_iommu *iommu) if (iommu->gcmd & DMA_GCMD_TE) iommu_disable_translation(iommu); +} - kfree(iommu->domains); - kfree(iommu->domain_ids); - iommu->domains = NULL; - iommu->domain_ids = NULL; +static void free_dmar_iommu(struct intel_iommu *iommu) +{ + if ((iommu->domains) && (iommu->domain_ids)) { + kfree(iommu->domains); + kfree(iommu->domain_ids); + iommu->domains = NULL; + iommu->domain_ids = NULL; + } g_iommus[iommu->seq_id] = NULL; @@ -2680,6 +2688,41 @@ static int __init iommu_prepare_static_identity_mapping(int hw) return 0; } +static void intel_iommu_init_qi(struct intel_iommu *iommu) +{ + /* +* Start from the sane iommu hardware state. +* If the queued invalidation is already initialized by us +* (for example, while enabling interrupt-remapping) then +* we got the things already rolling from a sane state. +*/ + if (!iommu->qi) { + /* +* Clear any previous faults. +*/ + dmar_fault(-1, iommu); + /* +* Disable queued invalidation if supported and already enabled +* before OS handover. +*/ + dmar_disable_qi(iommu); + } + + if (dmar_enable_qi(iommu)) { + /* +* Queued Invalidate not enabled, use Register Based Invalidate +*/ + iommu->flush.flush_context = __iommu_flush_context; + iommu->flush.flush_iotlb = __iommu_flush_iotlb; + pr_info("IOMMU: %s using Register based invalidation\n", + iommu->name); + } else { + iommu->flush.flush_context = qi_flush_context; + iommu->flush.flush_iotlb = qi_flush_iotlb; + pr_info("IOMMU: %s using Queued invalidation\n", iommu->name); + } +} + static int __init init_dmars(void) { struct dmar_drhd_unit *drhd; @@ -2708,6 +2751,10 @@ static int __init init_dmars(void) DMAR_UNITS_SUPPORTED); } + /* Preallocate enough resources for IOMMU hot-addition */ + if (g_num_of_iommus < DMAR_UNITS_SUPPORTED) + g_num_of_iommus = DMAR_UNITS_SUPPORTED; + g_iommus = kcalloc(g_num_of_iommus, sizeof(struct intel_iommu *), GFP_KERNEL); if (!g_iommus) { @@ -2736,58 +2783,14 @@ static int __init init_dmars(void) * among all IOMMU's. Need to Split it later. */ ret = iommu_alloc_root_entry(iommu); - if (ret) { - printk(KERN_ERR "IOMMU: allocate root entry failed\n"); + if (ret) goto free_iommu; - } if (!ecap_pass_through(iommu->ecap)) hw_pass_through = 0; } - /* -* Start from the sane iommu hardware state. -*/ - for_each_active_iommu(iommu, drhd) { - /* -* If the queued invalidation is already initialized by us -* (for example, while enabling interrupt-remapping) then -* we got the things already rolling from a sane state. -*/ - if (iommu->qi) - continue; - - /* -* Clear any previous faults. -*/ - dmar_fault(-1, iommu); - /* -* Disable queued invalidation if supported and already enabled -* before OS handover. -*/ - dmar_disable_qi(iommu); - } - -
[Patch Part3 V1 15/22] iommu/vt-d: enhance error recovery in function intel_enable_irq_remapping()
Enhance error recovery in function intel_enable_irq_remapping() by tearing down all created data structures. Signed-off-by: Jiang Liu --- drivers/iommu/intel_irq_remapping.c |8 +--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c index 9f1c86566452..f65b68d3de10 100644 --- a/drivers/iommu/intel_irq_remapping.c +++ b/drivers/iommu/intel_irq_remapping.c @@ -673,9 +673,11 @@ static int __init intel_enable_irq_remapping(void) return eim ? IRQ_REMAP_X2APIC_MODE : IRQ_REMAP_XAPIC_MODE; error: - /* -* handle error condition gracefully here! -*/ + for_each_iommu(iommu, drhd) + if (ecap_ir_support(iommu->ecap)) { + iommu_disable_irq_remapping(iommu); + intel_teardown_irq_remapping(iommu); + } if (x2apic_present) pr_warn("Failed to enable irq remapping. You are vulnerable to irq-injection attacks.\n"); -- 1.7.10.4 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[Patch Part3 V1 12/22] iommu/vt-d: implement DMAR unit hotplug framework
On Intel platforms, an IO Hub (PCI/PCIe host bridge) may contain DMAR units, so we need to support DMAR hotplug when supporting PCI host bridge hotplug on Intel platforms. According to Section 8.8 "Remapping Hardware Unit Hot Plug" in "Intel Virtualization Technology for Directed IO Architecture Specification Rev 2.2", ACPI BIOS should implement ACPI _DSM method under the ACPI object for the PCI host bridge to support DMAR hotplug. This patch introduces interfaces to parse ACPI _DSM method for DMAR unit hotplug. It also implements state machines for DMAR unit hot-addition and hot-removal. The PCI host bridge hotplug driver should call dmar_hotplug_hotplug() before scanning PCI devices connected for hot-addition and after destroying all PCI devices for hot-removal. Signed-off-by: Jiang Liu --- drivers/iommu/dmar.c| 257 +-- drivers/iommu/intel-iommu.c | 78 ++- drivers/iommu/intel_irq_remapping.c |5 + include/linux/dmar.h| 27 4 files changed, 353 insertions(+), 14 deletions(-) diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index d09ee0d55946..290b4bdcaa3a 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -74,7 +74,7 @@ static unsigned long dmar_seq_ids[BITS_TO_LONGS(DMAR_UNITS_SUPPORTED)]; static int alloc_iommu(struct dmar_drhd_unit *drhd); static void free_iommu(struct intel_iommu *iommu); -static void __init dmar_register_drhd_unit(struct dmar_drhd_unit *drhd) +static void dmar_register_drhd_unit(struct dmar_drhd_unit *drhd) { /* * add INCLUDE_ALL at the tail, so scan the list will find it at @@ -335,24 +335,45 @@ static struct notifier_block dmar_pci_bus_nb = { .priority = INT_MIN, }; +static struct dmar_drhd_unit * +dmar_find_dmaru(struct acpi_dmar_hardware_unit *drhd) +{ + struct dmar_drhd_unit *dmaru; + + list_for_each_entry_rcu(dmaru, &dmar_drhd_units, list) + if (dmaru->segment == drhd->segment && + dmaru->reg_base_addr == drhd->address) + return dmaru; + + return NULL; +} + /** * dmar_parse_one_drhd - parses exactly one DMA remapping hardware definition * structure which uniquely represent one DMA remapping hardware unit * present in the platform */ -static int __init -dmar_parse_one_drhd(struct acpi_dmar_header *header, void *arg) +static int dmar_parse_one_drhd(struct acpi_dmar_header *header, void *arg) { struct acpi_dmar_hardware_unit *drhd; struct dmar_drhd_unit *dmaru; int ret = 0; drhd = (struct acpi_dmar_hardware_unit *)header; - dmaru = kzalloc(sizeof(*dmaru), GFP_KERNEL); + dmaru = dmar_find_dmaru(drhd); + if (dmaru) + goto out; + + dmaru = kzalloc(sizeof(*dmaru) + header->length, GFP_KERNEL); if (!dmaru) return -ENOMEM; - dmaru->hdr = header; + /* +* If header is allocated from slab by ACPI _DSM method, we need to +* copy the content because the memory buffer will be freed on return. +*/ + dmaru->hdr = (void *)(dmaru + 1); + memcpy(dmaru->hdr, header, header->length); dmaru->reg_base_addr = drhd->address; dmaru->segment = drhd->segment; dmaru->include_all = drhd->flags & 0x1; /* BIT0: INCLUDE_ALL */ @@ -373,6 +394,7 @@ dmar_parse_one_drhd(struct acpi_dmar_header *header, void *arg) } dmar_register_drhd_unit(dmaru); +out: if (arg) (*(int *)arg)++; @@ -409,8 +431,7 @@ static int __init dmar_parse_one_andd(struct acpi_dmar_header *header, void *arg } #ifdef CONFIG_ACPI_NUMA -static int __init -dmar_parse_one_rhsa(struct acpi_dmar_header *header, void *arg) +static int dmar_parse_one_rhsa(struct acpi_dmar_header *header, void *arg) { struct acpi_dmar_rhsa *rhsa; struct dmar_drhd_unit *drhd; @@ -804,14 +825,22 @@ dmar_validate_one_drhd(struct acpi_dmar_header *entry, void *arg) return -EINVAL; } - addr = early_ioremap(drhd->address, VTD_PAGE_SIZE); + if (arg) + addr = ioremap(drhd->address, VTD_PAGE_SIZE); + else + addr = early_ioremap(drhd->address, VTD_PAGE_SIZE); if (!addr) { pr_warn("IOMMU: can't validate: %llx\n", drhd->address); return -EINVAL; } + cap = dmar_readq(addr + DMAR_CAP_REG); ecap = dmar_readq(addr + DMAR_ECAP_REG); - early_iounmap(addr, VTD_PAGE_SIZE); + + if (arg) + iounmap(addr); + else + early_iounmap(addr, VTD_PAGE_SIZE); if (cap == (uint64_t)-1 && ecap == (uint64_t)-1) { warn_invalid_dmar(drhd->address, " returns all ones"); @@ -1680,12 +1709,17 @@ int __init dmar_ir_support(void) return dmar->flags & 0x1; } +/* Check whether DMAR units are in use */ +static inline bool dmar_in_use(
[Patch Part3 V1 13/22] iommu/vt-d: search _DSM method for DMAR hotplug
According to Intel VT-d specification, _DSM method to support DMAR hotplug should exist directly under corresponding ACPI object representing PCI host bridge. But some BIOSes doesn't conform to this, so search for _DSM method in the subtree starting from the ACPI object representing the PCI host bridge. Signed-off-by: Jiang Liu --- drivers/iommu/dmar.c | 34 +++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index 290b4bdcaa3a..7a7383858c34 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -1920,20 +1920,48 @@ static int dmar_hotplug_remove(acpi_handle handle) return ret; } +static acpi_status dmar_get_dsm_handle(acpi_handle handle, u32 lvl, + void *context, void **retval) +{ + acpi_handle *phdl = retval; + + if (dmar_detect_dsm(handle, DMAR_DSM_FUNC_DRHD)) { + *phdl = handle; + return AE_CTRL_TERMINATE; + } + + return AE_OK; +} + int dmar_device_hotplug(acpi_handle handle, bool insert) { int ret; + acpi_handle tmp = NULL; + acpi_status status; if (!dmar_in_use()) return 0; - if (!dmar_detect_dsm(handle, DMAR_DSM_FUNC_DRHD)) + + if (dmar_detect_dsm(handle, DMAR_DSM_FUNC_DRHD)) { + tmp = handle; + } else { + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, +ACPI_UINT32_MAX, +dmar_get_dsm_handle, +NULL, NULL, &tmp); + if (ACPI_FAILURE(status)) { + pr_warn("Failed to locate _DSM method.\n"); + return -ENXIO; + } + } + if (tmp == NULL) return 0; down_write(&dmar_global_lock); if (insert) - ret = dmar_hotplug_insert(handle); + ret = dmar_hotplug_insert(tmp); else - ret = dmar_hotplug_remove(handle); + ret = dmar_hotplug_remove(tmp); up_write(&dmar_global_lock); return ret; -- 1.7.10.4 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[Patch Part3 V1 07/22] iommu/vt-d: avoid freeing virtual machine domain in free_dmar_iommu()
Virtual machine domains are created by intel_iommu_domain_init() and should be destroyed by intel_iommu_domain_destroy(). So avoid freeing virtual machine domain data structure in free_dmar_iommu() when doamin->iommu_count reaches zero, otherwise it may cause invalid memory access because the IOMMU framework still holds references to the domain structure. Signed-off-by: Jiang Liu --- drivers/iommu/intel-iommu.c |3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 1f10fcd1c696..5e865c1265ad 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -1482,7 +1482,8 @@ static void free_dmar_iommu(struct intel_iommu *iommu) domain = iommu->domains[i]; clear_bit(i, iommu->domain_ids); - if (domain_detach_iommu(domain, iommu) == 0) + if (domain_detach_iommu(domain, iommu) == 0 && + !domain_type_is_vm(domain)) domain_exit(domain); } } -- 1.7.10.4 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[Patch Part3 V1 09/22] iommu/vt-d: change iommu_enable/disable_translation to return void
Simplify error handling path by changing iommu_{enable|disable}_translation to return void. Signed-off-by: Jiang Liu --- drivers/iommu/intel-iommu.c | 18 +- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 5e865c1265ad..75d7c5e3f77d 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -1391,7 +1391,7 @@ static void iommu_disable_protect_mem_regions(struct intel_iommu *iommu) raw_spin_unlock_irqrestore(&iommu->register_lock, flags); } -static int iommu_enable_translation(struct intel_iommu *iommu) +static void iommu_enable_translation(struct intel_iommu *iommu) { u32 sts; unsigned long flags; @@ -1405,10 +1405,9 @@ static int iommu_enable_translation(struct intel_iommu *iommu) readl, (sts & DMA_GSTS_TES), sts); raw_spin_unlock_irqrestore(&iommu->register_lock, flags); - return 0; } -static int iommu_disable_translation(struct intel_iommu *iommu) +static void iommu_disable_translation(struct intel_iommu *iommu) { u32 sts; unsigned long flag; @@ -1422,7 +1421,6 @@ static int iommu_disable_translation(struct intel_iommu *iommu) readl, (!(sts & DMA_GSTS_TES)), sts); raw_spin_unlock_irqrestore(&iommu->register_lock, flag); - return 0; } @@ -2875,11 +2873,7 @@ static int __init init_dmars(void) iommu->flush.flush_context(iommu, 0, 0, 0, DMA_CCMD_GLOBAL_INVL); iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH); - - ret = iommu_enable_translation(iommu); - if (ret) - goto free_iommu; - + iommu_enable_translation(iommu); iommu_disable_protect_mem_regions(iommu); } @@ -3577,10 +3571,8 @@ static int init_iommu_hw(void) iommu->flush.flush_context(iommu, 0, 0, 0, DMA_CCMD_GLOBAL_INVL); - iommu->flush.flush_iotlb(iommu, 0, 0, 0, -DMA_TLB_GLOBAL_FLUSH); - if (iommu_enable_translation(iommu)) - return 1; + iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH); + iommu_enable_translation(iommu); iommu_disable_protect_mem_regions(iommu); } -- 1.7.10.4 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[Patch Part3 V1 08/22] iommu/VT-d: simplify include/linux/dmar.h
Simplify include/linux/dmar.h a bit based on the fact that both CONFIG_INTEL_IOMMU and CONFIG_IRQ_REMAP select CONFIG_DMAR_TABLE. Signed-off-by: Jiang Liu --- include/linux/dmar.h | 50 ++ 1 file changed, 18 insertions(+), 32 deletions(-) diff --git a/include/linux/dmar.h b/include/linux/dmar.h index 23c8db129560..1deece46a0ca 100644 --- a/include/linux/dmar.h +++ b/include/linux/dmar.h @@ -114,22 +114,30 @@ extern int dmar_remove_dev_scope(struct dmar_pci_notify_info *info, /* Intel IOMMU detection */ extern int detect_intel_iommu(void); extern int enable_drhd_fault_handling(void); -#else -struct dmar_pci_notify_info; -static inline int detect_intel_iommu(void) + +#ifdef CONFIG_INTEL_IOMMU +extern int iommu_detected, no_iommu; +extern int intel_iommu_init(void); +extern int dmar_parse_one_rmrr(struct acpi_dmar_header *header); +extern int dmar_parse_one_atsr(struct acpi_dmar_header *header); +extern int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info); +#else /* !CONFIG_INTEL_IOMMU: */ +static inline int intel_iommu_init(void) { return -ENODEV; } +static inline int dmar_parse_one_rmrr(struct acpi_dmar_header *header) { - return -ENODEV; + return 0; } - -static inline int dmar_table_init(void) +static inline int dmar_parse_one_atsr(struct acpi_dmar_header *header) { - return -ENODEV; + return 0; } -static inline int enable_drhd_fault_handling(void) +static inline int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info) { - return -1; + return 0; } -#endif /* !CONFIG_DMAR_TABLE */ +#endif /* CONFIG_INTEL_IOMMU */ + +#endif /* CONFIG_DMAR_TABLE */ struct irte { union { @@ -177,26 +185,4 @@ extern int dmar_set_interrupt(struct intel_iommu *iommu); extern irqreturn_t dmar_fault(int irq, void *dev_id); extern int arch_setup_dmar_msi(unsigned int irq); -#ifdef CONFIG_INTEL_IOMMU -extern int iommu_detected, no_iommu; -extern int dmar_parse_one_rmrr(struct acpi_dmar_header *header); -extern int dmar_parse_one_atsr(struct acpi_dmar_header *header); -extern int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info); -extern int intel_iommu_init(void); -#else /* !CONFIG_INTEL_IOMMU: */ -static inline int intel_iommu_init(void) { return -ENODEV; } -static inline int dmar_parse_one_rmrr(struct acpi_dmar_header *header) -{ - return 0; -} -static inline int dmar_parse_one_atsr(struct acpi_dmar_header *header) -{ - return 0; -} -static inline int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info) -{ - return 0; -} -#endif /* CONFIG_INTEL_IOMMU */ - #endif /* __DMAR_H__ */ -- 1.7.10.4 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[Patch Part3 V1 06/22] iommu/vt-d: fix possible invalid memory access caused by free_dmar_iommu()
Static identity and virtual machine domains may be cached in iommu->domain_ids array after corresponding IOMMUs have been removed from domain->iommu_bmp. So we should check domain->iommu_bmp before decreasing domain->iommu_count in function free_dmar_iommu(), otherwise it may cause free of inuse domain data structure. Signed-off-by: Jiang Liu --- drivers/iommu/intel-iommu.c | 11 --- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 0ab0cd236b16..1f10fcd1c696 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -425,6 +425,8 @@ static void domain_remove_one_dev_info(struct dmar_domain *domain, struct device *dev); static void iommu_detach_dependent_devices(struct intel_iommu *iommu, struct device *dev); +static int domain_detach_iommu(struct dmar_domain *domain, + struct intel_iommu *iommu); #ifdef CONFIG_INTEL_IOMMU_DEFAULT_ON int dmar_disabled = 0; @@ -1467,8 +1469,7 @@ static int iommu_init_domains(struct intel_iommu *iommu) static void free_dmar_iommu(struct intel_iommu *iommu) { struct dmar_domain *domain; - int i, count; - unsigned long flags; + int i; if ((iommu->domains) && (iommu->domain_ids)) { for_each_set_bit(i, iommu->domain_ids, cap_ndoms(iommu->cap)) { @@ -1481,11 +1482,7 @@ static void free_dmar_iommu(struct intel_iommu *iommu) domain = iommu->domains[i]; clear_bit(i, iommu->domain_ids); - - spin_lock_irqsave(&domain->iommu_lock, flags); - count = --domain->iommu_count; - spin_unlock_irqrestore(&domain->iommu_lock, flags); - if (count == 0) + if (domain_detach_iommu(domain, iommu) == 0) domain_exit(domain); } } -- 1.7.10.4 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[Patch Part3 V1 04/22] iommu/vt-d: introduce helper functions to make code symmetric for readability
Introduce domain_attach_iommu()/domain_detach_iommu() and refine iommu_attach_domain()/iommu_detach_domain() to make code symmetric and improve readability. Signed-off-by: Jiang Liu --- drivers/iommu/intel-iommu.c | 146 --- 1 file changed, 80 insertions(+), 66 deletions(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 63f351a39c63..29f613e7eea3 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -1525,32 +1525,39 @@ static struct dmar_domain *alloc_domain(int flags) return domain; } -static int iommu_attach_domain(struct dmar_domain *domain, - struct intel_iommu *iommu) +static int __iommu_attach_domain(struct dmar_domain *domain, +struct intel_iommu *iommu) { int num; unsigned long ndomains; - unsigned long flags; ndomains = cap_ndoms(iommu->cap); - - spin_lock_irqsave(&iommu->lock, flags); - num = find_first_zero_bit(iommu->domain_ids, ndomains); - if (num >= ndomains) { - spin_unlock_irqrestore(&iommu->lock, flags); - printk(KERN_ERR "IOMMU: no free domain ids\n"); - return -ENOMEM; + if (num < ndomains) { + set_bit(num, iommu->domain_ids); + iommu->domains[num] = domain; + } else { + num = -ENOSPC; } - domain->id = num; - domain->iommu_count++; - set_bit(num, iommu->domain_ids); - set_bit(iommu->seq_id, domain->iommu_bmp); - iommu->domains[num] = domain; + return num; +} + +static int iommu_attach_domain(struct dmar_domain *domain, + struct intel_iommu *iommu) +{ + int num; + unsigned long flags; + + spin_lock_irqsave(&iommu->lock, flags); + num = __iommu_attach_domain(domain, iommu); + if (num < 0) + pr_err("IOMMU: no free domain ids\n"); + else + domain->id = num; spin_unlock_irqrestore(&iommu->lock, flags); - return 0; + return num; } static void iommu_detach_domain(struct dmar_domain *domain, @@ -1560,17 +1567,53 @@ static void iommu_detach_domain(struct dmar_domain *domain, int num, ndomains; spin_lock_irqsave(&iommu->lock, flags); - ndomains = cap_ndoms(iommu->cap); - for_each_set_bit(num, iommu->domain_ids, ndomains) { - if (iommu->domains[num] == domain) { - clear_bit(num, iommu->domain_ids); - iommu->domains[num] = NULL; - break; + if (domain_type_is_vm_or_si(domain)) { + ndomains = cap_ndoms(iommu->cap); + for_each_set_bit(num, iommu->domain_ids, ndomains) { + if (iommu->domains[num] == domain) { + clear_bit(num, iommu->domain_ids); + iommu->domains[num] = NULL; + break; + } } + } else { + clear_bit(domain->id, iommu->domain_ids); + iommu->domains[domain->id] = NULL; } spin_unlock_irqrestore(&iommu->lock, flags); } +static void domain_attach_iommu(struct dmar_domain *domain, + struct intel_iommu *iommu) +{ + unsigned long flags; + + spin_lock_irqsave(&domain->iommu_lock, flags); + if (!test_and_set_bit(iommu->seq_id, domain->iommu_bmp)) { + domain->iommu_count++; + if (domain->iommu_count == 1) + domain->nid = iommu->node; + domain_update_iommu_cap(domain); + } + spin_unlock_irqrestore(&domain->iommu_lock, flags); +} + +static int domain_detach_iommu(struct dmar_domain *domain, + struct intel_iommu *iommu) +{ + unsigned long flags; + int count = INT_MAX; + + spin_lock_irqsave(&domain->iommu_lock, flags); + if (test_and_clear_bit(iommu->seq_id, domain->iommu_bmp)) { + count = --domain->iommu_count; + domain_update_iommu_cap(domain); + } + spin_unlock_irqrestore(&domain->iommu_lock, flags); + + return count; +} + static struct iova_domain reserved_iova_list; static struct lock_class_key reserved_rbtree_key; @@ -1708,9 +1751,7 @@ static void domain_exit(struct dmar_domain *domain) /* clear attached or cached domains */ rcu_read_lock(); for_each_active_iommu(iommu, drhd) - if (domain_type_is_vm(domain) || - test_bit(iommu->seq_id, domain->iommu_bmp)) - iommu_detach_domain(domain, iommu); + iommu_detach_domain(domain, iommu); rcu_read_unlock(); dma_free_pagelist(freelist); @@ -1764,16 +1805,12 @@ static int domain_context_mapping_one(struct dmar_
[Patch Part3 V1 05/22] iommu/vt-d: only dynamically allocate domain id for virtual domains
Check the same domain id is allocated for si_domain on each IOMMU, otherwise the IOTLB flush for si_domain will fail. Now the rules to allocate and manage domain id are: 1) For normal and static identity domains, domain id is allocated when creating domain structure. And this id will be written into context entry. 2) For virtual machine domain, a virtual id is allocated when creating domain. And when binding virtual machine domain to an iommu, a real domain id is allocated on demand and this domain id will be written into context entry. So domain->id for virtual machine domain may be different from the domain id written into context entry(used by hardware). Signed-off-by: Jiang Liu --- drivers/iommu/intel-iommu.c | 46 --- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 29f613e7eea3..0ab0cd236b16 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -1551,15 +1551,27 @@ static int iommu_attach_domain(struct dmar_domain *domain, spin_lock_irqsave(&iommu->lock, flags); num = __iommu_attach_domain(domain, iommu); + spin_unlock_irqrestore(&iommu->lock, flags); if (num < 0) pr_err("IOMMU: no free domain ids\n"); - else - domain->id = num; - spin_unlock_irqrestore(&iommu->lock, flags); return num; } +static int iommu_attach_vm_domain(struct dmar_domain *domain, + struct intel_iommu *iommu) +{ + int num; + unsigned long ndomains; + + ndomains = cap_ndoms(iommu->cap); + for_each_set_bit(num, iommu->domain_ids, ndomains) + if (iommu->domains[num] == domain) + return num; + + return __iommu_attach_domain(domain, iommu); +} + static void iommu_detach_domain(struct dmar_domain *domain, struct intel_iommu *iommu) { @@ -1766,8 +1778,6 @@ static int domain_context_mapping_one(struct dmar_domain *domain, struct context_entry *context; unsigned long flags; struct dma_pte *pgd; - unsigned long num; - unsigned long ndomains; int id; int agaw; struct device_domain_info *info = NULL; @@ -1792,20 +1802,8 @@ static int domain_context_mapping_one(struct dmar_domain *domain, pgd = domain->pgd; if (domain_type_is_vm_or_si(domain)) { - int found = 0; - - /* find an available domain id for this device in iommu */ - ndomains = cap_ndoms(iommu->cap); - for_each_set_bit(num, iommu->domain_ids, ndomains) { - if (iommu->domains[num] == domain) { - id = num; - found = 1; - break; - } - } - - if (found == 0) { - id = __iommu_attach_domain(domain, iommu); + if (domain_type_is_vm(domain)) { + id = iommu_attach_vm_domain(domain, iommu); if (id < 0) { spin_unlock_irqrestore(&iommu->lock, flags); pr_err("IOMMU: no free domain ids\n"); @@ -2281,7 +2279,8 @@ static struct dmar_domain *get_domain_for_dev(struct device *dev, int gaw) domain = alloc_domain(0); if (!domain) goto error; - if (iommu_attach_domain(domain, iommu) < 0) { + domain->id = iommu_attach_domain(domain, iommu); + if (domain->id < 0) { free_domain_mem(domain); domain = NULL; goto error; @@ -2442,6 +2441,7 @@ static int __init si_domain_init(int hw) struct dmar_drhd_unit *drhd; struct intel_iommu *iommu; int nid, ret = 0; + bool first = true; si_domain = alloc_domain(DOMAIN_FLAG_STATIC_IDENTITY); if (!si_domain) @@ -2452,6 +2452,12 @@ static int __init si_domain_init(int hw) if (ret < 0) { domain_exit(si_domain); return -EFAULT; + } else if (first) { + si_domain->id = ret; + first = false; + } else if (si_domain->id != ret) { + domain_exit(si_domain); + return -EFAULT; } domain_attach_iommu(si_domain, iommu); } -- 1.7.10.4 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[Patch Part3 V1 03/22] iommu/vt-d: introduce helper functions to improve code readability
Introduce domain_type_is_vm() and domain_type_is_vm_or_si() to improve code readability. Also kill useless macro DOMAIN_FLAG_P2P_MULTIPLE_DEVICES. Signed-off-by: Jiang Liu --- drivers/iommu/intel-iommu.c | 59 +++ 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 0b3e6981a2da..63f351a39c63 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -320,16 +320,13 @@ static inline int first_pte_in_page(struct dma_pte *pte) static struct dmar_domain *si_domain; static int hw_pass_through = 1; -/* devices under the same p2p bridge are owned in one domain */ -#define DOMAIN_FLAG_P2P_MULTIPLE_DEVICES (1 << 0) - /* domain represents a virtual machine, more than one devices * across iommus may be owned in one domain, e.g. kvm guest. */ -#define DOMAIN_FLAG_VIRTUAL_MACHINE(1 << 1) +#define DOMAIN_FLAG_VIRTUAL_MACHINE(1 << 0) /* si_domain contains mulitple devices */ -#define DOMAIN_FLAG_STATIC_IDENTITY(1 << 2) +#define DOMAIN_FLAG_STATIC_IDENTITY(1 << 1) /* define the limit of IOMMUs supported in each domain */ #ifdef CONFIG_X86 @@ -539,6 +536,16 @@ void free_iova_mem(struct iova *iova) kmem_cache_free(iommu_iova_cache, iova); } +static inline int domain_type_is_vm(struct dmar_domain *domain) +{ + return domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE; +} + +static inline int domain_type_is_vm_or_si(struct dmar_domain *domain) +{ + return domain->flags & (DOMAIN_FLAG_VIRTUAL_MACHINE | + DOMAIN_FLAG_STATIC_IDENTITY); +} static int __iommu_calculate_agaw(struct intel_iommu *iommu, int max_gaw) { @@ -579,9 +586,7 @@ static struct intel_iommu *domain_get_iommu(struct dmar_domain *domain) int iommu_id; /* si_domain and vm domain should not get here. */ - BUG_ON(domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE); - BUG_ON(domain->flags & DOMAIN_FLAG_STATIC_IDENTITY); - + BUG_ON(domain_type_is_vm_or_si(domain)); iommu_id = find_first_bit(domain->iommu_bmp, g_num_of_iommus); if (iommu_id < 0 || iommu_id >= g_num_of_iommus) return NULL; @@ -1499,7 +1504,7 @@ static void free_dmar_iommu(struct intel_iommu *iommu) free_context_table(iommu); } -static struct dmar_domain *alloc_domain(bool vm) +static struct dmar_domain *alloc_domain(int flags) { /* domain id for virtual machine, it won't be set in context */ static atomic_t vm_domid = ATOMIC_INIT(0); @@ -1509,16 +1514,13 @@ static struct dmar_domain *alloc_domain(bool vm) if (!domain) return NULL; + memset(domain, 0, sizeof(*domain)); domain->nid = -1; - domain->iommu_count = 0; - memset(domain->iommu_bmp, 0, sizeof(domain->iommu_bmp)); - domain->flags = 0; + domain->flags = flags; spin_lock_init(&domain->iommu_lock); INIT_LIST_HEAD(&domain->devices); - if (vm) { + if (flags & DOMAIN_FLAG_VIRTUAL_MACHINE) domain->id = atomic_inc_return(&vm_domid); - domain->flags = DOMAIN_FLAG_VIRTUAL_MACHINE; - } return domain; } @@ -1706,7 +1708,7 @@ static void domain_exit(struct dmar_domain *domain) /* clear attached or cached domains */ rcu_read_lock(); for_each_active_iommu(iommu, drhd) - if (domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE || + if (domain_type_is_vm(domain) || test_bit(iommu->seq_id, domain->iommu_bmp)) iommu_detach_domain(domain, iommu); rcu_read_unlock(); @@ -1748,8 +1750,7 @@ static int domain_context_mapping_one(struct dmar_domain *domain, id = domain->id; pgd = domain->pgd; - if (domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE || - domain->flags & DOMAIN_FLAG_STATIC_IDENTITY) { + if (domain_type_is_vm_or_si(domain)) { int found = 0; /* find an available domain id for this device in iommu */ @@ -2115,7 +2116,7 @@ static void domain_remove_dev_info(struct dmar_domain *domain) iommu_disable_dev_iotlb(info); iommu_detach_dev(info->iommu, info->bus, info->devfn); - if (domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE) { + if (domain_type_is_vm(domain)) { iommu_detach_dependent_devices(info->iommu, info->dev); /* clear this iommu in iommu_bmp, update iommu count * and capabilities @@ -2181,8 +2182,6 @@ static struct dmar_domain *dmar_insert_dev_info(struct intel_iommu *iommu, info->dev = dev; info->domain = domain; info->iommu = iommu; - if (!dev) - domain->flags |= DOMAIN_FLAG_P2P_MULTIPLE_DEVICES; spin_lock_irqsave(&device_domain_lock, flags); if (dev) @@ -2257,
[Patch Part3 V1 01/22] iommu/vt-d: match segment number when searching for dev_iotlb capable devices
For virtual machine and static identity domains, there may be devices from different PCI segments associated with the same domain. So function iommu_support_dev_iotlb() should also match PCI segment number (iommu unit) when searching for dev_iotlb capable devices. Signed-off-by: Jiang Liu --- drivers/iommu/intel-iommu.c |3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index f256ffc02e29..612fd441b225 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -1274,7 +1274,8 @@ iommu_support_dev_iotlb (struct dmar_domain *domain, struct intel_iommu *iommu, spin_lock_irqsave(&device_domain_lock, flags); list_for_each_entry(info, &domain->devices, link) - if (info->bus == bus && info->devfn == devfn) { + if (info->iommu == iommu && info->bus == bus && + info->devfn == devfn) { found = 1; break; } -- 1.7.10.4 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[Patch Part3 V1 02/22] iommu/vt-d: use correct domain id to flush virtual machine domains
For virtual machine domains, domain->id is a virtual id, and the real domain id written into context entry is dynamically allocated. So use the real domain id instead of domain->id when flushing iotlbs for virtual machine domains. Signed-off-by: Jiang Liu --- drivers/iommu/intel-iommu.c |2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 612fd441b225..0b3e6981a2da 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -1824,7 +1824,7 @@ static int domain_context_mapping_one(struct dmar_domain *domain, (((u16)bus) << 8) | devfn, DMA_CCMD_MASK_NOBIT, DMA_CCMD_DEVICE_INVL); - iommu->flush.flush_iotlb(iommu, domain->id, 0, 0, DMA_TLB_DSI_FLUSH); + iommu->flush.flush_iotlb(iommu, id, 0, 0, DMA_TLB_DSI_FLUSH); } else { iommu_flush_write_buffer(iommu); } -- 1.7.10.4 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu