Re: [PATCH v5 3/7] PCI: Introduce pci_real_dma_dev()
On Tue, Jan 21, 2020 at 06:37:47AM -0700, Jon Derrick wrote: > The current DMA alias implementation requires the aliased device be on > the same PCI bus as the requester ID. This introduces an arch-specific > mechanism to point to another PCI device when doing mapping and > PCI DMA alias search. The default case returns the actual device. > > CC: Christoph Hellwig > Signed-off-by: Jon Derrick Acked-by: Bjorn Helgaas Looks like a nice cleanup to me. Lorenzo, let me know if you want me to take this. > --- > arch/x86/pci/common.c | 10 ++ > drivers/pci/pci.c | 19 ++- > drivers/pci/search.c | 6 ++ > include/linux/pci.h | 1 + > 4 files changed, 35 insertions(+), 1 deletion(-) > > diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c > index 1e59df0..fe21a5c 100644 > --- a/arch/x86/pci/common.c > +++ b/arch/x86/pci/common.c > @@ -736,3 +736,13 @@ int pci_ext_cfg_avail(void) > else > return 0; > } > + > +#if IS_ENABLED(CONFIG_VMD) > +struct pci_dev *pci_real_dma_dev(struct pci_dev *dev) > +{ > + if (is_vmd(dev->bus)) > + return to_pci_sysdata(dev->bus)->vmd_dev; > + > + return dev; > +} > +#endif > diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c > index 581b177..36d24f2 100644 > --- a/drivers/pci/pci.c > +++ b/drivers/pci/pci.c > @@ -6048,7 +6048,9 @@ bool pci_devs_are_dma_aliases(struct pci_dev *dev1, > struct pci_dev *dev2) > return (dev1->dma_alias_mask && > test_bit(dev2->devfn, dev1->dma_alias_mask)) || > (dev2->dma_alias_mask && > - test_bit(dev1->devfn, dev2->dma_alias_mask)); > + test_bit(dev1->devfn, dev2->dma_alias_mask)) || > +pci_real_dma_dev(dev1) == dev2 || > +pci_real_dma_dev(dev2) == dev1; > } > > bool pci_device_is_present(struct pci_dev *pdev) > @@ -6072,6 +6074,21 @@ void pci_ignore_hotplug(struct pci_dev *dev) > } > EXPORT_SYMBOL_GPL(pci_ignore_hotplug); > > +/** > + * pci_real_dma_dev - Get PCI DMA device for PCI device > + * @dev: the PCI device that may have a PCI DMA alias > + * > + * Permits the platform to provide architecture-specific functionality to > + * devices needing to alias DMA to another PCI device on another PCI bus. If > + * the PCI device is on the same bus, it is recommended to use > + * pci_add_dma_alias(). This is the default implementation. Architecture > + * implementations can override this. > + */ > +struct pci_dev __weak *pci_real_dma_dev(struct pci_dev *dev) > +{ > + return dev; > +} > + > resource_size_t __weak pcibios_default_alignment(void) > { > return 0; > diff --git a/drivers/pci/search.c b/drivers/pci/search.c > index e4dbdef..2061672 100644 > --- a/drivers/pci/search.c > +++ b/drivers/pci/search.c > @@ -32,6 +32,12 @@ int pci_for_each_dma_alias(struct pci_dev *pdev, > struct pci_bus *bus; > int ret; > > + /* > + * The device may have an explicit alias requester ID for DMA where the > + * requester is on another PCI bus. > + */ > + pdev = pci_real_dma_dev(pdev); > ret = fn(pdev, pci_dev_id(pdev), data); > if (ret) > return ret; > diff --git a/include/linux/pci.h b/include/linux/pci.h > index 930fab2..3840a54 100644 > --- a/include/linux/pci.h > +++ b/include/linux/pci.h > @@ -1202,6 +1202,7 @@ u32 pcie_bandwidth_available(struct pci_dev *dev, > struct pci_dev **limiting_dev, > int pci_select_bars(struct pci_dev *dev, unsigned long flags); > bool pci_device_is_present(struct pci_dev *pdev); > void pci_ignore_hotplug(struct pci_dev *dev); > +struct pci_dev *pci_real_dma_dev(struct pci_dev *dev); > > int __printf(6, 7) pci_request_irq(struct pci_dev *dev, unsigned int nr, > irq_handler_t handler, irq_handler_t thread_fn, void *dev_id, > -- > 1.8.3.1 > ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [Patch v3 1/3] iommu: avoid unnecessary magazine allocations
On Wed, Jan 22, 2020 at 9:07 AM Robin Murphy wrote: > > On 21/01/2020 5:21 pm, Cong Wang wrote: > > On Tue, Jan 21, 2020 at 3:11 AM Robin Murphy wrote: > >> > >> On 18/12/2019 4:39 am, Cong Wang wrote: > >>> The IOVA cache algorithm implemented in IOMMU code does not > >>> exactly match the original algorithm described in the paper > >>> "Magazines and Vmem: Extending the Slab Allocator to Many > >>> CPUs and Arbitrary Resources". > >>> > >>> Particularly, it doesn't need to free the loaded empty magazine > >>> when trying to put it back to global depot. To make it work, we > >>> have to pre-allocate magazines in the depot and only recycle them > >>> when all of them are full. > >>> > >>> Before this patch, rcache->depot[] contains either full or > >>> freed entries, after this patch, it contains either full or > >>> empty (but allocated) entries. > >> > >> How much additional memory overhead does this impose (particularly on > >> systems that may have many domains mostly used for large, long-term > >> mappings)? I'm wary that trying to micro-optimise for the "churn network > >> packets as fast as possible" case may penalise every other case, > >> potentially quite badly. Lower-end embedded systems are using IOMMUs in > >> front of their GPUs, video codecs, etc. precisely because they *don't* > >> have much memory to spare (and thus need to scrape together large > >> buffers out of whatever pages they can find). > > > > The calculation is not complicated: 32 * 6 * 129 * 8 = 198144 bytes, > > which is roughly 192K, per domain. > > Theoretically. On many architectures, kmalloc(1032,...) is going to > consume rather more than 1032 bytes. Either way, it's rather a lot of > memory to waste in the many cases where it will never be used at all. If this is a concern, we can make IOVA_MAG_SIZE tunable in Kconfig. I myself want a larger IOVA_MAG_SIZE at least for experiments. You know, servers now have 100G+ memory, 192k is nearly nothing... > > >> But on the other hand, if we were to go down this route, then why is > >> there any dynamic allocation/freeing left at all? Once both the depot > >> and the rcaches are preallocated, then AFAICS it would make more sense > >> to rework the overflow case in __iova_rcache_insert() to just free the > >> IOVAs and swap the empty mag around rather than destroying and > >> recreating it entirely. > > > > It's due to the algorithm requires a swap(), which can't be done with > > statically allocated magzine. I had the same thought initially but gave it > > up quickly when realized this. > > I'm not sure I follow... we're replacing a "full magazine" pointer with > an "empty magazine" pointer regardless of where that empty magazine came > from. It would be trivial to preallocate an 'overflow' magazine for the > one remaining case of handling a full depot, although to be honest, at > that point it's probably most efficient to just free the pfns directly > from cpu_rcache->loaded while still under the percpu lock and be done > with it. I don't follow you either. I thought you are suggesting to completely get rid of dynamic memory allocations like: @@ -31,7 +31,7 @@ struct iova_cpu_rcache; struct iova_rcache { spinlock_t lock; unsigned long depot_size; - struct iova_magazine *depot[MAX_GLOBAL_MAGS]; + struct iova_magazine depot[MAX_GLOBAL_MAGS]; struct iova_cpu_rcache __percpu *cpu_rcaches; }; If it is so, I don't see how I can do swap() with pointers like cpu_rcache->prev. More importantly, this doesn't save any memory either for your embedded case. So I don't know why you want to bring it up. > > > If you are suggesting to change the algorithm, it is not a goal of this > > patchset. I do have plan to search for a better algorithm as the IOMMU > > performance still sucks (comparing to no IOMMU) after this patchset, > > but once again, I do not want to change it in this patchset. > > "Still sucks" is probably the most interesting thing here - the headline > number for the original patch series was that it reached about 98% of > bypass performance on Intel VT-d[1]. Sounds like it would be well worth > digging in to what's different about your system and/or workload. Just FYI: The latency is 10x/20x worse with IOMMU enabled on AMD servers here. (mlx5 driver for ethernet, if matters.) The throughput is roughly same. The patchset you linked only measures throughput. > > > (My ultimate goal is to find a spinlock-free algorithm, otherwise there is > > no way to make it close to no-IOMMU performance.) > > > >> > >> Perhaps there's a reasonable compromise wherein we don't preallocate, > >> but still 'free' empty magazines back to the depot, such that busy > >> domains will quickly reach a steady-state. In fact, having now dug up > >> the paper at this point of writing this reply, that appears to be what > >> fig. 3.1b describes anyway - I don't see any mention of preallocating > >> the depot. > > > > That paper missed a lot of things, it
Re: [Patch v3 2/3] iommu: optimize iova_magazine_free_pfns()
On Wed, Jan 22, 2020 at 9:34 AM Robin Murphy wrote: > Sorry, but without convincing evidence, this change just looks like > churn for the sake of it. The time I wasted on arguing with you isn't worth anything than the value this patch brings. So let's just drop it to save some time. Thanks. ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [Patch v3 2/3] iommu: optimize iova_magazine_free_pfns()
On 21/01/2020 5:29 pm, Cong Wang wrote: On Tue, Jan 21, 2020 at 1:52 AM Robin Murphy wrote: On 18/12/2019 4:39 am, Cong Wang wrote: If the magazine is empty, iova_magazine_free_pfns() should be a nop, however it misses the case of mag->size==0. So we should just call iova_magazine_empty(). This should reduce the contention on iovad->iova_rbtree_lock a little bit, not much at all. Have you measured that in any way? AFAICS the only time this can get called with a non-full magazine is in the CPU hotplug callback, where the impact of taking the rbtree lock and immediately releasing it seems unlikely to be significant on top of everything else involved in that operation. This patchset is only tested as a whole, it is not easy to deploy each to production and test it separately. Is there anything wrong to optimize a CPU hotplug path? :) And, it is called in alloc_iova_fast() too when, for example, over-cached. And if the IOVA space is consumed to the point that we've fallen back to that desperate last resort, what do you think the chances are that a significant number of percpu magazines will be *empty*? Also bear in mind that in that case we've already walked the rbtree once, so any notion of still being fast is long, long gone. As for CPU hotplug, it's a comparatively rare event involving all manner of system-wide synchronisation, and the "optimisation" of shaving a few dozen CPU cycles off at one point *if* things happen to line up correctly is taking a cup of water out of a lake. If the domain is busy at the time, then once again chances are the magazines aren't empty and having an extra check redundant with the loop condition simply adds (trivial, but nonzero) overhead to every call. And if the domain isn't busy, then the lock is unlikely to be contended anyway. Sorry, but without convincing evidence, this change just looks like churn for the sake of it. Robin. ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH v5 6/7] PCI: vmd: Stop overriding dma_map_ops
On Tue, Jan 21, 2020 at 06:37:50AM -0700, Jon Derrick wrote: > Devices on the VMD domain use the VMD endpoint's requester ID and have > been relying on the VMD endpoint's DMA operations. The problem with this > was that VMD domain devices would use the VMD endpoint's attributes when > doing DMA and IOMMU mapping. We can be smarter about this by only using > the VMD endpoint when mapping and providing the correct child device's > attributes during DMA operations. > > This patch removes the dma_map_ops redirect. > > Signed-off-by: Jon Derrick Looks good. Reviewed-by: Keith Busch ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH] svm/avic: iommu/amd: Flush IOMMU IRT after update all entries
On 20/03/19 09:14, Suthikulpanit, Suravee wrote: > When AVIC is enabled and the VM has discrete device assignment, > the interrupt remapping table (IRT) is used to keep track of which > destination APIC ID the IOMMU will inject the device interrput to. > > This means every time a vcpu is blocked or context-switched (i.e. > vcpu_blocking/unblocking() and vcpu_load/put()), the information > in IRT must be updated and the IOMMU IRT flush command must be > issued. > > The current implementation flushes IOMMU IRT every time an entry > is modified. If the assigned device has large number of interrupts > (hence large number of entries), this would add large amount of > overhead to vcpu context-switch. Instead, this can be optmized by > only flush IRT once per vcpu context-switch per device after all > IRT entries are modified. > > The function amd_iommu_update_ga() is refactored to only update > IRT entry, while the amd_iommu_sync_ga() is introduced to allow > IRT flushing to be done separately. > > Cc: Joerg Roedel > Cc: Radim Krčmář > Cc: Paolo Bonzini > Signed-off-by: Suravee Suthikulpanit > --- > arch/x86/kvm/svm.c| 35 ++- > drivers/iommu/amd_iommu.c | 20 +--- > include/linux/amd-iommu.h | 13 ++--- > 3 files changed, 61 insertions(+), 7 deletions(-) I found this patch in my inbox... I'd rather avoid allocating 8k of RAM per vCPU. Can you make it per-VM? Paolo > + /* > + * Bitmap used to store PCI devid to sync > + * AMD IOMMU interrupt remapping table > + */ > + unsigned long *avic_devid_sync_bitmap; > }; > > /* > @@ -1984,6 +1992,7 @@ static inline int > avic_update_iommu_vcpu_affinity(struct kvm_vcpu *vcpu, int cpu, bool r) > { > int ret = 0; > + int devid = 0; > unsigned long flags; > struct amd_svm_iommu_ir *ir; > struct vcpu_svm *svm = to_svm(vcpu); > @@ -2001,9 +2010,21 @@ avic_update_iommu_vcpu_affinity(struct kvm_vcpu *vcpu, > int cpu, bool r) > goto out; > > list_for_each_entry(ir, >ir_list, node) { > - ret = amd_iommu_update_ga(cpu, r, ir->data); > + ret = amd_iommu_update_ga(cpu, r, ir->data, ); > if (ret) > break; > + set_bit(devid, svm->avic_devid_sync_bitmap); > + } > + > + /* Sync AMD IOMMU interrupt remapping table changes for each device. */ > + devid = find_next_bit(svm->avic_devid_sync_bitmap, > + AVIC_DEVID_BITMAP_SIZE, 0); > + > + while (devid < AVIC_DEVID_BITMAP_SIZE) { > + clear_bit(devid, svm->avic_devid_sync_bitmap); > + ret = amd_iommu_sync_ga(devid); > + devid = find_next_bit(svm->avic_devid_sync_bitmap, > + AVIC_DEVID_BITMAP_SIZE, devid+1); > } > out: > spin_unlock_irqrestore(>ir_list_lock, flags); > @@ -2107,6 +2128,13 @@ static int avic_init_vcpu(struct vcpu_svm *svm) > INIT_LIST_HEAD(>ir_list); > spin_lock_init(>ir_list_lock); > > + svm->avic_devid_sync_bitmap = (void *)__get_free_pages( > + GFP_KERNEL | __GFP_ZERO, > + get_order(AVIC_DEVID_BITMAP_SIZE/8)); > + if (svm->avic_devid_sync_bitmap == NULL) > + ret = -ENOMEM; > + memset(svm->avic_devid_sync_bitmap, 0, AVIC_DEVID_BITMAP_SIZE/8); > + > return ret; > } > > @@ -2221,6 +2249,11 @@ static void svm_free_vcpu(struct kvm_vcpu *vcpu) > __free_pages(virt_to_page(svm->msrpm), MSRPM_ALLOC_ORDER); > __free_page(virt_to_page(svm->nested.hsave)); > __free_pages(virt_to_page(svm->nested.msrpm), MSRPM_ALLOC_ORDER); > + > + free_pages((unsigned long)svm->avic_devid_sync_bitmap, > +get_order(AVIC_DEVID_BITMAP_SIZE/8)); > + svm->avic_devid_sync_bitmap = NULL; > + > kvm_vcpu_uninit(vcpu); > kmem_cache_free(x86_fpu_cache, svm->vcpu.arch.guest_fpu); > kmem_cache_free(kvm_vcpu_cache, svm); > diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c > index 2a7b78bb98b4..637bcc9192e5 100644 > --- a/drivers/iommu/amd_iommu.c > +++ b/drivers/iommu/amd_iommu.c > @@ -4499,7 +4499,20 @@ int amd_iommu_create_irq_domain(struct amd_iommu > *iommu) > return 0; > } > > -int amd_iommu_update_ga(int cpu, bool is_run, void *data) > +int amd_iommu_sync_ga(int devid) > +{ > + struct amd_iommu *iommu = amd_iommu_rlookup_table[devid]; > + > + if (!iommu) > + return -ENODEV; > + > + iommu_flush_irt(iommu, devid); > + iommu_completion_wait(iommu); > + return 0; > +} > +EXPORT_SYMBOL(amd_iommu_sync_ga); > + > +int amd_iommu_update_ga(int cpu, bool is_run, void *data, int *id) > { > unsigned long flags; > struct amd_iommu *iommu; > @@ -4521,6 +4534,9 @@ int amd_iommu_update_ga(int cpu, bool is_run, void > *data) > if (!table) > return -ENODEV; > > +
[PATCH 1/1] iommu/amd: Remove the unnecessary assignment
From: Adrian Huang The assignment of the global variable 'iommu_detected' has been moved from amd_iommu_init_dma_ops() to amd_iommu_detect(), so this patch removes the assignment in amd_iommu_init_dma_ops(). Signed-off-by: Adrian Huang --- drivers/iommu/amd_iommu.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index bd25674ee4db..79f08c0a1f00 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -2297,7 +2297,6 @@ int __init amd_iommu_init_api(void) int __init amd_iommu_init_dma_ops(void) { swiotlb= (iommu_default_passthrough() || sme_me_mask) ? 1 : 0; - iommu_detected = 1; if (amd_iommu_unmap_flush) pr_info("IO/TLB flush on unmap enabled\n"); -- 2.17.1 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 0/3] iommu: Add Allwinner H6 IOMMU driver
Hi, Here's a series adding support for the IOMMU introduced in the Allwinner H6. The driver from Allwinner hints at more SoCs using it in the future (with more masters), so we can bet on that IOMMU becoming pretty much standard in new SoCs from Allwinner. One thing I wasn't really sure about was how to expose the statistics reported by the IOMMU PMU (TLB hit rates, latencies, and so on). The Allwinner driver exposes them through custom sysfs files, while they would be best represented through perf I guess? Anyway, I'm planning to support them later on. Let me know what you think, Maxime Maxime Ripard (3): dt-bindings: iommu: Add Allwinner H6 IOMMU bindings iommu: Add Allwinner H6 IOMMU driver arm64: dts: allwinner: h6: Add IOMMU Documentation/devicetree/bindings/iommu/allwinner,sun50i-h6-iommu.yaml | 61 - arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 11 +- drivers/iommu/Kconfig | 10 +- drivers/iommu/Makefile |1 +- drivers/iommu/sun50i-iommu.c | 1126 - 5 files changed, 1209 insertions(+) create mode 100644 Documentation/devicetree/bindings/iommu/allwinner,sun50i-h6-iommu.yaml create mode 100644 drivers/iommu/sun50i-iommu.c base-commit: e42617b825f8073569da76dc4510bfa019b1c35a -- git-series 0.9.1 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 3/3] arm64: dts: allwinner: h6: Add IOMMU
Now that we have a driver for the IOMMU, let's start using it. Signed-off-by: Maxime Ripard --- arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 11 +++ 1 file changed, 11 insertions(+) diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi index 29824081b43b..8608bcf1c52c 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi @@ -53,6 +53,7 @@ de: display-engine { compatible = "allwinner,sun50i-h6-display-engine"; allwinner,pipelines = <>; + iommus = < 0>; status = "disabled"; }; @@ -122,6 +123,7 @@ clock-names = "bus", "mod"; resets = <_clocks RST_MIXER0>; + iommus = < 0>; ports { #address-cells = <1>; @@ -345,6 +347,15 @@ #interrupt-cells = <3>; }; + iommu: iommu@30f { + compatible = "allwinner,sun50i-h6-iommu"; + reg = <0x030f 0x1>; + interrupts = ; + clocks = < CLK_BUS_IOMMU>; + resets = < RST_BUS_IOMMU>; + #iommu-cells = <1>; + }; + mmc0: mmc@402 { compatible = "allwinner,sun50i-h6-mmc", "allwinner,sun50i-a64-mmc"; -- git-series 0.9.1 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 2/3] iommu: Add Allwinner H6 IOMMU driver
The Allwinner H6 has introduced an IOMMU for a few DMA controllers, mostly video related: the display engine, the video decoders / encoders, the camera capture controller, etc. The design is pretty simple compared to other IOMMUs found in SoCs: there's a single instance, controlling all the masters, with a single address space. It also features a performance monitoring unit that allows to retrieve various informations (per-master and global TLB accesses, hits and misses, access latency, etc) that isn't supported at the moment. Signed-off-by: Maxime Ripard --- drivers/iommu/Kconfig| 10 +- drivers/iommu/Makefile |1 +- drivers/iommu/sun50i-iommu.c | 1126 +++- 3 files changed, 1137 insertions(+) create mode 100644 drivers/iommu/sun50i-iommu.c diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index 0b9d78a0f3ac..5cbfa6f282e2 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -289,6 +289,16 @@ config ROCKCHIP_IOMMU Say Y here if you are using a Rockchip SoC that includes an IOMMU device. +config SUN50I_IOMMU + bool "Allwinner H6 IOMMU Support" + depends on ARM || ARM64 + depends on ARCH_SUNXI + select ARM_DMA_USE_IOMMU + select IOMMU_API + select IOMMU_DMA + help + Support for the IOMMU introduced in the Allwinner H6 SoCs. + config TEGRA_IOMMU_GART bool "Tegra GART IOMMU Support" depends on ARCH_TEGRA_2x_SOC diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile index 97814cc861ea..43740a755786 100644 --- a/drivers/iommu/Makefile +++ b/drivers/iommu/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_MTK_IOMMU_V1) += mtk_iommu_v1.o obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.o obj-$(CONFIG_OMAP_IOMMU_DEBUG) += omap-iommu-debug.o obj-$(CONFIG_ROCKCHIP_IOMMU) += rockchip-iommu.o +obj-$(CONFIG_SUN50I_IOMMU) += sun50i-iommu.o obj-$(CONFIG_TEGRA_IOMMU_GART) += tegra-gart.o obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o diff --git a/drivers/iommu/sun50i-iommu.c b/drivers/iommu/sun50i-iommu.c new file mode 100644 index ..ffca92628006 --- /dev/null +++ b/drivers/iommu/sun50i-iommu.c @@ -0,0 +1,1126 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +// Copyright (C) 2016-2018, Allwinner Technology CO., LTD. +// Copyright (C) 2019-2020, Cerno + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IOMMU_RESET_REG0x010 +#define IOMMU_ENABLE_REG 0x020 +#define IOMMU_ENABLE_ENABLEBIT(0) + +#define IOMMU_BYPASS_REG 0x030 +#define IOMMU_AUTO_GATING_REG 0x040 +#define IOMMU_AUTO_GATING_ENABLE BIT(0) + +#define IOMMU_WBUF_CTRL_REG0x044 +#define IOMMU_OOO_CTRL_REG 0x048 +#define IOMMU_4KB_BDY_PRT_CTRL_REG 0x04c +#define IOMMU_TTB_REG 0x050 +#define IOMMU_TLB_ENABLE_REG 0x060 +#define IOMMU_TLB_PREFETCH_REG 0x070 +#define IOMMU_TLB_PREFETCH_MASTER_ENABLE(m)BIT(m) + +#define IOMMU_TLB_FLUSH_REG0x080 +#define IOMMU_TLB_FLUSH_PTW_CACHE BIT(17) +#define IOMMU_TLB_FLUSH_MACRO_TLB BIT(16) +#define IOMMU_TLB_FLUSH_MICRO_TLB(i) (BIT(i) & GENMASK(5, 0)) + +#define IOMMU_TLB_IVLD_ADDR_REG0x090 +#define IOMMU_TLB_IVLD_ADDR_MASK_REG 0x094 +#define IOMMU_TLB_IVLD_ENABLE_REG 0x098 +#define IOMMU_TLB_IVLD_ENABLE_ENABLE BIT(0) + +#define IOMMU_PC_IVLD_ADDR_REG 0x0a0 +#define IOMMU_PC_IVLD_ENABLE_REG 0x0a8 +#define IOMMU_PC_IVLD_ENABLE_ENABLEBIT(0) + +#define IOMMU_DM_AUT_CTRL_REG(d) (0x0b0 + ((d) / 2) * 4) +#define IOMMU_DM_AUT_CTRL_RD_UNAVAIL(d, m) (1 << (((d & 1) * 16) + ((m) * 2))) +#define IOMMU_DM_AUT_CTRL_RD_AVAIL(d, m) (0 << (((d & 1) * 16) + ((m) * 2))) +#define IOMMU_DM_AUT_CTRL_WR_UNAVAIL(d, m) (1 << (((d & 1) * 16) + ((m) * 2) + 1)) +#define IOMMU_DM_AUT_CTRL_WR_AVAIL(d, m) (0 << (((d & 1) * 16) + ((m) * 2) + 1)) + +#define IOMMU_DM_AUT_OVWT_REG 0x0d0 +#define IOMMU_INT_ENABLE_REG 0x100 +#define IOMMU_INT_CLR_REG 0x104 +#define IOMMU_INT_STA_REG 0x108 +#define IOMMU_INT_ERR_ADDR_REG(i) (0x110 + (i) * 4) +#define IOMMU_INT_ERR_ADDR_L1_REG 0x130 +#define IOMMU_INT_ERR_ADDR_L2_REG 0x134 +#define IOMMU_INT_ERR_DATA_REG(i) (0x150 + (i) * 4) +#define IOMMU_L1PG_INT_REG 0x0180 +#define IOMMU_L2PG_INT_REG 0x0184 + +#define IOMMU_INT_INVALID_L2PG BIT(17) +#define IOMMU_INT_INVALID_L1PG BIT(16) +#define IOMMU_INT_MASTER_PERMISSION(m) BIT(m) +#define IOMMU_INT_MASTER_MASK
[PATCH 1/3] dt-bindings: iommu: Add Allwinner H6 IOMMU bindings
The Allwinner H6 has introduced an IOMMU. Let's add a device tree binding for it. Signed-off-by: Maxime Ripard --- Documentation/devicetree/bindings/iommu/allwinner,sun50i-h6-iommu.yaml | 61 + 1 file changed, 61 insertions(+) create mode 100644 Documentation/devicetree/bindings/iommu/allwinner,sun50i-h6-iommu.yaml diff --git a/Documentation/devicetree/bindings/iommu/allwinner,sun50i-h6-iommu.yaml b/Documentation/devicetree/bindings/iommu/allwinner,sun50i-h6-iommu.yaml new file mode 100644 index ..5e125cf2a88b --- /dev/null +++ b/Documentation/devicetree/bindings/iommu/allwinner,sun50i-h6-iommu.yaml @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iommu/allwinner,sun50i-h6-iommu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Allwinner H6 IOMMU Device Tree Bindings + +maintainers: + - Chen-Yu Tsai + - Maxime Ripard + +properties: + "#iommu-cells": +const: 1 +description: + The content of the cell is the master ID. + + compatible: +const: allwinner,sun50i-h6-iommu + + reg: +maxItems: 1 + + interrupts: +maxItems: 1 + + clocks: +maxItems: 1 + + resets: +maxItems: 1 + +required: + - "#iommu-cells" + - compatible + - reg + - interrupts + - clocks + - resets + +additionalProperties: false + +examples: + - | + #include + #include + + #include + #include + + iommu: iommu@30f { + compatible = "allwinner,sun50i-h6-iommu"; + reg = <0x030f 0x1>; + interrupts = ; + clocks = < CLK_BUS_IOMMU>; + resets = < RST_BUS_IOMMU>; + #iommu-cells = <1>; + }; + +... -- git-series 0.9.1 ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 0/2] iommu/arm-smmu: Allow client devices to select direct mapping
This series allows drm devices to set a default identity mapping using iommu_request_dm_for_dev(). First patch is a cleanup to support other SoCs to call into QCOM specific implementation and preparation for second patch. Second patch sets the default identity domain for drm devices. Jordan Crouse (1): iommu/arm-smmu: Allow client devices to select direct mapping Sai Prakash Ranjan (1): iommu: arm-smmu-impl: Convert to a generic reset implementation drivers/iommu/arm-smmu-impl.c | 8 +++-- drivers/iommu/arm-smmu-qcom.c | 55 +-- drivers/iommu/arm-smmu.c | 3 ++ drivers/iommu/arm-smmu.h | 5 4 files changed, 65 insertions(+), 6 deletions(-) -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 2/2] iommu/arm-smmu: Allow client devices to select direct mapping
From: Jordan Crouse Some client devices want to directly map the IOMMU themselves instead of using the DMA domain. Allow those devices to opt in to direct mapping by way of a list of compatible strings. Signed-off-by: Jordan Crouse Co-developed-by: Sai Prakash Ranjan Signed-off-by: Sai Prakash Ranjan --- drivers/iommu/arm-smmu-qcom.c | 39 +++ drivers/iommu/arm-smmu.c | 3 +++ drivers/iommu/arm-smmu.h | 5 + 3 files changed, 47 insertions(+) diff --git a/drivers/iommu/arm-smmu-qcom.c b/drivers/iommu/arm-smmu-qcom.c index 64a4ab270ab7..ff746acd1c81 100644 --- a/drivers/iommu/arm-smmu-qcom.c +++ b/drivers/iommu/arm-smmu-qcom.c @@ -3,6 +3,7 @@ * Copyright (c) 2019, The Linux Foundation. All rights reserved. */ +#include #include #include "arm-smmu.h" @@ -11,6 +12,43 @@ struct qcom_smmu { struct arm_smmu_device smmu; }; +static const struct arm_smmu_client_match_data qcom_adreno = { + .direct_mapping = true, +}; + +static const struct arm_smmu_client_match_data qcom_mdss = { + .direct_mapping = true, +}; + +static const struct of_device_id qcom_smmu_client_of_match[] = { + { .compatible = "qcom,adreno", .data = _adreno }, + { .compatible = "qcom,mdp4", .data = _mdss }, + { .compatible = "qcom,mdss", .data = _mdss }, + { .compatible = "qcom,sc7180-mdss", .data = _mdss }, + { .compatible = "qcom,sdm845-mdss", .data = _mdss }, + {}, +}; + +static const struct arm_smmu_client_match_data * +qcom_smmu_client_data(struct device *dev) +{ + const struct of_device_id *match = + of_match_device(qcom_smmu_client_of_match, dev); + + return match ? match->data : NULL; +} + +static int qcom_smmu_request_domain(struct device *dev) +{ + const struct arm_smmu_client_match_data *client; + + client = qcom_smmu_client_data(dev); + if (client) + iommu_request_dm_for_dev(dev); + + return 0; +} + static int qcom_sdm845_smmu500_reset(struct arm_smmu_device *smmu) { int ret; @@ -41,6 +79,7 @@ static int qcom_smmu500_reset(struct arm_smmu_device *smmu) } static const struct arm_smmu_impl qcom_smmu_impl = { + .req_domain = qcom_smmu_request_domain, .reset = qcom_smmu500_reset, }; diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 16c4b87af42b..67dd9326247a 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -1448,6 +1448,9 @@ static int arm_smmu_add_device(struct device *dev) device_link_add(dev, smmu->dev, DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_SUPPLIER); + if (smmu->impl && smmu->impl->req_domain) + return smmu->impl->req_domain(dev); + return 0; out_cfg_free: diff --git a/drivers/iommu/arm-smmu.h b/drivers/iommu/arm-smmu.h index 8d1cd54d82a6..059dc9c39f64 100644 --- a/drivers/iommu/arm-smmu.h +++ b/drivers/iommu/arm-smmu.h @@ -244,6 +244,10 @@ enum arm_smmu_arch_version { ARM_SMMU_V2, }; +struct arm_smmu_client_match_data { + bool direct_mapping; +}; + enum arm_smmu_implementation { GENERIC_SMMU, ARM_MMU500, @@ -386,6 +390,7 @@ struct arm_smmu_impl { int (*init_context)(struct arm_smmu_domain *smmu_domain); void (*tlb_sync)(struct arm_smmu_device *smmu, int page, int sync, int status); + int (*req_domain)(struct device *dev); }; static inline void __iomem *arm_smmu_page(struct arm_smmu_device *smmu, int n) -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
[PATCH 1/2] iommu: arm-smmu-impl: Convert to a generic reset implementation
Currently the QCOM specific smmu reset implementation is very specific to SDM845 SoC and has a wait-for-safe logic which may not be required for other SoCs. So move the SDM845 specific logic to its specific reset function. Also add SC7180 SMMU compatible for calling into QCOM specific implementation. Signed-off-by: Sai Prakash Ranjan --- drivers/iommu/arm-smmu-impl.c | 8 +--- drivers/iommu/arm-smmu-qcom.c | 16 +--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/iommu/arm-smmu-impl.c b/drivers/iommu/arm-smmu-impl.c index 74d97a886e93..c75b9d957b70 100644 --- a/drivers/iommu/arm-smmu-impl.c +++ b/drivers/iommu/arm-smmu-impl.c @@ -150,6 +150,8 @@ static const struct arm_smmu_impl arm_mmu500_impl = { struct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu) { + const struct device_node *np = smmu->dev->of_node; + /* * We will inevitably have to combine model-specific implementation * quirks with platform-specific integration quirks, but everything @@ -166,11 +168,11 @@ struct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu) break; } - if (of_property_read_bool(smmu->dev->of_node, - "calxeda,smmu-secure-config-access")) + if (of_property_read_bool(np, "calxeda,smmu-secure-config-access")) smmu->impl = _impl; - if (of_device_is_compatible(smmu->dev->of_node, "qcom,sdm845-smmu-500")) + if (of_device_is_compatible(np, "qcom,sdm845-smmu-500") || + of_device_is_compatible(np, "qcom,sc7180-smmu-500")) return qcom_smmu_impl_init(smmu); return smmu; diff --git a/drivers/iommu/arm-smmu-qcom.c b/drivers/iommu/arm-smmu-qcom.c index 24c071c1d8b0..64a4ab270ab7 100644 --- a/drivers/iommu/arm-smmu-qcom.c +++ b/drivers/iommu/arm-smmu-qcom.c @@ -15,8 +15,6 @@ static int qcom_sdm845_smmu500_reset(struct arm_smmu_device *smmu) { int ret; - arm_mmu500_reset(smmu); - /* * To address performance degradation in non-real time clients, * such as USB and UFS, turn off wait-for-safe on sdm845 based boards, @@ -30,8 +28,20 @@ static int qcom_sdm845_smmu500_reset(struct arm_smmu_device *smmu) return ret; } +static int qcom_smmu500_reset(struct arm_smmu_device *smmu) +{ + const struct device_node *np = smmu->dev->of_node; + + arm_mmu500_reset(smmu); + + if (of_device_is_compatible(np, "qcom,sdm845-smmu-500")) + return qcom_sdm845_smmu500_reset(smmu); + + return 0; +} + static const struct arm_smmu_impl qcom_smmu_impl = { - .reset = qcom_sdm845_smmu500_reset, + .reset = qcom_smmu500_reset, }; struct arm_smmu_device *qcom_smmu_impl_init(struct arm_smmu_device *smmu) -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu
Re: [PATCH] iommu/arm-smmu-v3: Batch ATC invalidation commands
Hi Rob, On Mon, Jan 13, 2020 at 08:39:06AM -0600, Rob Herring wrote: > Similar to commit 2af2e72b18b4 ("iommu/arm-smmu-v3: Defer TLB > invalidation until ->iotlb_sync()"), build up a list of ATC invalidation > commands and submit them all at once to the command queue instead of > one-by-one. > > Cc: Jean-Philippe Brucker > Cc: Will Deacon > Cc: Robin Murphy > Cc: Joerg Roedel > Signed-off-by: Rob Herring > --- > drivers/iommu/arm-smmu-v3.c | 23 +++ > 1 file changed, 19 insertions(+), 4 deletions(-) > > diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c > index effe72eb89e7..e91b4a098215 100644 > --- a/drivers/iommu/arm-smmu-v3.c > +++ b/drivers/iommu/arm-smmu-v3.c > @@ -1919,10 +1919,11 @@ static int arm_smmu_atc_inv_master(struct > arm_smmu_master *master, > static int arm_smmu_atc_inv_domain(struct arm_smmu_domain *smmu_domain, > int ssid, unsigned long iova, size_t size) > { > - int ret = 0; > + int i, cmdn = 0; > unsigned long flags; > struct arm_smmu_cmdq_ent cmd; > struct arm_smmu_master *master; > + u64 cmds[CMDQ_BATCH_ENTRIES * CMDQ_ENT_DWORDS]; > > if (!(smmu_domain->smmu->features & ARM_SMMU_FEAT_ATS)) > return 0; > @@ -1947,11 +1948,25 @@ static int arm_smmu_atc_inv_domain(struct > arm_smmu_domain *smmu_domain, > arm_smmu_atc_inv_to_cmd(ssid, iova, size, ); > > spin_lock_irqsave(_domain->devices_lock, flags); > - list_for_each_entry(master, _domain->devices, domain_head) > - ret |= arm_smmu_atc_inv_master(master, ); It may be worth reworking arm_smmu_atc_inv_master() as well since it's now only called by arm_smmu_disable_ats() to invalidate the whole ATC. I don't think it requires batching (it's not on any fast path and num_sids will almost always be 1), but it doesn't need the cmd argument anymore. Thanks, Jean > + list_for_each_entry(master, _domain->devices, domain_head) { > + if (!master->ats_enabled) > + continue; > + > + for (i = 0; i < master->num_sids; i++) { > + if (cmdn == CMDQ_BATCH_ENTRIES) { > + arm_smmu_cmdq_issue_cmdlist(smmu_domain->smmu, > + cmds, cmdn, false); > + cmdn = 0; > + } > + > + cmd.atc.sid = master->sids[i]; > + arm_smmu_cmdq_build_cmd([cmdn * CMDQ_ENT_DWORDS], > ); > + cmdn++; > + } > + } > spin_unlock_irqrestore(_domain->devices_lock, flags); > > - return ret ? -ETIMEDOUT : 0; > + return arm_smmu_cmdq_issue_cmdlist(smmu_domain->smmu, cmds, cmdn, true); > } > > /* IO_PGTABLE API */ > -- > 2.20.1 > ___ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu