Re: [Intel-gfx] [PATCH 0/8] Convert the intel iommu driver to the dma-iommu api

2020-09-10 Thread Tom Murphy
On Thu, 10 Sep 2020 at 14:33, Tom Murphy  wrote:
>
> On Wed, 9 Sep 2020 at 13:56, Tvrtko Ursulin
>  wrote:
> >
> >
> > On 09/09/2020 10:16, Tvrtko Ursulin wrote:
> > > On 08/09/2020 23:43, Tom Murphy wrote:
> > >> On Tue, 8 Sep 2020 at 16:56, Tvrtko Ursulin
> > >>  wrote:
> > >>> On 08/09/2020 16:44, Logan Gunthorpe wrote:
> > >>>> On 2020-09-08 9:28 a.m., Tvrtko Ursulin wrote:
> > >>>>>>
> > >>>>>> diff --git a/drivers/gpu/drm/i915/i915_scatterlist.h
> > >>>>>> b/drivers/gpu/drm/i915/i915
> > >>>>>> index b7b59328cb76..9367ac801f0c 100644
> > >>>>>> --- a/drivers/gpu/drm/i915/i915_scatterlist.h
> > >>>>>> +++ b/drivers/gpu/drm/i915/i915_scatterlist.h
> > >>>>>> @@ -27,13 +27,19 @@ static __always_inline struct sgt_iter {
> > >>>>>> } __sgt_iter(struct scatterlist *sgl, bool dma) {
> > >>>>>>struct sgt_iter s = { .sgp = sgl };
> > >>>>>>
> > >>>>>> +   if (sgl && !sg_dma_len(s.sgp))
> > >>>>>
> > >>>>> I'd extend the condition to be, just to be safe:
> > >>>>>   if (dma && sgl && !sg_dma_len(s.sgp))
> > >>>>>
> > >>>>
> > >>>> Right, good catch, that's definitely necessary.
> > >>>>
> > >>>>>> +   s.sgp = NULL;
> > >>>>>> +
> > >>>>>>if (s.sgp) {
> > >>>>>>s.max = s.curr = s.sgp->offset;
> > >>>>>> -   s.max += s.sgp->length;
> > >>>>>> -   if (dma)
> > >>>>>> +
> > >>>>>> +   if (dma) {
> > >>>>>> +   s.max += sg_dma_len(s.sgp);
> > >>>>>>s.dma = sg_dma_address(s.sgp);
> > >>>>>> -   else
> > >>>>>> +   } else {
> > >>>>>> +   s.max += s.sgp->length;
> > >>>>>>s.pfn = page_to_pfn(sg_page(s.sgp));
> > >>>>>> +   }
> > >>>>>
> > >>>>> Otherwise has this been tested or alternatively how to test it?
> > >>>>> (How to
> > >>>>> repro the issue.)
> > >>>>
> > >>>> It has not been tested. To test it, you need Tom's patch set without
> > >>>> the
> > >>>> last "DO NOT MERGE" patch:
> > >>>>
> > >>>> https://lkml.kernel.org/lkml/20200907070035.ga25...@infradead.org/T/
> > >>>
> > >>> Tom, do you have a branch somewhere I could pull from? (Just being lazy
> > >>> about downloading a bunch of messages from the archives.)
> > >>
> > >> I don't unfortunately. I'm working locally with poor internet.
> > >>
> > >>>
> > >>> What GPU is in your Lenovo x1 carbon 5th generation and what
> > >>> graphical/desktop setup I need to repro?
> > >>
> > >>
> > >> Is this enough info?:
> > >>
> > >> $ lspci -vnn | grep VGA -A 12
> > >> 00:02.0 VGA compatible controller [0300]: Intel Corporation HD
> > >> Graphics 620 [8086:5916] (rev 02) (prog-if 00 [VGA controller])
> > >>  Subsystem: Lenovo ThinkPad X1 Carbon 5th Gen [17aa:224f]
> > >>  Flags: bus master, fast devsel, latency 0, IRQ 148
> > >>  Memory at eb00 (64-bit, non-prefetchable) [size=16M]
> > >>  Memory at 6000 (64-bit, prefetchable) [size=256M]
> > >>  I/O ports at e000 [size=64]
> > >>  [virtual] Expansion ROM at 000c [disabled] [size=128K]
> > >>  Capabilities: [40] Vendor Specific Information: Len=0c 
> > >>  Capabilities: [70] Express Root Complex Integrated Endpoint, MSI 00
> > >>  Capabilities: [ac] MSI: Enable+ Count=1/1 Maskable- 64bit-
> > >>  Capabilities: [d0] Power Management version 2
> > >>  Capabilities: [100] Process Address Space ID (PASID)
> > >>  Capabilities: [200] Address Translation Service (ATS)
> > >
> > > Works for a

Re: [Intel-gfx] [PATCH 0/8] Convert the intel iommu driver to the dma-iommu api

2020-09-10 Thread Tom Murphy
On Wed, 9 Sep 2020 at 13:56, Tvrtko Ursulin
 wrote:
>
>
> On 09/09/2020 10:16, Tvrtko Ursulin wrote:
> > On 08/09/2020 23:43, Tom Murphy wrote:
> >> On Tue, 8 Sep 2020 at 16:56, Tvrtko Ursulin
> >>  wrote:
> >>> On 08/09/2020 16:44, Logan Gunthorpe wrote:
> >>>> On 2020-09-08 9:28 a.m., Tvrtko Ursulin wrote:
> >>>>>>
> >>>>>> diff --git a/drivers/gpu/drm/i915/i915_scatterlist.h
> >>>>>> b/drivers/gpu/drm/i915/i915
> >>>>>> index b7b59328cb76..9367ac801f0c 100644
> >>>>>> --- a/drivers/gpu/drm/i915/i915_scatterlist.h
> >>>>>> +++ b/drivers/gpu/drm/i915/i915_scatterlist.h
> >>>>>> @@ -27,13 +27,19 @@ static __always_inline struct sgt_iter {
> >>>>>> } __sgt_iter(struct scatterlist *sgl, bool dma) {
> >>>>>>struct sgt_iter s = { .sgp = sgl };
> >>>>>>
> >>>>>> +   if (sgl && !sg_dma_len(s.sgp))
> >>>>>
> >>>>> I'd extend the condition to be, just to be safe:
> >>>>>   if (dma && sgl && !sg_dma_len(s.sgp))
> >>>>>
> >>>>
> >>>> Right, good catch, that's definitely necessary.
> >>>>
> >>>>>> +   s.sgp = NULL;
> >>>>>> +
> >>>>>>if (s.sgp) {
> >>>>>>s.max = s.curr = s.sgp->offset;
> >>>>>> -   s.max += s.sgp->length;
> >>>>>> -   if (dma)
> >>>>>> +
> >>>>>> +   if (dma) {
> >>>>>> +   s.max += sg_dma_len(s.sgp);
> >>>>>>s.dma = sg_dma_address(s.sgp);
> >>>>>> -   else
> >>>>>> +   } else {
> >>>>>> +   s.max += s.sgp->length;
> >>>>>>s.pfn = page_to_pfn(sg_page(s.sgp));
> >>>>>> +   }
> >>>>>
> >>>>> Otherwise has this been tested or alternatively how to test it?
> >>>>> (How to
> >>>>> repro the issue.)
> >>>>
> >>>> It has not been tested. To test it, you need Tom's patch set without
> >>>> the
> >>>> last "DO NOT MERGE" patch:
> >>>>
> >>>> https://lkml.kernel.org/lkml/20200907070035.ga25...@infradead.org/T/
> >>>
> >>> Tom, do you have a branch somewhere I could pull from? (Just being lazy
> >>> about downloading a bunch of messages from the archives.)
> >>
> >> I don't unfortunately. I'm working locally with poor internet.
> >>
> >>>
> >>> What GPU is in your Lenovo x1 carbon 5th generation and what
> >>> graphical/desktop setup I need to repro?
> >>
> >>
> >> Is this enough info?:
> >>
> >> $ lspci -vnn | grep VGA -A 12
> >> 00:02.0 VGA compatible controller [0300]: Intel Corporation HD
> >> Graphics 620 [8086:5916] (rev 02) (prog-if 00 [VGA controller])
> >>  Subsystem: Lenovo ThinkPad X1 Carbon 5th Gen [17aa:224f]
> >>  Flags: bus master, fast devsel, latency 0, IRQ 148
> >>  Memory at eb00 (64-bit, non-prefetchable) [size=16M]
> >>  Memory at 6000 (64-bit, prefetchable) [size=256M]
> >>  I/O ports at e000 [size=64]
> >>  [virtual] Expansion ROM at 000c [disabled] [size=128K]
> >>  Capabilities: [40] Vendor Specific Information: Len=0c 
> >>  Capabilities: [70] Express Root Complex Integrated Endpoint, MSI 00
> >>  Capabilities: [ac] MSI: Enable+ Count=1/1 Maskable- 64bit-
> >>  Capabilities: [d0] Power Management version 2
> >>  Capabilities: [100] Process Address Space ID (PASID)
> >>  Capabilities: [200] Address Translation Service (ATS)
> >
> > Works for a start. What about the steps to repro? Any desktop
> > environment and it is just visual corruption, no hangs/stalls or such?
> >
> > I've submitted a series consisting of what I understood are the patches
> > needed to repro the issue to our automated CI here:
> >
> > https://patchwork.freedesktop.org/series/81489/
> >
> > So will see if it will catch something, or more targeted testing will be
> > required. Hopefully it does trip over in which case I can add the patch
> > suggested by Logan on top and see if that fixes it. Or I'll need to
> > write a new test case.
> >
> > If you could glance over my series to check I identified the patches
> > correctly it would be appreciated.
>
> Our CI was more than capable at catching the breakage so I've copied you
> on a patch (https://patchwork.freedesktop.org/series/81497/) which has a
> good potential to fix this. (Or improve the robustness of our sg walks,
> depends how you look at it.)
>
> Would you be able to test it in your environment by any chance? If it
> works I understand it unblocks your IOMMU work, right?

I tested your latest patch set ([PATCH 1/2] drm/i915: Fix DMA mapped
scatterlist walks) and it fixes the issue. great work!

>
> Regards,
>
> Tvrtko
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH] Handle init_iova_flush_queue failure in dma-iommu path

2020-09-10 Thread Tom Murphy
init_iova_flush_queue can fail if we run out of memory. Fall back to noflush
 queue if it fails.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/dma-iommu.c | 7 +--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 4959f5df21bd..5f69126f3e91 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -343,8 +343,11 @@ static int iommu_dma_init_domain(struct iommu_domain 
*domain, dma_addr_t base,
 
if (!cookie->fq_domain && !iommu_domain_get_attr(domain,
DOMAIN_ATTR_DMA_USE_FLUSH_QUEUE, ) && attr) {
-   cookie->fq_domain = domain;
-   init_iova_flush_queue(iovad, iommu_dma_flush_iotlb_all, NULL);
+   if (init_iova_flush_queue(iovad, iommu_dma_flush_iotlb_all,
+   NULL))
+   pr_warn("iova flush queue initialization failed\n");
+   else
+   cookie->fq_domain = domain;
}
 
if (!dev)
-- 
2.20.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [Intel-gfx] [PATCH 0/8] Convert the intel iommu driver to the dma-iommu api

2020-09-08 Thread Tom Murphy
On Tue, 8 Sep 2020 at 16:56, Tvrtko Ursulin
 wrote:
>
>
> On 08/09/2020 16:44, Logan Gunthorpe wrote:
> > On 2020-09-08 9:28 a.m., Tvrtko Ursulin wrote:
> >>>
> >>> diff --git a/drivers/gpu/drm/i915/i915_scatterlist.h
> >>> b/drivers/gpu/drm/i915/i915
> >>> index b7b59328cb76..9367ac801f0c 100644
> >>> --- a/drivers/gpu/drm/i915/i915_scatterlist.h
> >>> +++ b/drivers/gpu/drm/i915/i915_scatterlist.h
> >>> @@ -27,13 +27,19 @@ static __always_inline struct sgt_iter {
> >>>} __sgt_iter(struct scatterlist *sgl, bool dma) {
> >>>   struct sgt_iter s = { .sgp = sgl };
> >>>
> >>> +   if (sgl && !sg_dma_len(s.sgp))
> >>
> >> I'd extend the condition to be, just to be safe:
> >>  if (dma && sgl && !sg_dma_len(s.sgp))
> >>
> >
> > Right, good catch, that's definitely necessary.
> >
> >>> +   s.sgp = NULL;
> >>> +
> >>>   if (s.sgp) {
> >>>   s.max = s.curr = s.sgp->offset;
> >>> -   s.max += s.sgp->length;
> >>> -   if (dma)
> >>> +
> >>> +   if (dma) {
> >>> +   s.max += sg_dma_len(s.sgp);
> >>>   s.dma = sg_dma_address(s.sgp);
> >>> -   else
> >>> +   } else {
> >>> +   s.max += s.sgp->length;
> >>>   s.pfn = page_to_pfn(sg_page(s.sgp));
> >>> +   }
> >>
> >> Otherwise has this been tested or alternatively how to test it? (How to
> >> repro the issue.)
> >
> > It has not been tested. To test it, you need Tom's patch set without the
> > last "DO NOT MERGE" patch:
> >
> > https://lkml.kernel.org/lkml/20200907070035.ga25...@infradead.org/T/
>
> Tom, do you have a branch somewhere I could pull from? (Just being lazy
> about downloading a bunch of messages from the archives.)

I don't unfortunately. I'm working locally with poor internet.

>
> What GPU is in your Lenovo x1 carbon 5th generation and what
> graphical/desktop setup I need to repro?


Is this enough info?:

$ lspci -vnn | grep VGA -A 12
00:02.0 VGA compatible controller [0300]: Intel Corporation HD
Graphics 620 [8086:5916] (rev 02) (prog-if 00 [VGA controller])
Subsystem: Lenovo ThinkPad X1 Carbon 5th Gen [17aa:224f]
Flags: bus master, fast devsel, latency 0, IRQ 148
Memory at eb00 (64-bit, non-prefetchable) [size=16M]
Memory at 6000 (64-bit, prefetchable) [size=256M]
I/O ports at e000 [size=64]
[virtual] Expansion ROM at 000c [disabled] [size=128K]
Capabilities: [40] Vendor Specific Information: Len=0c 
Capabilities: [70] Express Root Complex Integrated Endpoint, MSI 00
Capabilities: [ac] MSI: Enable+ Count=1/1 Maskable- 64bit-
Capabilities: [d0] Power Management version 2
Capabilities: [100] Process Address Space ID (PASID)
Capabilities: [200] Address Translation Service (ATS)


>
> Regards,
>
> Tvrtko
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH V2 5/5] DO NOT MERGE: iommu: disable list appending in dma-iommu

2020-09-07 Thread Tom Murphy
On Mon, 7 Sep 2020 at 08:00, Christoph Hellwig  wrote:
>
> On Thu, Sep 03, 2020 at 09:18:37PM +0100, Tom Murphy wrote:
> > Disable combining sg segments in the dma-iommu api.
> > Combining the sg segments exposes a bug in the intel i915 driver which
> > causes visual artifacts and the screen to freeze. This is most likely
> > because of how the i915 handles the returned list. It probably doesn't
> > respect the returned value specifying the number of elements in the list
> > and instead depends on the previous behaviour of the intel iommu driver
> > which would return the same number of elements in the output list as in
> > the input list.
>
> So what is the state of addressing this properly in i915?  IF we can't

I think this is the latest on addressing this issue:
https://patchwork.kernel.org/cover/11306999/

tl;dr: some people seem to be looking at it but I'm not sure if it's
being actively worked on

> get it done ASAP I wonder if we need a runtime quirk to disable
> merging instead of blocking this conversion..

Yeah we talked about passing an attr to map_sg to disable merging at
the following microconfernce:
https://linuxplumbersconf.org/event/7/contributions/846/
As far as I can remember everyone seemed happy with that solution. I
won't be working on this though as I don't have any more time to
dedicate to this. It seems Lu Baolu will take over this.
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH V2 3/5] iommu: allow the dma-iommu api to use bounce buffers

2020-09-03 Thread Tom Murphy
Allow the dma-iommu api to use bounce buffers for untrusted devices.
This is a copy of the intel bounce buffer code.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/dma-iommu.c   | 94 ++---
 drivers/iommu/intel/iommu.c |  6 +++
 drivers/iommu/iommu.c   | 10 
 include/linux/iommu.h   |  7 +++
 4 files changed, 101 insertions(+), 16 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 33f3f4f5edc5..185cd504ca5a 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -21,9 +21,11 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
+#include 
 
 struct iommu_dma_msi_page {
struct list_headlist;
@@ -498,26 +500,87 @@ static void __iommu_dma_unmap(struct device *dev, 
dma_addr_t dma_addr,
iommu_dma_free_iova(cookie, dma_addr, size, iotlb_gather.freelist);
 }
 
+static void __iommu_dma_unmap_swiotlb(struct device *dev, dma_addr_t dma_addr,
+   size_t size, enum dma_data_direction dir,
+   unsigned long attrs)
+{
+   struct iommu_domain *domain = iommu_get_dma_domain(dev);
+   struct iommu_dma_cookie *cookie = domain->iova_cookie;
+   struct iova_domain *iovad = >iovad;
+   size_t iova_off = iova_offset(iovad, dma_addr);
+   size_t aligned_size = iova_align(iovad, size + iova_off);
+   phys_addr_t phys;
+
+   phys = iommu_iova_to_phys(domain, dma_addr);
+   if (WARN_ON(!phys))
+   return;
+
+   __iommu_dma_unmap(dev, dma_addr, size);
+
+#ifdef CONFIG_SWIOTLB
+   if (unlikely(is_swiotlb_buffer(phys)))
+   swiotlb_tbl_unmap_single(dev, phys, size,
+   aligned_size, dir, attrs);
+#endif
+}
+
 static dma_addr_t __iommu_dma_map(struct device *dev, phys_addr_t phys,
-   size_t size, int prot, u64 dma_mask)
+   size_t org_size, dma_addr_t dma_mask, bool coherent,
+   enum dma_data_direction dir, unsigned long attrs)
 {
+   int prot = dma_info_to_prot(dir, coherent, attrs);
struct iommu_domain *domain = iommu_get_dma_domain(dev);
struct iommu_dma_cookie *cookie = domain->iova_cookie;
struct iova_domain *iovad = >iovad;
size_t iova_off = iova_offset(iovad, phys);
+   size_t aligned_size = iova_align(iovad, org_size + iova_off);
+   void *padding_start;
+   size_t padding_size;
dma_addr_t iova;
 
if (unlikely(iommu_dma_deferred_attach(dev, domain)))
return DMA_MAPPING_ERROR;
 
-   size = iova_align(iovad, size + iova_off);
+#ifdef CONFIG_SWIOTLB
+   /*
+* If both the physical buffer start address and size are
+* page aligned, we don't need to use a bounce page.
+*/
+   if (iommu_needs_bounce_buffer(dev)
+   && !iova_offset(iovad, phys | org_size)) {
+   phys = swiotlb_tbl_map_single(dev,
+   __phys_to_dma(dev, io_tlb_start),
+   phys, org_size, aligned_size, dir, attrs);
+
+   if (phys == DMA_MAPPING_ERROR)
+   return DMA_MAPPING_ERROR;
+
+   /* Cleanup the padding area. */
+   padding_start = phys_to_virt(phys);
+   padding_size = aligned_size;
+
+   if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
+   (dir == DMA_TO_DEVICE ||
+dir == DMA_BIDIRECTIONAL)) {
+   padding_start += org_size;
+   padding_size -= org_size;
+   }
 
-   iova = iommu_dma_alloc_iova(domain, size, dma_mask, dev);
+   memset(padding_start, 0, padding_size);
+   }
+#endif
+
+   iova = iommu_dma_alloc_iova(domain, aligned_size, dma_mask, dev);
if (!iova)
return DMA_MAPPING_ERROR;
 
-   if (iommu_map_atomic(domain, iova, phys - iova_off, size, prot)) {
-   iommu_dma_free_iova(cookie, iova, size, NULL);
+   if (iommu_map_atomic(domain, iova, phys - iova_off, aligned_size,
+   prot)) {
+
+   if (unlikely(is_swiotlb_buffer(phys)))
+   swiotlb_tbl_unmap_single(dev, phys, aligned_size,
+   aligned_size, dir, attrs);
+   iommu_dma_free_iova(cookie, iova, aligned_size, NULL);
return DMA_MAPPING_ERROR;
}
return iova + iova_off;
@@ -751,10 +814,10 @@ static dma_addr_t iommu_dma_map_page(struct device *dev, 
struct page *page,
 {
phys_addr_t phys = page_to_phys(page) + offset;
bool coherent = dev_is_dma_coherent(dev);
-   int prot = dma_info_to_prot(dir, coherent, attrs);
dma_addr_t dma_handle;
 
-   dma_handle = __iommu_dma_map(dev, phys, size, prot, dma_get_mask(dev));
+   dma_handle = __iommu_dma_map(dev, phys, size, dma_get_mask(dev),

[PATCH V2 2/5] iommu: Add iommu_dma_free_cpu_cached_iovas function

2020-09-03 Thread Tom Murphy
to dma-iommu ops

Add a iommu_dma_free_cpu_cached_iovas function to allow drivers which
use the dma-iommu ops to free cached cpu iovas.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/dma-iommu.c | 9 +
 include/linux/dma-iommu.h | 3 +++
 2 files changed, 12 insertions(+)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index f69dc9467d71..33f3f4f5edc5 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -50,6 +50,15 @@ struct iommu_dma_cookie {
struct iommu_domain *fq_domain;
 };
 
+void iommu_dma_free_cpu_cached_iovas(unsigned int cpu,
+   struct iommu_domain *domain)
+{
+   struct iommu_dma_cookie *cookie = domain->iova_cookie;
+   struct iova_domain *iovad = >iovad;
+
+   free_cpu_cached_iovas(cpu, iovad);
+}
+
 static void iommu_dma_entry_dtor(unsigned long data)
 {
struct page *freelist = (struct page *)data;
diff --git a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h
index 2112f21f73d8..316d22a4a860 100644
--- a/include/linux/dma-iommu.h
+++ b/include/linux/dma-iommu.h
@@ -37,6 +37,9 @@ void iommu_dma_compose_msi_msg(struct msi_desc *desc,
 
 void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list);
 
+void iommu_dma_free_cpu_cached_iovas(unsigned int cpu,
+   struct iommu_domain *domain);
+
 #else /* CONFIG_IOMMU_DMA */
 
 struct iommu_domain;
-- 
2.20.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH 0/8] Convert the intel iommu driver to the dma-iommu api

2020-09-03 Thread Tom Murphy
On Fri, 28 Aug 2020 at 00:34, Tom Murphy  wrote:
>
> On Thu, 27 Aug 2020 at 22:36, Logan Gunthorpe  wrote:
> >
> >
> >
> > On 2020-08-23 6:04 p.m., Tom Murphy wrote:
> > > I have added a check for the sg_dma_len == 0 :
> > > """
> > >  } __sgt_iter(struct scatterlist *sgl, bool dma) {
> > > struct sgt_iter s = { .sgp = sgl };
> > >
> > > +   if (sgl && sg_dma_len(sgl) == 0)
> > > +   s.sgp = NULL;
> > >
> > > if (s.sgp) {
> > > .
> > > """
> > > at location [1].
> > > but it doens't fix the problem.
> >
> > Based on my read of the code, it looks like we also need to change usage
> > of sgl->length... Something like the rough patch below, maybe?
> >
> > Also, Tom, do you have an updated version of the patchset to convert the
> > Intel IOMMU to dma-iommu available? The last one I've found doesn't
> > apply cleanly (I'm assuming parts of it have been merged in slightly
> > modified forms).
> >
>
> I'll try and post one in the next 24hours

I have just posted this now:
The subject of the cover letter is:
"[PATCH V2 0/5] Convert the intel iommu driver to the dma-iommu api"

>
> > Thanks,
> >
> > Logan
> >
> > --
> >
> > diff --git a/drivers/gpu/drm/i915/i915_scatterlist.h
> > b/drivers/gpu/drm/i915/i915
> > index b7b59328cb76..9367ac801f0c 100644
> > --- a/drivers/gpu/drm/i915/i915_scatterlist.h
> > +++ b/drivers/gpu/drm/i915/i915_scatterlist.h
> > @@ -27,13 +27,19 @@ static __always_inline struct sgt_iter {
> >  } __sgt_iter(struct scatterlist *sgl, bool dma) {
> > struct sgt_iter s = { .sgp = sgl };
> >
> > +   if (sgl && !sg_dma_len(s.sgp))
> > +   s.sgp = NULL;
> > +
> > if (s.sgp) {
> > s.max = s.curr = s.sgp->offset;
> > -   s.max += s.sgp->length;
> > -   if (dma)
> > +
> > +   if (dma) {
> > +   s.max += sg_dma_len(s.sgp);
> > s.dma = sg_dma_address(s.sgp);
> > -   else
> > +   } else {
> > +   s.max += s.sgp->length;
> > s.pfn = page_to_pfn(sg_page(s.sgp));
> > +   }
> > }
> >
> > return s;
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH V2 0/5] Convert the intel iommu driver to the dma-iommu api

2020-09-03 Thread Tom Murphy
This patchset converts the intel iommu driver to the dma-iommu api.

While converting the driver I exposed a bug in the intel i915 driver which 
causes a huge amount of artifacts on the screen of my laptop. You can see a 
picture of it here:
https://github.com/pippy360/kernelPatches/blob/master/IMG_20191219_225922.jpg

This issue is in the i915 driver and is caused by the driver not respecting the 
return value of the dma_map_ops::map_sg function.

We talked about this in this microconference:
https://linuxplumbersconf.org/event/7/contributions/846/
and came to the conclusion that we should add an attribute to disable combining 
sg segments in the dma-iommu api (in the __finalise_sg function). This will 
work as a temporary fix and allow us to convert the intel iommu driver to the 
dma-iommu path while we wait for the i915 driver to be rewritten to respect the 
return value of map_sg. I haven't done this work yet and won't have time to do 
it. If someone else could take this on that would be great.

To allow my patch set to be tested I have added a patch (the "DO NOT MERGE..." 
patch) in this series to disable combining sg segments in the dma-iommu api 
which fixes the bug but it doesn't fix the actual problem.

As part of this patch series I copied the intel bounce buffer code to the 
dma-iommu path. The addition of the bounce buffer code took me by surprise. I 
did most of my development on this patch series before the bounce buffer code 
was added and my reimplementation in the dma-iommu path is very rushed and not 
properly tested but I’m running out of time to work on this patch set.

On top of that I also didn’t port over the intel tracing code from this commit:
https://github.com/torvalds/linux/commit/3b53034c268d550d9e8522e613a14ab53b8840d8#diff-6b3e7c4993f05e76331e463ab1fc87e1
So all the work in that commit is now wasted. The code will need to be removed 
and reimplemented in the dma-iommu path. I would like to take the time to do 
this but I really don’t have the time at the moment and I want to get these 
changes out before the iommu code changes any more.

Unfortunately I no longer have enough spare time to continue to work on/rebase 
this patch series. So this will most likely be the last patch series from me 
for the intel dma-iommu conversion.

Change-log:
v2:
-Rebase on top of the latest staging branch
-move the freelist parameter to iommu_iotlb_gather

Signed-off-by: Tom Murphy 

Tom Murphy (5):
  iommu: Handle freelists when using deferred flushing in iommu drivers
  iommu: Add iommu_dma_free_cpu_cached_iovas function
  iommu: allow the dma-iommu api to use bounce buffers
  iommu/vt-d: Convert intel iommu driver to the iommu ops
  DO NOT MERGE: iommu: disable list appending in dma-iommu

 drivers/iommu/Kconfig   |   1 +
 drivers/iommu/dma-iommu.c   | 169 +---
 drivers/iommu/intel/iommu.c | 805 
 drivers/iommu/iommu.c   |  10 +
 include/linux/dma-iommu.h   |   3 +
 include/linux/iommu.h   |   8 +
 6 files changed, 222 insertions(+), 774 deletions(-)

-- 
2.20.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

[PATCH V2 1/5] iommu: Handle freelists when using deferred flushing in iommu drivers

2020-09-03 Thread Tom Murphy
Allow the iommu_unmap_fast to return newly freed page table pages and
pass the freelist to queue_iova in the dma-iommu ops path.

This is useful for iommu drivers (in this case the intel iommu driver)
which need to wait for the ioTLB to be flushed before newly
free/unmapped page table pages can be freed. This way we can still batch
ioTLB free operations and handle the freelists.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/dma-iommu.c   | 30 ++--
 drivers/iommu/intel/iommu.c | 55 -
 include/linux/iommu.h   |  1 +
 3 files changed, 59 insertions(+), 27 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 4959f5df21bd..f69dc9467d71 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -50,6 +50,18 @@ struct iommu_dma_cookie {
struct iommu_domain *fq_domain;
 };
 
+static void iommu_dma_entry_dtor(unsigned long data)
+{
+   struct page *freelist = (struct page *)data;
+
+   while (freelist != NULL) {
+   unsigned long p = (unsigned long)page_address(freelist);
+
+   freelist = freelist->freelist;
+   free_page(p);
+   }
+}
+
 static inline size_t cookie_msi_granule(struct iommu_dma_cookie *cookie)
 {
if (cookie->type == IOMMU_DMA_IOVA_COOKIE)
@@ -344,7 +356,8 @@ static int iommu_dma_init_domain(struct iommu_domain 
*domain, dma_addr_t base,
if (!cookie->fq_domain && !iommu_domain_get_attr(domain,
DOMAIN_ATTR_DMA_USE_FLUSH_QUEUE, ) && attr) {
cookie->fq_domain = domain;
-   init_iova_flush_queue(iovad, iommu_dma_flush_iotlb_all, NULL);
+   init_iova_flush_queue(iovad, iommu_dma_flush_iotlb_all,
+   iommu_dma_entry_dtor);
}
 
if (!dev)
@@ -438,7 +451,7 @@ static dma_addr_t iommu_dma_alloc_iova(struct iommu_domain 
*domain,
 }
 
 static void iommu_dma_free_iova(struct iommu_dma_cookie *cookie,
-   dma_addr_t iova, size_t size)
+   dma_addr_t iova, size_t size, struct page *freelist)
 {
struct iova_domain *iovad = >iovad;
 
@@ -447,7 +460,8 @@ static void iommu_dma_free_iova(struct iommu_dma_cookie 
*cookie,
cookie->msi_iova -= size;
else if (cookie->fq_domain) /* non-strict mode */
queue_iova(iovad, iova_pfn(iovad, iova),
-   size >> iova_shift(iovad), 0);
+   size >> iova_shift(iovad),
+   (unsigned long) freelist);
else
free_iova_fast(iovad, iova_pfn(iovad, iova),
size >> iova_shift(iovad));
@@ -472,7 +486,7 @@ static void __iommu_dma_unmap(struct device *dev, 
dma_addr_t dma_addr,
 
if (!cookie->fq_domain)
iommu_tlb_sync(domain, _gather);
-   iommu_dma_free_iova(cookie, dma_addr, size);
+   iommu_dma_free_iova(cookie, dma_addr, size, iotlb_gather.freelist);
 }
 
 static dma_addr_t __iommu_dma_map(struct device *dev, phys_addr_t phys,
@@ -494,7 +508,7 @@ static dma_addr_t __iommu_dma_map(struct device *dev, 
phys_addr_t phys,
return DMA_MAPPING_ERROR;
 
if (iommu_map_atomic(domain, iova, phys - iova_off, size, prot)) {
-   iommu_dma_free_iova(cookie, iova, size);
+   iommu_dma_free_iova(cookie, iova, size, NULL);
return DMA_MAPPING_ERROR;
}
return iova + iova_off;
@@ -649,7 +663,7 @@ static void *iommu_dma_alloc_remap(struct device *dev, 
size_t size,
 out_free_sg:
sg_free_table();
 out_free_iova:
-   iommu_dma_free_iova(cookie, iova, size);
+   iommu_dma_free_iova(cookie, iova, size, NULL);
 out_free_pages:
__iommu_dma_free_pages(pages, count);
return NULL;
@@ -900,7 +914,7 @@ static int iommu_dma_map_sg(struct device *dev, struct 
scatterlist *sg,
return __finalise_sg(dev, sg, nents, iova);
 
 out_free_iova:
-   iommu_dma_free_iova(cookie, iova, iova_len);
+   iommu_dma_free_iova(cookie, iova, iova_len, NULL);
 out_restore_sg:
__invalidate_sg(sg, nents);
return 0;
@@ -1194,7 +1208,7 @@ static struct iommu_dma_msi_page 
*iommu_dma_get_msi_page(struct device *dev,
return msi_page;
 
 out_free_iova:
-   iommu_dma_free_iova(cookie, iova, size);
+   iommu_dma_free_iova(cookie, iova, size, NULL);
 out_free_page:
kfree(msi_page);
return NULL;
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index 237a470e1e9c..03699860880b 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -1160,17 +1160,17 @@ static struct page *dma_pte_clear_level(struct 
dmar_domain *domain, int level,
pages can only be freed after the IOTLB flush has been done. */
 static struct page *domain_unmap(struct dmar_doma

[PATCH V2 5/5] DO NOT MERGE: iommu: disable list appending in dma-iommu

2020-09-03 Thread Tom Murphy
Disable combining sg segments in the dma-iommu api.
Combining the sg segments exposes a bug in the intel i915 driver which
causes visual artifacts and the screen to freeze. This is most likely
because of how the i915 handles the returned list. It probably doesn't
respect the returned value specifying the number of elements in the list
and instead depends on the previous behaviour of the intel iommu driver
which would return the same number of elements in the output list as in
the input list.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/dma-iommu.c | 38 ++
 1 file changed, 6 insertions(+), 32 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 185cd504ca5a..6697b4ad0df6 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -843,49 +843,23 @@ static int __finalise_sg(struct device *dev, struct 
scatterlist *sg, int nents,
dma_addr_t dma_addr)
 {
struct scatterlist *s, *cur = sg;
-   unsigned long seg_mask = dma_get_seg_boundary(dev);
-   unsigned int cur_len = 0, max_len = dma_get_max_seg_size(dev);
-   int i, count = 0;
+   int i;
 
for_each_sg(sg, s, nents, i) {
/* Restore this segment's original unaligned fields first */
unsigned int s_iova_off = sg_dma_address(s);
unsigned int s_length = sg_dma_len(s);
unsigned int s_iova_len = s->length;
+   if (i > 0)
+   cur = sg_next(cur);
 
s->offset += s_iova_off;
s->length = s_length;
-   sg_dma_address(s) = DMA_MAPPING_ERROR;
-   sg_dma_len(s) = 0;
-
-   /*
-* Now fill in the real DMA data. If...
-* - there is a valid output segment to append to
-* - and this segment starts on an IOVA page boundary
-* - but doesn't fall at a segment boundary
-* - and wouldn't make the resulting output segment too long
-*/
-   if (cur_len && !s_iova_off && (dma_addr & seg_mask) &&
-   (max_len - cur_len >= s_length)) {
-   /* ...then concatenate it with the previous one */
-   cur_len += s_length;
-   } else {
-   /* Otherwise start the next output segment */
-   if (i > 0)
-   cur = sg_next(cur);
-   cur_len = s_length;
-   count++;
-
-   sg_dma_address(cur) = dma_addr + s_iova_off;
-   }
-
-   sg_dma_len(cur) = cur_len;
+   sg_dma_address(cur) = dma_addr + s_iova_off;
+   sg_dma_len(cur) = s_length;
dma_addr += s_iova_len;
-
-   if (s_length + s_iova_off < s_iova_len)
-   cur_len = 0;
}
-   return count;
+   return nents;
 }
 
 /*
-- 
2.20.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH V2 4/5] iommu/vt-d: Convert intel iommu driver to the iommu ops

2020-09-03 Thread Tom Murphy
Convert the intel iommu driver to the dma-iommu api. Remove the iova
handling and reserve region code from the intel iommu driver.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/Kconfig   |   1 +
 drivers/iommu/intel/iommu.c | 756 +++-
 2 files changed, 51 insertions(+), 706 deletions(-)

diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index b622af72448f..f1404fc4cc5f 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -191,6 +191,7 @@ config INTEL_IOMMU
select DMAR_TABLE
select SWIOTLB
select IOASID
+   select IOMMU_DMA
help
  DMA remapping (DMAR) devices support enables independent address
  translations for Direct Memory Access (DMA) from devices.
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index ba47623f0f12..98cda61681d2 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -31,6 +31,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -41,7 +42,6 @@
 #include 
 #include 
 #include 
-#include 
 #include 
 #include 
 #include 
@@ -383,9 +383,6 @@ struct device_domain_info *get_domain_info(struct device 
*dev)
 DEFINE_SPINLOCK(device_domain_lock);
 static LIST_HEAD(device_domain_list);
 
-#define device_needs_bounce(d) (!intel_no_bounce && dev_is_pci(d) &&   \
-   to_pci_dev(d)->untrusted)
-
 /*
  * Iterate over elements in device_domain_list and call the specified
  * callback @fn against each element.
@@ -748,7 +745,7 @@ static int iommu_dummy(struct device *dev)
 
 static bool attach_deferred(struct device *dev)
 {
-   return dev->archdata.iommu == DEFER_DEVICE_DOMAIN_INFO;
+   return dev_iommu_priv_get(dev) == DEFER_DEVICE_DOMAIN_INFO;
 }
 
 /**
@@ -1194,13 +1191,6 @@ static void dma_free_pagelist(struct page *freelist)
}
 }
 
-static void iova_entry_free(unsigned long data)
-{
-   struct page *freelist = (struct page *)data;
-
-   dma_free_pagelist(freelist);
-}
-
 /* iommu handling */
 static int iommu_alloc_root_entry(struct intel_iommu *iommu)
 {
@@ -1565,19 +1555,17 @@ static inline void __mapping_notify_one(struct 
intel_iommu *iommu,
iommu_flush_write_buffer(iommu);
 }
 
-static void iommu_flush_iova(struct iova_domain *iovad)
+static void intel_flush_iotlb_all(struct iommu_domain *domain)
 {
-   struct dmar_domain *domain;
+   struct dmar_domain *dmar_domain = to_dmar_domain(domain);
int idx;
 
-   domain = container_of(iovad, struct dmar_domain, iovad);
-
-   for_each_domain_iommu(idx, domain) {
+   for_each_domain_iommu(idx, dmar_domain) {
struct intel_iommu *iommu = g_iommus[idx];
-   u16 did = domain->iommu_did[iommu->seq_id];
+   u16 did = dmar_domain->iommu_did[iommu->seq_id];
 
-   if (domain_use_first_level(domain))
-   domain_flush_piotlb(iommu, domain, 0, -1, 0);
+   if (domain_use_first_level(dmar_domain))
+   domain_flush_piotlb(iommu, dmar_domain, 0, -1, 0);
else
iommu->flush.flush_iotlb(iommu, did, 0, 0,
 DMA_TLB_DSI_FLUSH);
@@ -1855,48 +1843,6 @@ static int domain_detach_iommu(struct dmar_domain 
*domain,
return count;
 }
 
-static struct iova_domain reserved_iova_list;
-static struct lock_class_key reserved_rbtree_key;
-
-static int dmar_init_reserved_ranges(void)
-{
-   struct pci_dev *pdev = NULL;
-   struct iova *iova;
-   int i;
-
-   init_iova_domain(_iova_list, VTD_PAGE_SIZE, IOVA_START_PFN);
-
-   lockdep_set_class(_iova_list.iova_rbtree_lock,
-   _rbtree_key);
-
-   /* IOAPIC ranges shouldn't be accessed by DMA */
-   iova = reserve_iova(_iova_list, IOVA_PFN(IOAPIC_RANGE_START),
-   IOVA_PFN(IOAPIC_RANGE_END));
-   if (!iova) {
-   pr_err("Reserve IOAPIC range failed\n");
-   return -ENODEV;
-   }
-
-   /* Reserve all PCI MMIO to avoid peer-to-peer access */
-   for_each_pci_dev(pdev) {
-   struct resource *r;
-
-   for (i = 0; i < PCI_NUM_RESOURCES; i++) {
-   r = >resource[i];
-   if (!r->flags || !(r->flags & IORESOURCE_MEM))
-   continue;
-   iova = reserve_iova(_iova_list,
-   IOVA_PFN(r->start),
-   IOVA_PFN(r->end));
-   if (!iova) {
-   pci_err(pdev, "Reserve iova for %pR failed\n", 
r);
-   return -ENODEV;
-   }
-   }
-   }
-   return 0;
-}
-
 static inline int guestwidth_to_adjustwidth(int gaw)
 {
   

Re: [PATCH 0/8] Convert the intel iommu driver to the dma-iommu api

2020-08-27 Thread Tom Murphy
On Thu, 27 Aug 2020 at 22:36, Logan Gunthorpe  wrote:
>
>
>
> On 2020-08-23 6:04 p.m., Tom Murphy wrote:
> > I have added a check for the sg_dma_len == 0 :
> > """
> >  } __sgt_iter(struct scatterlist *sgl, bool dma) {
> > struct sgt_iter s = { .sgp = sgl };
> >
> > +   if (sgl && sg_dma_len(sgl) == 0)
> > +   s.sgp = NULL;
> >
> > if (s.sgp) {
> > .
> > """
> > at location [1].
> > but it doens't fix the problem.
>
> Based on my read of the code, it looks like we also need to change usage
> of sgl->length... Something like the rough patch below, maybe?
>
> Also, Tom, do you have an updated version of the patchset to convert the
> Intel IOMMU to dma-iommu available? The last one I've found doesn't
> apply cleanly (I'm assuming parts of it have been merged in slightly
> modified forms).
>

I'll try and post one in the next 24hours

> Thanks,
>
> Logan
>
> --
>
> diff --git a/drivers/gpu/drm/i915/i915_scatterlist.h
> b/drivers/gpu/drm/i915/i915
> index b7b59328cb76..9367ac801f0c 100644
> --- a/drivers/gpu/drm/i915/i915_scatterlist.h
> +++ b/drivers/gpu/drm/i915/i915_scatterlist.h
> @@ -27,13 +27,19 @@ static __always_inline struct sgt_iter {
>  } __sgt_iter(struct scatterlist *sgl, bool dma) {
> struct sgt_iter s = { .sgp = sgl };
>
> +   if (sgl && !sg_dma_len(s.sgp))
> +   s.sgp = NULL;
> +
> if (s.sgp) {
> s.max = s.curr = s.sgp->offset;
> -   s.max += s.sgp->length;
> -   if (dma)
> +
> +   if (dma) {
> +   s.max += sg_dma_len(s.sgp);
> s.dma = sg_dma_address(s.sgp);
> -   else
> +   } else {
> +   s.max += s.sgp->length;
> s.pfn = page_to_pfn(sg_page(s.sgp));
> +   }
> }
>
> return s;
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH 0/8] Convert the intel iommu driver to the dma-iommu api

2020-08-26 Thread Tom Murphy
That would be great!

On Wed., Aug. 26, 2020, 2:14 p.m. Robin Murphy, 
wrote:

> Hi Tom,
>
> On 2019-12-21 15:03, Tom Murphy wrote:
> > This patchset converts the intel iommu driver to the dma-iommu api.
> >
> > While converting the driver I exposed a bug in the intel i915 driver
> which causes a huge amount of artifacts on the screen of my laptop. You can
> see a picture of it here:
> >
> https://github.com/pippy360/kernelPatches/blob/master/IMG_20191219_225922.jpg
> >
> > This issue is most likely in the i915 driver and is most likely caused
> by the driver not respecting the return value of the dma_map_ops::map_sg
> function. You can see the driver ignoring the return value here:
> >
> https://github.com/torvalds/linux/blob/7e0165b2f1a912a06e381e91f0f4e495f4ac3736/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c#L51
> >
> > Previously this didn’t cause issues because the intel map_sg always
> returned the same number of elements as the input scatter gather list but
> with the change to this dma-iommu api this is no longer the case. I wasn’t
> able to track the bug down to a specific line of code unfortunately.
> >
> > Could someone from the intel team look at this?
> >
> >
> > I have been testing on a lenovo x1 carbon 5th generation. Let me know if
> there’s any more information you need.
> >
> > To allow my patch set to be tested I have added a patch (patch 8/8) in
> this series to disable combining sg segments in the dma-iommu api which
> fixes the bug but it doesn't fix the actual problem.
> >
> > As part of this patch series I copied the intel bounce buffer code to
> the dma-iommu path. The addition of the bounce buffer code took me by
> surprise. I did most of my development on this patch series before the
> bounce buffer code was added and my reimplementation in the dma-iommu path
> is very rushed and not properly tested but I’m running out of time to work
> on this patch set.
> >
> > On top of that I also didn’t port over the intel tracing code from this
> commit:
> >
> https://github.com/torvalds/linux/commit/3b53034c268d550d9e8522e613a14ab53b8840d8#diff-6b3e7c4993f05e76331e463ab1fc87e1
> > So all the work in that commit is now wasted. The code will need to be
> removed and reimplemented in the dma-iommu path. I would like to take the
> time to do this but I really don’t have the time at the moment and I want
> to get these changes out before the iommu code changes any more.
>
> Further to what we just discussed at LPC, I've realised that tracepoints
> are actually something I could do with *right now* for debugging my Arm
> DMA ops series, so if I'm going to hack something up anyway I may as
> well take responsibility for polishing it into a proper patch as well :)
>
> Robin.
>
> >
> > Tom Murphy (8):
> >iommu/vt-d: clean up 32bit si_domain assignment
> >iommu/vt-d: Use default dma_direct_* mapping functions for direct
> >  mapped devices
> >iommu/vt-d: Remove IOVA handling code from non-dma_ops path
> >iommu: Handle freelists when using deferred flushing in iommu drivers
> >iommu: Add iommu_dma_free_cpu_cached_iovas function
> >iommu: allow the dma-iommu api to use bounce buffers
> >iommu/vt-d: Convert intel iommu driver to the iommu ops
> >DO NOT MERGE: iommu: disable list appending in dma-iommu
> >
> >   drivers/iommu/Kconfig   |   1 +
> >   drivers/iommu/amd_iommu.c   |  14 +-
> >   drivers/iommu/arm-smmu-v3.c |   3 +-
> >   drivers/iommu/arm-smmu.c|   3 +-
> >   drivers/iommu/dma-iommu.c   | 183 +--
> >   drivers/iommu/exynos-iommu.c|   3 +-
> >   drivers/iommu/intel-iommu.c | 936 
> >   drivers/iommu/iommu.c   |  39 +-
> >   drivers/iommu/ipmmu-vmsa.c  |   3 +-
> >   drivers/iommu/msm_iommu.c   |   3 +-
> >   drivers/iommu/mtk_iommu.c   |   3 +-
> >   drivers/iommu/mtk_iommu_v1.c|   3 +-
> >   drivers/iommu/omap-iommu.c  |   3 +-
> >   drivers/iommu/qcom_iommu.c  |   3 +-
> >   drivers/iommu/rockchip-iommu.c  |   3 +-
> >   drivers/iommu/s390-iommu.c  |   3 +-
> >   drivers/iommu/tegra-gart.c  |   3 +-
> >   drivers/iommu/tegra-smmu.c  |   3 +-
> >   drivers/iommu/virtio-iommu.c|   3 +-
> >   drivers/vfio/vfio_iommu_type1.c |   2 +-
> >   include/linux/dma-iommu.h   |   3 +
> >   include/linux/intel-iommu.h |   1 -
> >   include/linux/iommu.h   |  32 +-
> >   23 files changed, 345 insertions(+), 908 deletions(-)
> >
>
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Re: [PATCH 0/8] Convert the intel iommu driver to the dma-iommu api

2020-08-23 Thread Tom Murphy
Hi Logan/All,

I have added a check for the sg_dma_len == 0 :
"""
 } __sgt_iter(struct scatterlist *sgl, bool dma) {
struct sgt_iter s = { .sgp = sgl };

+   if (sgl && sg_dma_len(sgl) == 0)
+   s.sgp = NULL;

if (s.sgp) {
.
"""
at location [1].
but it doens't fix the problem.

You're right though, this change does need to be made, this code
doesn't handle pages of sg_dma_len(sg) == 0 correctly
So my guess is that we have more bugs in other parts of the i915
driver (or there is a problem with my "sg_dma_len == 0" fix above).
I have been trying to spot where else the code might be buggy but I
haven't had any luck so far.

I'm doing a microconfernce (at LPC 2020) this wednesdays [1] on this
if you're interested in attending.
I'm hoping I can chat about it with a few people and find how can
reproduce and fix this issues. I don't have any more time I can give
to this unfortunately and it would be a shame for the work to go to
waste.

[0] 
https://github.com/torvalds/linux/blob/d012a7190fc1fd72ed48911e77ca97ba4521bccd/drivers/gpu/drm/i915/i915_scatterlist.h#L28
[1] https://linuxplumbersconf.org/event/7/contributions/846/

On Fri, 29 May 2020 at 22:21, Logan Gunthorpe  wrote:
>
>
>
> On 2020-05-29 3:11 p.m., Marek Szyprowski wrote:
> > Patches are pending:
> > https://lore.kernel.org/linux-iommu/20200513132114.6046-1-m.szyprow...@samsung.com/T/
>
> Cool, nice! Though, I still don't think that fixes the issue in
> i915_scatterlist.h given it still ignores sg_dma_len() and strictly
> relies on sg_next()/sg_is_last() to stop iterating -- and I suspect this
> is the bug that got in Tom's way.
>
> >> However, as Robin pointed out, there are other ugly tricks like stopping
> >> iterating through the SGL when sg_dma_len() is zero. For example, the
> >> AMD driver appears to use drm_prime_sg_to_page_addr_arrays() which does
> >> this trick and thus likely isn't buggy (otherwise, I'd expect someone to
> >> have complained by now seeing AMD has already switched to IOMMU-DMA.
> >
> > I'm not sure that this is a trick. Stopping at zero sg_dma_len() was
> > somewhere documented.
>
> Well whatever you want to call it, it is ugly to have some drivers doing
> one thing with the returned value and others assuming there's an extra
> zero at the end. It just causes confusion for people reading/copying the
> code. It would be better if they are all consistent. However, I concede
> stopping at zero should not be broken, presently.
>
> Logan
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH V2 1/2] Add new flush_iotlb_range and handle freelists when using iommu_unmap_fast

2020-08-18 Thread Tom Murphy
On Tue, 18 Aug 2020 at 16:17, Robin Murphy  wrote:
>
> On 2020-08-18 07:04, Tom Murphy wrote:
> > Add a flush_iotlb_range to allow flushing of an iova range instead of a
> > full flush in the dma-iommu path.
> >
> > Allow the iommu_unmap_fast to return newly freed page table pages and
> > pass the freelist to queue_iova in the dma-iommu ops path.
> >
> > This patch is useful for iommu drivers (in this case the intel iommu
> > driver) which need to wait for the ioTLB to be flushed before newly
> > free/unmapped page table pages can be freed. This way we can still batch
> > ioTLB free operations and handle the freelists.
>
> It sounds like the freelist is something that logically belongs in the
> iommu_iotlb_gather structure. And even if it's not a perfect fit I'd be
> inclined to jam it in there anyway just to avoid this giant argument
> explosion ;)

Good point, I'll do that.

>
> Why exactly do we need to introduce a new flush_iotlb_range() op? Can't
> the AMD driver simply use the gather mechanism like everyone else?

No, there's no reason it can't simply use the gather mechanism. I will
use the gather mechanism.
I think I wrote this patch way back before the gather mechanism was
introduced and I've been rebasing/slightly updating it since then
without paying proper attention to the code.

>
> Robin.
>
> > Change-log:
> > V2:
> > -fix missing parameter in mtk_iommu_v1.c
> >
> > Signed-off-by: Tom Murphy 
> > ---
> >   drivers/iommu/amd/iommu.c   | 14 -
> >   drivers/iommu/arm-smmu-v3.c |  3 +-
> >   drivers/iommu/arm-smmu.c|  3 +-
> >   drivers/iommu/dma-iommu.c   | 45 ---
> >   drivers/iommu/exynos-iommu.c|  3 +-
> >   drivers/iommu/intel/iommu.c | 54 +
> >   drivers/iommu/iommu.c   | 25 +++
> >   drivers/iommu/ipmmu-vmsa.c  |  3 +-
> >   drivers/iommu/msm_iommu.c   |  3 +-
> >   drivers/iommu/mtk_iommu.c   |  3 +-
> >   drivers/iommu/mtk_iommu_v1.c|  3 +-
> >   drivers/iommu/omap-iommu.c  |  3 +-
> >   drivers/iommu/qcom_iommu.c  |  3 +-
> >   drivers/iommu/rockchip-iommu.c  |  3 +-
> >   drivers/iommu/s390-iommu.c  |  3 +-
> >   drivers/iommu/sun50i-iommu.c|  3 +-
> >   drivers/iommu/tegra-gart.c  |  3 +-
> >   drivers/iommu/tegra-smmu.c  |  3 +-
> >   drivers/iommu/virtio-iommu.c|  3 +-
> >   drivers/vfio/vfio_iommu_type1.c |  2 +-
> >   include/linux/iommu.h   | 21 +++--
> >   21 files changed, 150 insertions(+), 56 deletions(-)
> >
> > diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
> > index 2f22326ee4df..25fbacab23c3 100644
> > --- a/drivers/iommu/amd/iommu.c
> > +++ b/drivers/iommu/amd/iommu.c
> > @@ -2513,7 +2513,8 @@ static int amd_iommu_map(struct iommu_domain *dom, 
> > unsigned long iova,
> >
> >   static size_t amd_iommu_unmap(struct iommu_domain *dom, unsigned long 
> > iova,
> > size_t page_size,
> > -   struct iommu_iotlb_gather *gather)
> > +   struct iommu_iotlb_gather *gather,
> > +   struct page **freelist)
> >   {
> >   struct protection_domain *domain = to_pdomain(dom);
> >   struct domain_pgtable pgtable;
> > @@ -2636,6 +2637,16 @@ static void amd_iommu_flush_iotlb_all(struct 
> > iommu_domain *domain)
> >   spin_unlock_irqrestore(>lock, flags);
> >   }
> >
> > +static void amd_iommu_flush_iotlb_range(struct iommu_domain *domain,
> > + unsigned long iova, size_t size,
> > + struct page *freelist)
> > +{
> > + struct protection_domain *dom = to_pdomain(domain);
> > +
> > + domain_flush_pages(dom, iova, size);
> > + domain_flush_complete(dom);
> > +}
> > +
> >   static void amd_iommu_iotlb_sync(struct iommu_domain *domain,
> >struct iommu_iotlb_gather *gather)
> >   {
> > @@ -2675,6 +2686,7 @@ const struct iommu_ops amd_iommu_ops = {
> >   .is_attach_deferred = amd_iommu_is_attach_deferred,
> >   .pgsize_bitmap  = AMD_IOMMU_PGSIZES,
> >   .flush_iotlb_all = amd_iommu_flush_iotlb_all,
> > + .flush_iotlb_range = amd_iommu_flush_iotlb_range,
> >   .iotlb_sync = amd_iommu_iotlb_sync,
> >   .def_domain_type = amd_iommu_def_domain_type,
> >   };
> > diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
&g

[PATCH V2 1/2] Add new flush_iotlb_range and handle freelists when using iommu_unmap_fast

2020-08-18 Thread Tom Murphy
Add a flush_iotlb_range to allow flushing of an iova range instead of a
full flush in the dma-iommu path.

Allow the iommu_unmap_fast to return newly freed page table pages and
pass the freelist to queue_iova in the dma-iommu ops path.

This patch is useful for iommu drivers (in this case the intel iommu
driver) which need to wait for the ioTLB to be flushed before newly
free/unmapped page table pages can be freed. This way we can still batch
ioTLB free operations and handle the freelists.

Change-log:
V2:
-fix missing parameter in mtk_iommu_v1.c

Signed-off-by: Tom Murphy 
---
 drivers/iommu/amd/iommu.c   | 14 -
 drivers/iommu/arm-smmu-v3.c |  3 +-
 drivers/iommu/arm-smmu.c|  3 +-
 drivers/iommu/dma-iommu.c   | 45 ---
 drivers/iommu/exynos-iommu.c|  3 +-
 drivers/iommu/intel/iommu.c | 54 +
 drivers/iommu/iommu.c   | 25 +++
 drivers/iommu/ipmmu-vmsa.c  |  3 +-
 drivers/iommu/msm_iommu.c   |  3 +-
 drivers/iommu/mtk_iommu.c   |  3 +-
 drivers/iommu/mtk_iommu_v1.c|  3 +-
 drivers/iommu/omap-iommu.c  |  3 +-
 drivers/iommu/qcom_iommu.c  |  3 +-
 drivers/iommu/rockchip-iommu.c  |  3 +-
 drivers/iommu/s390-iommu.c  |  3 +-
 drivers/iommu/sun50i-iommu.c|  3 +-
 drivers/iommu/tegra-gart.c  |  3 +-
 drivers/iommu/tegra-smmu.c  |  3 +-
 drivers/iommu/virtio-iommu.c|  3 +-
 drivers/vfio/vfio_iommu_type1.c |  2 +-
 include/linux/iommu.h   | 21 +++--
 21 files changed, 150 insertions(+), 56 deletions(-)

diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index 2f22326ee4df..25fbacab23c3 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -2513,7 +2513,8 @@ static int amd_iommu_map(struct iommu_domain *dom, 
unsigned long iova,
 
 static size_t amd_iommu_unmap(struct iommu_domain *dom, unsigned long iova,
  size_t page_size,
- struct iommu_iotlb_gather *gather)
+ struct iommu_iotlb_gather *gather,
+ struct page **freelist)
 {
struct protection_domain *domain = to_pdomain(dom);
struct domain_pgtable pgtable;
@@ -2636,6 +2637,16 @@ static void amd_iommu_flush_iotlb_all(struct 
iommu_domain *domain)
spin_unlock_irqrestore(>lock, flags);
 }
 
+static void amd_iommu_flush_iotlb_range(struct iommu_domain *domain,
+   unsigned long iova, size_t size,
+   struct page *freelist)
+{
+   struct protection_domain *dom = to_pdomain(domain);
+
+   domain_flush_pages(dom, iova, size);
+   domain_flush_complete(dom);
+}
+
 static void amd_iommu_iotlb_sync(struct iommu_domain *domain,
 struct iommu_iotlb_gather *gather)
 {
@@ -2675,6 +2686,7 @@ const struct iommu_ops amd_iommu_ops = {
.is_attach_deferred = amd_iommu_is_attach_deferred,
.pgsize_bitmap  = AMD_IOMMU_PGSIZES,
.flush_iotlb_all = amd_iommu_flush_iotlb_all,
+   .flush_iotlb_range = amd_iommu_flush_iotlb_range,
.iotlb_sync = amd_iommu_iotlb_sync,
.def_domain_type = amd_iommu_def_domain_type,
 };
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index f578677a5c41..8d328dc25326 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -2854,7 +2854,8 @@ static int arm_smmu_map(struct iommu_domain *domain, 
unsigned long iova,
 }
 
 static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
-size_t size, struct iommu_iotlb_gather *gather)
+size_t size, struct iommu_iotlb_gather *gather,
+struct page **freelist)
 {
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops;
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 243bc4cb2705..0cd0dfc89875 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -1234,7 +1234,8 @@ static int arm_smmu_map(struct iommu_domain *domain, 
unsigned long iova,
 }
 
 static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
-size_t size, struct iommu_iotlb_gather *gather)
+size_t size, struct iommu_iotlb_gather *gather,
+struct page **freelist)
 {
struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops;
struct arm_smmu_device *smmu = to_smmu_domain(domain)->smmu;
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 4959f5df21bd..7433f74d921a 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -50,6 +50,19 @@ struct iommu_dma_cookie {
struct iommu_domain *fq_domain;
 };
 
+
+static void iommu_dma_entry_d

[PATCH V2 2/2] Handle init_iova_flush_queue failure in dma-iommu path

2020-08-18 Thread Tom Murphy
init_iova_flush_queue can fail if we run out of memory. Fall back to no
flush queue if it fails.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/dma-iommu.c | 8 +---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 7433f74d921a..5445e2be08b5 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -356,9 +356,11 @@ static int iommu_dma_init_domain(struct iommu_domain 
*domain, dma_addr_t base,
 
if (!cookie->fq_domain && !iommu_domain_get_attr(domain,
DOMAIN_ATTR_DMA_USE_FLUSH_QUEUE, ) && attr) {
-   cookie->fq_domain = domain;
-   init_iova_flush_queue(iovad, iommu_dma_flush_iotlb_all,
-   iommu_dma_entry_dtor);
+   if (init_iova_flush_queue(iovad, iommu_dma_flush_iotlb_all,
+   iommu_dma_entry_dtor))
+   pr_warn("iova flush queue initialization failed\n");
+   else
+   cookie->fq_domain = domain;
}
 
if (!dev)
-- 
2.20.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 2/2] Handle init_iova_flush_queue failure in dma-iommu path

2020-08-17 Thread Tom Murphy
init_iova_flush_queue can fail if we run out of memory. Fall back to no
flush queue if it fails.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/dma-iommu.c | 8 +---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 7433f74d921a..5445e2be08b5 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -356,9 +356,11 @@ static int iommu_dma_init_domain(struct iommu_domain 
*domain, dma_addr_t base,
 
if (!cookie->fq_domain && !iommu_domain_get_attr(domain,
DOMAIN_ATTR_DMA_USE_FLUSH_QUEUE, ) && attr) {
-   cookie->fq_domain = domain;
-   init_iova_flush_queue(iovad, iommu_dma_flush_iotlb_all,
-   iommu_dma_entry_dtor);
+   if (init_iova_flush_queue(iovad, iommu_dma_flush_iotlb_all,
+   iommu_dma_entry_dtor))
+   pr_warn("iova flush queue initialization failed\n");
+   else
+   cookie->fq_domain = domain;
}
 
if (!dev)
-- 
2.20.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Rename iommu_tlb_* functions to iommu_iotlb_*

2020-08-17 Thread Tom Murphy
To keep naming consistent we should stick with *iotlb*. This patch
renames a few remaining functions.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/dma-iommu.c   |  2 +-
 drivers/iommu/iommu.c   |  4 ++--
 drivers/vfio/vfio_iommu_type1.c |  2 +-
 include/linux/io-pgtable.h  |  2 +-
 include/linux/iommu.h   | 10 +-
 5 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 79e6d8d799a3..59adb1a0aefc 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -503,7 +503,7 @@ static void __iommu_dma_unmap(struct device *dev, 
dma_addr_t dma_addr,
domain->ops->flush_iotlb_range(domain, dma_addr, size,
freelist);
else
-   iommu_tlb_sync(domain, _gather);
+   iommu_iotlb_sync(domain, _gather);
}
 
iommu_dma_free_iova(cookie, dma_addr, size, freelist);
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 9065127d7e9c..70a85f41876f 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -762,7 +762,7 @@ static int iommu_create_device_direct_mappings(struct 
iommu_group *group,
 
}
 
-   iommu_flush_tlb_all(domain);
+   iommu_flush_iotlb_all(domain);
 
 out:
iommu_put_resv_regions(dev, );
@@ -2317,7 +2317,7 @@ size_t iommu_unmap(struct iommu_domain *domain,
if (ops->flush_iotlb_range)
ops->flush_iotlb_range(domain, iova, ret, freelist);
else
-   iommu_tlb_sync(domain, _gather);
+   iommu_iotlb_sync(domain, _gather);
 
return ret;
 }
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index 570ebf878fea..d550ceb7b2aa 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -774,7 +774,7 @@ static long vfio_sync_unpin(struct vfio_dma *dma, struct 
vfio_domain *domain,
long unlocked = 0;
struct vfio_regions *entry, *next;
 
-   iommu_tlb_sync(domain->domain, iotlb_gather);
+   iommu_iotlb_sync(domain->domain, iotlb_gather);
 
list_for_each_entry_safe(entry, next, regions, list) {
unlocked += vfio_unpin_pages_remote(dma,
diff --git a/include/linux/io-pgtable.h b/include/linux/io-pgtable.h
index 53d53c6c2be9..d3f2bd4a3ac4 100644
--- a/include/linux/io-pgtable.h
+++ b/include/linux/io-pgtable.h
@@ -31,7 +31,7 @@ enum io_pgtable_fmt {
  *  single page.  IOMMUs that cannot batch TLB invalidation
  *  operations efficiently will typically issue them here, but
  *  others may decide to update the iommu_iotlb_gather 
structure
- *  and defer the invalidation until iommu_tlb_sync() instead.
+ *  and defer the invalidation until iommu_iotlb_sync() 
instead.
  *
  * Note that these can all be called in atomic context and must therefore
  * not block.
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 77e773d03f22..7b363f24bf99 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -542,7 +542,7 @@ extern void iommu_domain_window_disable(struct iommu_domain 
*domain, u32 wnd_nr)
 extern int report_iommu_fault(struct iommu_domain *domain, struct device *dev,
  unsigned long iova, int flags);
 
-static inline void iommu_flush_tlb_all(struct iommu_domain *domain)
+static inline void iommu_flush_iotlb_all(struct iommu_domain *domain)
 {
if (domain->ops->flush_iotlb_all)
domain->ops->flush_iotlb_all(domain);
@@ -556,7 +556,7 @@ static inline void flush_iotlb_range(struct iommu_domain 
*domain,
domain->ops->flush_iotlb_range(domain, iova, size, freelist);
 }
 
-static inline void iommu_tlb_sync(struct iommu_domain *domain,
+static inline void iommu_iotlb_sync(struct iommu_domain *domain,
  struct iommu_iotlb_gather *iotlb_gather)
 {
if (domain->ops->iotlb_sync)
@@ -579,7 +579,7 @@ static inline void iommu_iotlb_gather_add_page(struct 
iommu_domain *domain,
if (gather->pgsize != size ||
end < gather->start || start > gather->end) {
if (gather->pgsize)
-   iommu_tlb_sync(domain, gather);
+   iommu_iotlb_sync(domain, gather);
gather->pgsize = size;
}
 
@@ -762,11 +762,11 @@ static inline size_t iommu_map_sg_atomic(struct 
iommu_domain *domain,
return 0;
 }
 
-static inline void iommu_flush_tlb_all(struct iommu_domain *domain)
+static inline void iommu_flush_iotlb_all(struct iommu_domain *domain)
 {
 }
 
-static inline void iommu_tlb_sync(struct iommu_domain *domain,
+static inline void iommu_iotlb_sync(struct iommu_domain *domain,

[PATCH 1/2] Add new flush_iotlb_range and handle freelists when using iommu_unmap_fast

2020-08-17 Thread Tom Murphy
Add a flush_iotlb_range to allow flushing of an iova range instead of a
full flush in the dma-iommu path.

Allow the iommu_unmap_fast to return newly freed page table pages and
pass the freelist to queue_iova in the dma-iommu ops path.

This patch is useful for iommu drivers (in this case the intel iommu
driver) which need to wait for the ioTLB to be flushed before newly
free/unmapped page table pages can be freed. This way we can still batch
ioTLB free operations and handle the freelists.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/amd/iommu.c   | 14 -
 drivers/iommu/arm-smmu-v3.c |  3 +-
 drivers/iommu/arm-smmu.c|  3 +-
 drivers/iommu/dma-iommu.c   | 45 ---
 drivers/iommu/exynos-iommu.c|  3 +-
 drivers/iommu/intel/iommu.c | 54 +
 drivers/iommu/iommu.c   | 25 +++
 drivers/iommu/ipmmu-vmsa.c  |  3 +-
 drivers/iommu/msm_iommu.c   |  3 +-
 drivers/iommu/mtk_iommu.c   |  3 +-
 drivers/iommu/omap-iommu.c  |  3 +-
 drivers/iommu/qcom_iommu.c  |  3 +-
 drivers/iommu/rockchip-iommu.c  |  3 +-
 drivers/iommu/s390-iommu.c  |  3 +-
 drivers/iommu/sun50i-iommu.c|  3 +-
 drivers/iommu/tegra-gart.c  |  3 +-
 drivers/iommu/tegra-smmu.c  |  3 +-
 drivers/iommu/virtio-iommu.c|  3 +-
 drivers/vfio/vfio_iommu_type1.c |  2 +-
 include/linux/iommu.h   | 21 +++--
 20 files changed, 148 insertions(+), 55 deletions(-)

diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index 2f22326ee4df..25fbacab23c3 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -2513,7 +2513,8 @@ static int amd_iommu_map(struct iommu_domain *dom, 
unsigned long iova,
 
 static size_t amd_iommu_unmap(struct iommu_domain *dom, unsigned long iova,
  size_t page_size,
- struct iommu_iotlb_gather *gather)
+ struct iommu_iotlb_gather *gather,
+ struct page **freelist)
 {
struct protection_domain *domain = to_pdomain(dom);
struct domain_pgtable pgtable;
@@ -2636,6 +2637,16 @@ static void amd_iommu_flush_iotlb_all(struct 
iommu_domain *domain)
spin_unlock_irqrestore(>lock, flags);
 }
 
+static void amd_iommu_flush_iotlb_range(struct iommu_domain *domain,
+   unsigned long iova, size_t size,
+   struct page *freelist)
+{
+   struct protection_domain *dom = to_pdomain(domain);
+
+   domain_flush_pages(dom, iova, size);
+   domain_flush_complete(dom);
+}
+
 static void amd_iommu_iotlb_sync(struct iommu_domain *domain,
 struct iommu_iotlb_gather *gather)
 {
@@ -2675,6 +2686,7 @@ const struct iommu_ops amd_iommu_ops = {
.is_attach_deferred = amd_iommu_is_attach_deferred,
.pgsize_bitmap  = AMD_IOMMU_PGSIZES,
.flush_iotlb_all = amd_iommu_flush_iotlb_all,
+   .flush_iotlb_range = amd_iommu_flush_iotlb_range,
.iotlb_sync = amd_iommu_iotlb_sync,
.def_domain_type = amd_iommu_def_domain_type,
 };
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index f578677a5c41..8d328dc25326 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -2854,7 +2854,8 @@ static int arm_smmu_map(struct iommu_domain *domain, 
unsigned long iova,
 }
 
 static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
-size_t size, struct iommu_iotlb_gather *gather)
+size_t size, struct iommu_iotlb_gather *gather,
+struct page **freelist)
 {
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops;
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 243bc4cb2705..0cd0dfc89875 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -1234,7 +1234,8 @@ static int arm_smmu_map(struct iommu_domain *domain, 
unsigned long iova,
 }
 
 static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
-size_t size, struct iommu_iotlb_gather *gather)
+size_t size, struct iommu_iotlb_gather *gather,
+struct page **freelist)
 {
struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops;
struct arm_smmu_device *smmu = to_smmu_domain(domain)->smmu;
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 4959f5df21bd..7433f74d921a 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -50,6 +50,19 @@ struct iommu_dma_cookie {
struct iommu_domain *fq_domain;
 };
 
+
+static void iommu_dma_entry_dtor(unsigned long data)
+{
+   struct page *freelist = (struct page *)data;
+
+   whi

Re: [PATCH V6 4/5] iommu/dma-iommu: Use the dev->coherent_dma_mask

2020-07-10 Thread Tom Murphy
>Btw, what is the current state of converting intel-iommu to the dma-iommu

These changes expose a bug in the i915 intel driver which hasn't been
fixed yet. I don't think anyone is actively working on it but I plan
on merging as many patches as I can so it's easier to do the
intel-iommu -> dma-iommu conversion once the bug is fixed.

You can read more about it here:
https://patchwork.kernel.org/cover/11306999/

On Fri, 10 Jul 2020 at 08:59, Christoph Hellwig  wrote:
>
> Btw, what is the current state of converting intel-iommu to the dma-iommu
> code?
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH] intel-iommu: Remove all IOVA handling code from the non-dma_ops path in the intel

2020-04-01 Thread Tom Murphy
There's no need for the non-dma_ops path to keep track of IOVAs. The
whole point of the non-dma_ops path is that it allows the IOVAs to be
handled separately. The IOVA handling code removed in this patch is
pointless.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/intel-iommu.c | 97 +
 1 file changed, 33 insertions(+), 64 deletions(-)

diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 4be549478691..b92606979914 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -1911,11 +1911,6 @@ static int dmar_init_reserved_ranges(void)
return 0;
 }
 
-static void domain_reserve_special_ranges(struct dmar_domain *domain)
-{
-   copy_reserved_iova(_iova_list, >iovad);
-}
-
 static inline int guestwidth_to_adjustwidth(int gaw)
 {
int agaw;
@@ -1946,7 +1941,7 @@ static int domain_init(struct dmar_domain *domain, struct 
intel_iommu *iommu,
pr_info("iova flush queue initialization failed\n");
}
 
-   domain_reserve_special_ranges(domain);
+   copy_reserved_iova(_iova_list, >iovad);
 
/* calculate AGAW */
if (guest_width > cap_mgaw(iommu->cap))
@@ -1996,7 +1991,8 @@ static void domain_exit(struct dmar_domain *domain)
domain_remove_dev_info(domain);
 
/* destroy iovas */
-   put_iova_domain(>iovad);
+   if (domain->domain.type == IOMMU_DOMAIN_DMA)
+   put_iova_domain(>iovad);
 
if (domain->pgd) {
struct page *freelist;
@@ -2793,19 +2789,9 @@ static struct dmar_domain *set_domain_for_dev(struct 
device *dev,
 }
 
 static int iommu_domain_identity_map(struct dmar_domain *domain,
-unsigned long long start,
-unsigned long long end)
+unsigned long first_vpfn,
+unsigned long last_vpfn)
 {
-   unsigned long first_vpfn = start >> VTD_PAGE_SHIFT;
-   unsigned long last_vpfn = end >> VTD_PAGE_SHIFT;
-
-   if (!reserve_iova(>iovad, dma_to_mm_pfn(first_vpfn),
- dma_to_mm_pfn(last_vpfn))) {
-   pr_err("Reserving iova failed\n");
-   return -ENOMEM;
-   }
-
-   pr_debug("Mapping reserved region %llx-%llx\n", start, end);
/*
 * RMRR range might have overlap with physical memory range,
 * clear it first
@@ -2882,7 +2868,8 @@ static int __init si_domain_init(int hw)
 
for_each_mem_pfn_range(i, nid, _pfn, _pfn, NULL) {
ret = iommu_domain_identity_map(si_domain,
-   PFN_PHYS(start_pfn), PFN_PHYS(end_pfn));
+   mm_to_dma_pfn(start_pfn),
+   mm_to_dma_pfn(end_pfn));
if (ret)
return ret;
}
@@ -4812,58 +4799,37 @@ static int intel_iommu_memory_notifier(struct 
notifier_block *nb,
   unsigned long val, void *v)
 {
struct memory_notify *mhp = v;
-   unsigned long long start, end;
-   unsigned long start_vpfn, last_vpfn;
+   unsigned long start_vpfn = mm_to_dma_pfn(mhp->start_pfn);
+   unsigned long last_vpfn = mm_to_dma_pfn(mhp->start_pfn +
+   mhp->nr_pages - 1);
 
switch (val) {
case MEM_GOING_ONLINE:
-   start = mhp->start_pfn << PAGE_SHIFT;
-   end = ((mhp->start_pfn + mhp->nr_pages) << PAGE_SHIFT) - 1;
-   if (iommu_domain_identity_map(si_domain, start, end)) {
-   pr_warn("Failed to build identity map for 
[%llx-%llx]\n",
-   start, end);
+   if (iommu_domain_identity_map(si_domain, start_vpfn,
+   last_vpfn)) {
+   pr_warn("Failed to build identity map for [%lx-%lx]\n",
+   start_vpfn, last_vpfn);
return NOTIFY_BAD;
}
break;
 
case MEM_OFFLINE:
case MEM_CANCEL_ONLINE:
-   start_vpfn = mm_to_dma_pfn(mhp->start_pfn);
-   last_vpfn = mm_to_dma_pfn(mhp->start_pfn + mhp->nr_pages - 1);
-   while (start_vpfn <= last_vpfn) {
-   struct iova *iova;
+   {
struct dmar_drhd_unit *drhd;
struct intel_iommu *iommu;
struct page *freelist;
 
-   iova = find_iova(_domain->iovad, start_vpfn);
-   if (iova == NULL) {
-   pr_debug("Failed get IOVA for PFN %lx\n",
-sta

Re: [PATCH 3/8] iommu/vt-d: Remove IOVA handling code from non-dma_ops path

2020-03-20 Thread Tom Murphy
Could we merge patch 1-3 from this series? it just cleans up weird
code and merging these patches will cover some of the work needed to
move the intel iommu driver to the dma-iommu api in the future.

On Sat, 21 Dec 2019 at 07:04, Tom Murphy  wrote:
>
> Remove all IOVA handling code from the non-dma_ops path in the intel
> iommu driver.
>
> There's no need for the non-dma_ops path to keep track of IOVAs. The
> whole point of the non-dma_ops path is that it allows the IOVAs to be
> handled separately. The IOVA handling code removed in this patch is
> pointless.
>
> Signed-off-by: Tom Murphy 
> ---
>  drivers/iommu/intel-iommu.c | 89 ++---
>  1 file changed, 33 insertions(+), 56 deletions(-)
>
> diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
> index 64b1a9793daa..8d72ea0fb843 100644
> --- a/drivers/iommu/intel-iommu.c
> +++ b/drivers/iommu/intel-iommu.c
> @@ -1908,7 +1908,8 @@ static void domain_exit(struct dmar_domain *domain)
> domain_remove_dev_info(domain);
>
> /* destroy iovas */
> -   put_iova_domain(>iovad);
> +   if (domain->domain.type == IOMMU_DOMAIN_DMA)
> +   put_iova_domain(>iovad);
>
> if (domain->pgd) {
> struct page *freelist;
> @@ -2671,19 +2672,9 @@ static struct dmar_domain *set_domain_for_dev(struct 
> device *dev,
>  }
>
>  static int iommu_domain_identity_map(struct dmar_domain *domain,
> -unsigned long long start,
> -unsigned long long end)
> +unsigned long first_vpfn,
> +unsigned long last_vpfn)
>  {
> -   unsigned long first_vpfn = start >> VTD_PAGE_SHIFT;
> -   unsigned long last_vpfn = end >> VTD_PAGE_SHIFT;
> -
> -   if (!reserve_iova(>iovad, dma_to_mm_pfn(first_vpfn),
> - dma_to_mm_pfn(last_vpfn))) {
> -   pr_err("Reserving iova failed\n");
> -   return -ENOMEM;
> -   }
> -
> -   pr_debug("Mapping reserved region %llx-%llx\n", start, end);
> /*
>  * RMRR range might have overlap with physical memory range,
>  * clear it first
> @@ -2760,7 +2751,8 @@ static int __init si_domain_init(int hw)
>
> for_each_mem_pfn_range(i, nid, _pfn, _pfn, NULL) {
> ret = iommu_domain_identity_map(si_domain,
> -   PFN_PHYS(start_pfn), 
> PFN_PHYS(end_pfn));
> +   mm_to_dma_pfn(start_pfn),
> +   mm_to_dma_pfn(end_pfn));
> if (ret)
> return ret;
> }
> @@ -4593,58 +4585,37 @@ static int intel_iommu_memory_notifier(struct 
> notifier_block *nb,
>unsigned long val, void *v)
>  {
> struct memory_notify *mhp = v;
> -   unsigned long long start, end;
> -   unsigned long start_vpfn, last_vpfn;
> +   unsigned long start_vpfn = mm_to_dma_pfn(mhp->start_pfn);
> +   unsigned long last_vpfn = mm_to_dma_pfn(mhp->start_pfn +
> +   mhp->nr_pages - 1);
>
> switch (val) {
> case MEM_GOING_ONLINE:
> -   start = mhp->start_pfn << PAGE_SHIFT;
> -   end = ((mhp->start_pfn + mhp->nr_pages) << PAGE_SHIFT) - 1;
> -   if (iommu_domain_identity_map(si_domain, start, end)) {
> -   pr_warn("Failed to build identity map for 
> [%llx-%llx]\n",
> -   start, end);
> +   if (iommu_domain_identity_map(si_domain, start_vpfn,
> +   last_vpfn)) {
> +   pr_warn("Failed to build identity map for 
> [%lx-%lx]\n",
> +   start_vpfn, last_vpfn);
> return NOTIFY_BAD;
> }
> break;
>
> case MEM_OFFLINE:
> case MEM_CANCEL_ONLINE:
> -   start_vpfn = mm_to_dma_pfn(mhp->start_pfn);
> -   last_vpfn = mm_to_dma_pfn(mhp->start_pfn + mhp->nr_pages - 1);
> -   while (start_vpfn <= last_vpfn) {
> -   struct iova *iova;
> +   {
> struct dmar_drhd_unit *drhd;
> struct intel_iommu *iommu;
> struct page *freelist;
>
> -   iova = find_iova(_domain->iovad,

Re: [PATCH 0/8] Convert the intel iommu driver to the dma-iommu api

2020-03-20 Thread Tom Murphy
Any news on this? Is there anyone who wants to try and fix this possible bug?

On Mon, 23 Dec 2019 at 03:41, Jani Nikula  wrote:
>
> On Mon, 23 Dec 2019, Robin Murphy  wrote:
> > On 2019-12-23 10:37 am, Jani Nikula wrote:
> >> On Sat, 21 Dec 2019, Tom Murphy  wrote:
> >>> This patchset converts the intel iommu driver to the dma-iommu api.
> >>>
> >>> While converting the driver I exposed a bug in the intel i915 driver
> >>> which causes a huge amount of artifacts on the screen of my
> >>> laptop. You can see a picture of it here:
> >>> https://github.com/pippy360/kernelPatches/blob/master/IMG_20191219_225922.jpg
> >>>
> >>> This issue is most likely in the i915 driver and is most likely caused
> >>> by the driver not respecting the return value of the
> >>> dma_map_ops::map_sg function. You can see the driver ignoring the
> >>> return value here:
> >>> https://github.com/torvalds/linux/blob/7e0165b2f1a912a06e381e91f0f4e495f4ac3736/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c#L51
> >>>
> >>> Previously this didn’t cause issues because the intel map_sg always
> >>> returned the same number of elements as the input scatter gather list
> >>> but with the change to this dma-iommu api this is no longer the
> >>> case. I wasn’t able to track the bug down to a specific line of code
> >>> unfortunately.
> >>>
> >>> Could someone from the intel team look at this?
> >>
> >> Let me get this straight. There is current API that on success always
> >> returns the same number of elements as the input scatter gather
> >> list. You propose to change the API so that this is no longer the case?
> >
> > No, the API for dma_map_sg() has always been that it may return fewer
> > DMA segments than nents - see Documentation/DMA-API.txt (and otherwise,
> > the return value would surely be a simple success/fail condition).
> > Relying on a particular implementation behaviour has never been strictly
> > correct, even if it does happen to be a very common behaviour.
> >
> >> A quick check of various dma_map_sg() calls in the kernel seems to
> >> indicate checking for 0 for errors and then ignoring the non-zero return
> >> is a common pattern. Are you sure it's okay to make the change you're
> >> proposing?
> >
> > Various code uses tricks like just iterating the mapped list until the
> > first segment with zero sg_dma_len(). Others may well simply have bugs.
>
> Thanks for the clarification.
>
> BR,
> Jani.
>
> >
> > Robin.
> >
> >> Anyway, due to the time of year and all, I'd like to ask you to file a
> >> bug against i915 at [1] so this is not forgotten, and please let's not
> >> merge the changes before this is resolved.
> >>
> >>
> >> Thanks,
> >> Jani.
> >>
> >>
> >> [1] https://gitlab.freedesktop.org/drm/intel/issues/new
> >>
> >>
>
> --
> Jani Nikula, Intel Open Source Graphics Center
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

[PATCH 8/8] DO NOT MERGE: iommu: disable list appending in dma-iommu

2019-12-21 Thread Tom Murphy
ops __finalise_sg

Disable combining sg segments in the dma-iommu api.
Combining the sg segments exposes a bug in the intel i915 driver which
causes visual artifacts and the screen to freeze. This is most likely
because of how the i915 handles the returned list. It probably doesn't
respect the returned value specifying the number of elements in the list
and instead depends on the previous behaviour of the intel iommu driver
which would return the same number of elements in the output list as in
the input list.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/dma-iommu.c | 38 +++---
 1 file changed, 7 insertions(+), 31 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index cf778db7d84d..d7547b912c87 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -853,8 +853,7 @@ static int __finalise_sg(struct device *dev, struct 
scatterlist *sg, int nents,
 {
struct scatterlist *s, *cur = sg;
unsigned long seg_mask = dma_get_seg_boundary(dev);
-   unsigned int cur_len = 0, max_len = dma_get_max_seg_size(dev);
-   int i, count = 0;
+   int i;
 
for_each_sg(sg, s, nents, i) {
/* Restore this segment's original unaligned fields first */
@@ -862,39 +861,16 @@ static int __finalise_sg(struct device *dev, struct 
scatterlist *sg, int nents,
unsigned int s_length = sg_dma_len(s);
unsigned int s_iova_len = s->length;
 
+   if (i > 0)
+   cur = sg_next(cur);
+
s->offset += s_iova_off;
s->length = s_length;
-   sg_dma_address(s) = DMA_MAPPING_ERROR;
-   sg_dma_len(s) = 0;
-
-   /*
-* Now fill in the real DMA data. If...
-* - there is a valid output segment to append to
-* - and this segment starts on an IOVA page boundary
-* - but doesn't fall at a segment boundary
-* - and wouldn't make the resulting output segment too long
-*/
-   if (cur_len && !s_iova_off && (dma_addr & seg_mask) &&
-   (max_len - cur_len >= s_length)) {
-   /* ...then concatenate it with the previous one */
-   cur_len += s_length;
-   } else {
-   /* Otherwise start the next output segment */
-   if (i > 0)
-   cur = sg_next(cur);
-   cur_len = s_length;
-   count++;
-
-   sg_dma_address(cur) = dma_addr + s_iova_off;
-   }
-
-   sg_dma_len(cur) = cur_len;
+   sg_dma_address(cur) = dma_addr + s_iova_off;
+   sg_dma_len(cur) = s_length;
dma_addr += s_iova_len;
-
-   if (s_length + s_iova_off < s_iova_len)
-   cur_len = 0;
}
-   return count;
+   return nents;
 }
 
 /*
-- 
2.20.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 4/8] iommu: Handle freelists when using deferred flushing in iommu drivers

2019-12-21 Thread Tom Murphy
Allow the iommu_unmap_fast to return newly freed page table pages and
pass the freelist to queue_iova in the dma-iommu ops path.

This is useful for iommu drivers (in this case the intel iommu driver)
which need to wait for the ioTLB to be flushed before newly
free/unmapped page table pages can be freed. This way we can still batch
ioTLB free operations and handle the freelists.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/amd_iommu.c   | 14 -
 drivers/iommu/arm-smmu-v3.c |  3 +-
 drivers/iommu/arm-smmu.c|  3 +-
 drivers/iommu/dma-iommu.c   | 45 ++---
 drivers/iommu/exynos-iommu.c|  3 +-
 drivers/iommu/intel-iommu.c | 51 +
 drivers/iommu/iommu.c   | 29 ++-
 drivers/iommu/ipmmu-vmsa.c  |  3 +-
 drivers/iommu/msm_iommu.c   |  3 +-
 drivers/iommu/mtk_iommu.c   |  3 +-
 drivers/iommu/mtk_iommu_v1.c|  3 +-
 drivers/iommu/omap-iommu.c  |  3 +-
 drivers/iommu/qcom_iommu.c  |  3 +-
 drivers/iommu/rockchip-iommu.c  |  3 +-
 drivers/iommu/s390-iommu.c  |  3 +-
 drivers/iommu/tegra-gart.c  |  3 +-
 drivers/iommu/tegra-smmu.c  |  3 +-
 drivers/iommu/virtio-iommu.c|  3 +-
 drivers/vfio/vfio_iommu_type1.c |  2 +-
 include/linux/iommu.h   | 25 
 20 files changed, 151 insertions(+), 57 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index bd25674ee4db..e8a4c0842624 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -2542,7 +2542,8 @@ static int amd_iommu_map(struct iommu_domain *dom, 
unsigned long iova,
 
 static size_t amd_iommu_unmap(struct iommu_domain *dom, unsigned long iova,
  size_t page_size,
- struct iommu_iotlb_gather *gather)
+ struct iommu_iotlb_gather *gather,
+ struct page **freelist)
 {
struct protection_domain *domain = to_pdomain(dom);
 
@@ -2668,6 +2669,16 @@ static void amd_iommu_flush_iotlb_all(struct 
iommu_domain *domain)
spin_unlock_irqrestore(>lock, flags);
 }
 
+static void amd_iommu_flush_iotlb_range(struct iommu_domain *domain,
+   unsigned long iova, size_t size,
+   struct page *freelist)
+{
+   struct protection_domain *dom = to_pdomain(domain);
+
+   domain_flush_pages(dom, iova, size);
+   domain_flush_complete(dom);
+}
+
 static void amd_iommu_iotlb_sync(struct iommu_domain *domain,
 struct iommu_iotlb_gather *gather)
 {
@@ -2692,6 +2703,7 @@ const struct iommu_ops amd_iommu_ops = {
.is_attach_deferred = amd_iommu_is_attach_deferred,
.pgsize_bitmap  = AMD_IOMMU_PGSIZES,
.flush_iotlb_all = amd_iommu_flush_iotlb_all,
+   .flush_iotlb_range = amd_iommu_flush_iotlb_range,
.iotlb_sync = amd_iommu_iotlb_sync,
 };
 
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index effe72eb89e7..a27d4bf4492c 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -2459,7 +2459,8 @@ static int arm_smmu_map(struct iommu_domain *domain, 
unsigned long iova,
 }
 
 static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
-size_t size, struct iommu_iotlb_gather *gather)
+size_t size, struct iommu_iotlb_gather *gather,
+struct page **freelist)
 {
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops;
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 4f1a350d9529..ea1ab3387a07 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -1205,7 +1205,8 @@ static int arm_smmu_map(struct iommu_domain *domain, 
unsigned long iova,
 }
 
 static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
-size_t size, struct iommu_iotlb_gather *gather)
+size_t size, struct iommu_iotlb_gather *gather,
+struct page **freelist)
 {
struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops;
struct arm_smmu_device *smmu = to_smmu_domain(domain)->smmu;
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 0cc702a70a96..df28facdfb8b 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -50,6 +50,19 @@ struct iommu_dma_cookie {
struct iommu_domain *fq_domain;
 };
 
+static void iommu_dma_entry_dtor(unsigned long data)
+{
+   struct page *freelist = (struct page *)data;
+
+   while (freelist != NULL) {
+   unsigned long p = (unsigned long)page_address(freelist);
+
+   freelist = freelist->freelist;
+   free_page(p);
+   }
+}
+
+
 stati

[PATCH 7/8] iommu/vt-d: Convert intel iommu driver to the iommu ops

2019-12-21 Thread Tom Murphy
Convert the intel iommu driver to the dma-iommu api. Remove the iova
handling and reserve region code from the intel iommu driver.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/Kconfig   |   1 +
 drivers/iommu/intel-iommu.c | 742 +++-
 include/linux/intel-iommu.h |   1 -
 3 files changed, 55 insertions(+), 689 deletions(-)

diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 0b9d78a0f3ac..4126bb2794c7 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -188,6 +188,7 @@ config INTEL_IOMMU
select NEED_DMA_MAP_STATE
select DMAR_TABLE
select SWIOTLB
+   select IOMMU_DMA
help
  DMA remapping (DMAR) devices support enables independent address
  translations for Direct Memory Access (DMA) from devices.
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 675ca2aa6e20..e673e1ee9288 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -31,6 +31,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -41,7 +42,6 @@
 #include 
 #include 
 #include 
-#include 
 #include 
 #include 
 #include 
@@ -380,9 +380,6 @@ EXPORT_SYMBOL_GPL(intel_iommu_gfx_mapped);
 static DEFINE_SPINLOCK(device_domain_lock);
 static LIST_HEAD(device_domain_list);
 
-#define device_needs_bounce(d) (!intel_no_bounce && dev_is_pci(d) &&   \
-   to_pci_dev(d)->untrusted)
-
 /*
  * Iterate over elements in device_domain_list and call the specified
  * callback @fn against each element.
@@ -1180,13 +1177,6 @@ static void dma_free_pagelist(struct page *freelist)
}
 }
 
-static void iova_entry_free(unsigned long data)
-{
-   struct page *freelist = (struct page *)data;
-
-   dma_free_pagelist(freelist);
-}
-
 /* iommu handling */
 static int iommu_alloc_root_entry(struct intel_iommu *iommu)
 {
@@ -1530,16 +1520,14 @@ static inline void __mapping_notify_one(struct 
intel_iommu *iommu,
iommu_flush_write_buffer(iommu);
 }
 
-static void iommu_flush_iova(struct iova_domain *iovad)
+static void intel_flush_iotlb_all(struct iommu_domain *domain)
 {
-   struct dmar_domain *domain;
+   struct dmar_domain *dmar_domain = to_dmar_domain(domain);
int idx;
 
-   domain = container_of(iovad, struct dmar_domain, iovad);
-
-   for_each_domain_iommu(idx, domain) {
+   for_each_domain_iommu(idx, dmar_domain) {
struct intel_iommu *iommu = g_iommus[idx];
-   u16 did = domain->iommu_did[iommu->seq_id];
+   u16 did = dmar_domain->iommu_did[iommu->seq_id];
 
iommu->flush.flush_iotlb(iommu, did, 0, 0, DMA_TLB_DSI_FLUSH);
 
@@ -1784,53 +1772,6 @@ static int domain_detach_iommu(struct dmar_domain 
*domain,
return count;
 }
 
-static struct iova_domain reserved_iova_list;
-static struct lock_class_key reserved_rbtree_key;
-
-static int dmar_init_reserved_ranges(void)
-{
-   struct pci_dev *pdev = NULL;
-   struct iova *iova;
-   int i;
-
-   init_iova_domain(_iova_list, VTD_PAGE_SIZE, IOVA_START_PFN);
-
-   lockdep_set_class(_iova_list.iova_rbtree_lock,
-   _rbtree_key);
-
-   /* IOAPIC ranges shouldn't be accessed by DMA */
-   iova = reserve_iova(_iova_list, IOVA_PFN(IOAPIC_RANGE_START),
-   IOVA_PFN(IOAPIC_RANGE_END));
-   if (!iova) {
-   pr_err("Reserve IOAPIC range failed\n");
-   return -ENODEV;
-   }
-
-   /* Reserve all PCI MMIO to avoid peer-to-peer access */
-   for_each_pci_dev(pdev) {
-   struct resource *r;
-
-   for (i = 0; i < PCI_NUM_RESOURCES; i++) {
-   r = >resource[i];
-   if (!r->flags || !(r->flags & IORESOURCE_MEM))
-   continue;
-   iova = reserve_iova(_iova_list,
-   IOVA_PFN(r->start),
-   IOVA_PFN(r->end));
-   if (!iova) {
-   pci_err(pdev, "Reserve iova for %pR failed\n", 
r);
-   return -ENODEV;
-   }
-   }
-   }
-   return 0;
-}
-
-static void domain_reserve_special_ranges(struct dmar_domain *domain)
-{
-   copy_reserved_iova(_iova_list, >iovad);
-}
-
 static inline int guestwidth_to_adjustwidth(int gaw)
 {
int agaw;
@@ -1850,16 +1791,11 @@ static int domain_init(struct dmar_domain *domain, 
struct intel_iommu *iommu,
 {
int adjust_width, agaw;
unsigned long sagaw;
-   int err;
-
-   init_iova_domain(>iovad, VTD_PAGE_SIZE, IOVA_START_PFN);
-
-   err = init_iova_flush_queue(>iovad,
-   iommu_flush_iova, iova_entry_free);
-   if (err)
-   retu

[PATCH 1/8] iommu/vt-d: clean up 32bit si_domain assignment

2019-12-21 Thread Tom Murphy
In the intel iommu driver devices which only support 32bit DMA can't be
direct mapped. The implementation of this is weird. Currently we assign
it a direct mapped domain and then remove the domain later and replace
it with a domain of type IOMMU_DOMAIN_IDENTITY. We should just assign it
a domain of type IOMMU_DOMAIN_IDENTITY from the begging rather than
needlessly swapping domains.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/intel-iommu.c | 88 +
 1 file changed, 31 insertions(+), 57 deletions(-)

diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 0c8d81f56a30..c1ea66467918 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -3462,46 +3462,9 @@ static struct dmar_domain 
*get_private_domain_for_dev(struct device *dev)
 }
 
 /* Check if the dev needs to go through non-identity map and unmap process.*/
-static bool iommu_need_mapping(struct device *dev)
+static bool iommu_no_mapping(struct device *dev)
 {
-   int ret;
-
-   if (iommu_dummy(dev))
-   return false;
-
-   ret = identity_mapping(dev);
-   if (ret) {
-   u64 dma_mask = *dev->dma_mask;
-
-   if (dev->coherent_dma_mask && dev->coherent_dma_mask < dma_mask)
-   dma_mask = dev->coherent_dma_mask;
-
-   if (dma_mask >= dma_direct_get_required_mask(dev))
-   return false;
-
-   /*
-* 32 bit DMA is removed from si_domain and fall back to
-* non-identity mapping.
-*/
-   dmar_remove_one_dev_info(dev);
-   ret = iommu_request_dma_domain_for_dev(dev);
-   if (ret) {
-   struct iommu_domain *domain;
-   struct dmar_domain *dmar_domain;
-
-   domain = iommu_get_domain_for_dev(dev);
-   if (domain) {
-   dmar_domain = to_dmar_domain(domain);
-   dmar_domain->flags |= DOMAIN_FLAG_LOSE_CHILDREN;
-   }
-   dmar_remove_one_dev_info(dev);
-   get_private_domain_for_dev(dev);
-   }
-
-   dev_info(dev, "32bit DMA uses non-identity mapping\n");
-   }
-
-   return true;
+   return iommu_dummy(dev) || identity_mapping(dev);
 }
 
 static dma_addr_t __intel_map_single(struct device *dev, phys_addr_t paddr,
@@ -3568,20 +3531,22 @@ static dma_addr_t intel_map_page(struct device *dev, 
struct page *page,
 enum dma_data_direction dir,
 unsigned long attrs)
 {
-   if (iommu_need_mapping(dev))
-   return __intel_map_single(dev, page_to_phys(page) + offset,
-   size, dir, *dev->dma_mask);
-   return dma_direct_map_page(dev, page, offset, size, dir, attrs);
+   if (iommu_no_mapping(dev))
+   return dma_direct_map_page(dev, page, offset, size, dir, attrs);
+
+   return __intel_map_single(dev, page_to_phys(page) + offset, size, dir,
+   *dev->dma_mask);
 }
 
 static dma_addr_t intel_map_resource(struct device *dev, phys_addr_t phys_addr,
 size_t size, enum dma_data_direction dir,
 unsigned long attrs)
 {
-   if (iommu_need_mapping(dev))
-   return __intel_map_single(dev, phys_addr, size, dir,
-   *dev->dma_mask);
-   return dma_direct_map_resource(dev, phys_addr, size, dir, attrs);
+   if (iommu_no_mapping(dev))
+   return dma_direct_map_resource(dev, phys_addr, size, dir,
+   attrs);
+
+   return __intel_map_single(dev, phys_addr, size, dir, *dev->dma_mask);
 }
 
 static void intel_unmap(struct device *dev, dma_addr_t dev_addr, size_t size)
@@ -3632,16 +3597,16 @@ static void intel_unmap_page(struct device *dev, 
dma_addr_t dev_addr,
 size_t size, enum dma_data_direction dir,
 unsigned long attrs)
 {
-   if (iommu_need_mapping(dev))
-   intel_unmap(dev, dev_addr, size);
-   else
+   if (iommu_no_mapping(dev))
dma_direct_unmap_page(dev, dev_addr, size, dir, attrs);
+   else
+   intel_unmap(dev, dev_addr, size);
 }
 
 static void intel_unmap_resource(struct device *dev, dma_addr_t dev_addr,
size_t size, enum dma_data_direction dir, unsigned long attrs)
 {
-   if (iommu_need_mapping(dev))
+   if (!iommu_no_mapping(dev))
intel_unmap(dev, dev_addr, size);
 }
 
@@ -3652,7 +3617,7 @@ static void *intel_alloc_coherent(struct device *dev, 
size_t size,
struct page *page = NULL;
int order;
 
-   if (!iommu_need_mapping(dev))
+ 

[PATCH 2/8] iommu/vt-d: Use default dma_direct_* mapping functions for direct mapped devices

2019-12-21 Thread Tom Murphy
We should only assign intel_dma_ops to devices which will actually use
the iommu and let the default fall back dma_direct_* functions handle
all other devices. This won't change any behaviour but will just use the
generic implementations for direct mapped devices rather than intel
specific ones.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/intel-iommu.c | 52 +
 1 file changed, 6 insertions(+), 46 deletions(-)

diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index c1ea66467918..64b1a9793daa 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -2794,17 +2794,6 @@ static int __init si_domain_init(int hw)
return 0;
 }
 
-static int identity_mapping(struct device *dev)
-{
-   struct device_domain_info *info;
-
-   info = dev->archdata.iommu;
-   if (info && info != DUMMY_DEVICE_DOMAIN_INFO && info != 
DEFER_DEVICE_DOMAIN_INFO)
-   return (info->domain == si_domain);
-
-   return 0;
-}
-
 static int domain_add_dev_info(struct dmar_domain *domain, struct device *dev)
 {
struct dmar_domain *ndomain;
@@ -3461,12 +3450,6 @@ static struct dmar_domain 
*get_private_domain_for_dev(struct device *dev)
return domain;
 }
 
-/* Check if the dev needs to go through non-identity map and unmap process.*/
-static bool iommu_no_mapping(struct device *dev)
-{
-   return iommu_dummy(dev) || identity_mapping(dev);
-}
-
 static dma_addr_t __intel_map_single(struct device *dev, phys_addr_t paddr,
 size_t size, int dir, u64 dma_mask)
 {
@@ -3531,9 +3514,6 @@ static dma_addr_t intel_map_page(struct device *dev, 
struct page *page,
 enum dma_data_direction dir,
 unsigned long attrs)
 {
-   if (iommu_no_mapping(dev))
-   return dma_direct_map_page(dev, page, offset, size, dir, attrs);
-
return __intel_map_single(dev, page_to_phys(page) + offset, size, dir,
*dev->dma_mask);
 }
@@ -3542,10 +3522,6 @@ static dma_addr_t intel_map_resource(struct device *dev, 
phys_addr_t phys_addr,
 size_t size, enum dma_data_direction dir,
 unsigned long attrs)
 {
-   if (iommu_no_mapping(dev))
-   return dma_direct_map_resource(dev, phys_addr, size, dir,
-   attrs);
-
return __intel_map_single(dev, phys_addr, size, dir, *dev->dma_mask);
 }
 
@@ -3597,17 +3573,13 @@ static void intel_unmap_page(struct device *dev, 
dma_addr_t dev_addr,
 size_t size, enum dma_data_direction dir,
 unsigned long attrs)
 {
-   if (iommu_no_mapping(dev))
-   dma_direct_unmap_page(dev, dev_addr, size, dir, attrs);
-   else
-   intel_unmap(dev, dev_addr, size);
+   intel_unmap(dev, dev_addr, size);
 }
 
 static void intel_unmap_resource(struct device *dev, dma_addr_t dev_addr,
size_t size, enum dma_data_direction dir, unsigned long attrs)
 {
-   if (!iommu_no_mapping(dev))
-   intel_unmap(dev, dev_addr, size);
+   intel_unmap(dev, dev_addr, size);
 }
 
 static void *intel_alloc_coherent(struct device *dev, size_t size,
@@ -3617,9 +3589,6 @@ static void *intel_alloc_coherent(struct device *dev, 
size_t size,
struct page *page = NULL;
int order;
 
-   if (iommu_no_mapping(dev))
-   return dma_direct_alloc(dev, size, dma_handle, flags, attrs);
-
size = PAGE_ALIGN(size);
order = get_order(size);
 
@@ -3653,9 +3622,6 @@ static void intel_free_coherent(struct device *dev, 
size_t size, void *vaddr,
int order;
struct page *page = virt_to_page(vaddr);
 
-   if (iommu_no_mapping(dev))
-   return dma_direct_free(dev, size, vaddr, dma_handle, attrs);
-
size = PAGE_ALIGN(size);
order = get_order(size);
 
@@ -3673,9 +3639,6 @@ static void intel_unmap_sg(struct device *dev, struct 
scatterlist *sglist,
struct scatterlist *sg;
int i;
 
-   if (iommu_no_mapping(dev))
-   return dma_direct_unmap_sg(dev, sglist, nelems, dir, attrs);
-
for_each_sg(sglist, sg, nelems, i) {
nrpages += aligned_nrpages(sg_dma_address(sg), sg_dma_len(sg));
}
@@ -3699,8 +3662,6 @@ static int intel_map_sg(struct device *dev, struct 
scatterlist *sglist, int nele
struct intel_iommu *iommu;
 
BUG_ON(dir == DMA_NONE);
-   if (iommu_no_mapping(dev))
-   return dma_direct_map_sg(dev, sglist, nelems, dir, attrs);
 
domain = deferred_attach_domain(dev);
if (!domain)
@@ -3747,8 +3708,6 @@ static int intel_map_sg(struct device *dev, struct 
scatterlist *sglist, int nele
 
 static u64 intel_get_required_mask(struct device *dev)
 {
-   if (iommu_no_mapping(

[PATCH 3/8] iommu/vt-d: Remove IOVA handling code from non-dma_ops path

2019-12-21 Thread Tom Murphy
Remove all IOVA handling code from the non-dma_ops path in the intel
iommu driver.

There's no need for the non-dma_ops path to keep track of IOVAs. The
whole point of the non-dma_ops path is that it allows the IOVAs to be
handled separately. The IOVA handling code removed in this patch is
pointless.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/intel-iommu.c | 89 ++---
 1 file changed, 33 insertions(+), 56 deletions(-)

diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 64b1a9793daa..8d72ea0fb843 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -1908,7 +1908,8 @@ static void domain_exit(struct dmar_domain *domain)
domain_remove_dev_info(domain);
 
/* destroy iovas */
-   put_iova_domain(>iovad);
+   if (domain->domain.type == IOMMU_DOMAIN_DMA)
+   put_iova_domain(>iovad);
 
if (domain->pgd) {
struct page *freelist;
@@ -2671,19 +2672,9 @@ static struct dmar_domain *set_domain_for_dev(struct 
device *dev,
 }
 
 static int iommu_domain_identity_map(struct dmar_domain *domain,
-unsigned long long start,
-unsigned long long end)
+unsigned long first_vpfn,
+unsigned long last_vpfn)
 {
-   unsigned long first_vpfn = start >> VTD_PAGE_SHIFT;
-   unsigned long last_vpfn = end >> VTD_PAGE_SHIFT;
-
-   if (!reserve_iova(>iovad, dma_to_mm_pfn(first_vpfn),
- dma_to_mm_pfn(last_vpfn))) {
-   pr_err("Reserving iova failed\n");
-   return -ENOMEM;
-   }
-
-   pr_debug("Mapping reserved region %llx-%llx\n", start, end);
/*
 * RMRR range might have overlap with physical memory range,
 * clear it first
@@ -2760,7 +2751,8 @@ static int __init si_domain_init(int hw)
 
for_each_mem_pfn_range(i, nid, _pfn, _pfn, NULL) {
ret = iommu_domain_identity_map(si_domain,
-   PFN_PHYS(start_pfn), PFN_PHYS(end_pfn));
+   mm_to_dma_pfn(start_pfn),
+   mm_to_dma_pfn(end_pfn));
if (ret)
return ret;
}
@@ -4593,58 +4585,37 @@ static int intel_iommu_memory_notifier(struct 
notifier_block *nb,
   unsigned long val, void *v)
 {
struct memory_notify *mhp = v;
-   unsigned long long start, end;
-   unsigned long start_vpfn, last_vpfn;
+   unsigned long start_vpfn = mm_to_dma_pfn(mhp->start_pfn);
+   unsigned long last_vpfn = mm_to_dma_pfn(mhp->start_pfn +
+   mhp->nr_pages - 1);
 
switch (val) {
case MEM_GOING_ONLINE:
-   start = mhp->start_pfn << PAGE_SHIFT;
-   end = ((mhp->start_pfn + mhp->nr_pages) << PAGE_SHIFT) - 1;
-   if (iommu_domain_identity_map(si_domain, start, end)) {
-   pr_warn("Failed to build identity map for 
[%llx-%llx]\n",
-   start, end);
+   if (iommu_domain_identity_map(si_domain, start_vpfn,
+   last_vpfn)) {
+   pr_warn("Failed to build identity map for [%lx-%lx]\n",
+   start_vpfn, last_vpfn);
return NOTIFY_BAD;
}
break;
 
case MEM_OFFLINE:
case MEM_CANCEL_ONLINE:
-   start_vpfn = mm_to_dma_pfn(mhp->start_pfn);
-   last_vpfn = mm_to_dma_pfn(mhp->start_pfn + mhp->nr_pages - 1);
-   while (start_vpfn <= last_vpfn) {
-   struct iova *iova;
+   {
struct dmar_drhd_unit *drhd;
struct intel_iommu *iommu;
struct page *freelist;
 
-   iova = find_iova(_domain->iovad, start_vpfn);
-   if (iova == NULL) {
-   pr_debug("Failed get IOVA for PFN %lx\n",
-start_vpfn);
-   break;
-   }
-
-   iova = split_and_remove_iova(_domain->iovad, iova,
-start_vpfn, last_vpfn);
-   if (iova == NULL) {
-   pr_warn("Failed to split IOVA PFN [%lx-%lx]\n",
-   start_vpfn, last_vpfn);
-   return NOTIFY_BAD;
-   }
-
-   freelist = domain_unmap(si_domain, iova->p

[PATCH 5/8] iommu: Add iommu_dma_free_cpu_cached_iovas function

2019-12-21 Thread Tom Murphy
to dma-iommu ops

Add a iommu_dma_free_cpu_cached_iovas function to allow drivers which
use the dma-iommu ops to free cached cpu iovas.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/dma-iommu.c | 9 +
 include/linux/dma-iommu.h | 3 +++
 2 files changed, 12 insertions(+)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index df28facdfb8b..4eac3cd35443 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -50,6 +50,15 @@ struct iommu_dma_cookie {
struct iommu_domain *fq_domain;
 };
 
+void iommu_dma_free_cpu_cached_iovas(unsigned int cpu,
+   struct iommu_domain *domain)
+{
+   struct iommu_dma_cookie *cookie = domain->iova_cookie;
+   struct iova_domain *iovad = >iovad;
+
+   free_cpu_cached_iovas(cpu, iovad);
+}
+
 static void iommu_dma_entry_dtor(unsigned long data)
 {
struct page *freelist = (struct page *)data;
diff --git a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h
index 2112f21f73d8..316d22a4a860 100644
--- a/include/linux/dma-iommu.h
+++ b/include/linux/dma-iommu.h
@@ -37,6 +37,9 @@ void iommu_dma_compose_msi_msg(struct msi_desc *desc,
 
 void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list);
 
+void iommu_dma_free_cpu_cached_iovas(unsigned int cpu,
+   struct iommu_domain *domain);
+
 #else /* CONFIG_IOMMU_DMA */
 
 struct iommu_domain;
-- 
2.20.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 6/8] iommu: allow the dma-iommu api to use bounce buffers

2019-12-21 Thread Tom Murphy
Allow the dma-iommu api to use bounce buffers for untrusted devices.
This is a copy of the intel bounce buffer code.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/dma-iommu.c | 93 ---
 drivers/iommu/iommu.c | 10 +
 include/linux/iommu.h |  9 +++-
 3 files changed, 95 insertions(+), 17 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 4eac3cd35443..cf778db7d84d 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -20,9 +20,11 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
+#include 
 
 struct iommu_dma_msi_page {
struct list_headlist;
@@ -505,29 +507,89 @@ static void __iommu_dma_unmap(struct device *dev, 
dma_addr_t dma_addr,
iommu_tlb_sync(domain, _gather);
}
 
+
iommu_dma_free_iova(cookie, dma_addr, size, freelist);
 }
 
+static void __iommu_dma_unmap_swiotlb(struct device *dev, dma_addr_t dma_addr,
+   size_t size, enum dma_data_direction dir,
+   unsigned long attrs)
+{
+   struct iommu_domain *domain = iommu_get_dma_domain(dev);
+   struct iommu_dma_cookie *cookie = domain->iova_cookie;
+   struct iova_domain *iovad = >iovad;
+   size_t iova_off = iova_offset(iovad, dma_addr);
+   size_t aligned_size = iova_align(iovad, size + iova_off);
+   phys_addr_t phys;
+
+   phys = iommu_iova_to_phys(domain, dma_addr);
+   if (WARN_ON(!phys))
+   return;
+
+   __iommu_dma_unmap(dev, dma_addr, size);
+
+#ifdef CONFIG_SWIOTLB
+   if (unlikely(is_swiotlb_buffer(phys)))
+   swiotlb_tbl_unmap_single(dev, phys, size,
+   aligned_size, dir, attrs);
+#endif
+}
+
 static dma_addr_t __iommu_dma_map(struct device *dev, phys_addr_t phys,
-   size_t size, int prot, dma_addr_t dma_mask)
+   size_t org_size, dma_addr_t dma_mask, bool coherent,
+   enum dma_data_direction dir, unsigned long attrs)
 {
+   int prot = dma_info_to_prot(dir, coherent, attrs);
struct iommu_domain *domain = iommu_get_dma_domain(dev);
struct iommu_dma_cookie *cookie = domain->iova_cookie;
struct iova_domain *iovad = >iovad;
size_t iova_off = iova_offset(iovad, phys);
+   size_t aligned_size = iova_align(iovad, org_size + iova_off);
dma_addr_t iova;
 
if (unlikely(iommu_dma_deferred_attach(dev, domain)))
return DMA_MAPPING_ERROR;
 
-   size = iova_align(iovad, size + iova_off);
+#ifdef CONFIG_SWIOTLB
+   /*
+* If both the physical buffer start address and size are
+* page aligned, we don't need to use a bounce page.
+*/
+   if (iommu_needs_bounce_buffer(dev)
+   && !iova_offset(iovad, phys | org_size)) {
+   phys = swiotlb_tbl_map_single(dev,
+   __phys_to_dma(dev, io_tlb_start),
+   phys, org_size, aligned_size, dir, attrs);
+
+   if (phys == DMA_MAPPING_ERROR)
+   return DMA_MAPPING_ERROR;
+
+   /* Cleanup the padding area. */
+   void *padding_start = phys_to_virt(phys);
+   size_t padding_size = aligned_size;
+
+   if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
+   (dir == DMA_TO_DEVICE ||
+dir == DMA_BIDIRECTIONAL)) {
+   padding_start += org_size;
+   padding_size -= org_size;
+   }
 
-   iova = iommu_dma_alloc_iova(domain, size, dma_mask, dev);
+   memset(padding_start, 0, padding_size);
+   }
+#endif
+
+   iova = iommu_dma_alloc_iova(domain, aligned_size, dma_mask, dev);
if (!iova)
return DMA_MAPPING_ERROR;
 
-   if (iommu_map_atomic(domain, iova, phys - iova_off, size, prot)) {
-   iommu_dma_free_iova(cookie, iova, size, NULL);
+   if (iommu_map_atomic(domain, iova, phys - iova_off, aligned_size,
+   prot)) {
+
+   if (unlikely(is_swiotlb_buffer(phys)))
+   swiotlb_tbl_unmap_single(dev, phys, aligned_size,
+   aligned_size, dir, attrs);
+   iommu_dma_free_iova(cookie, iova, aligned_size, NULL);
return DMA_MAPPING_ERROR;
}
return iova + iova_off;
@@ -761,10 +823,10 @@ static dma_addr_t iommu_dma_map_page(struct device *dev, 
struct page *page,
 {
phys_addr_t phys = page_to_phys(page) + offset;
bool coherent = dev_is_dma_coherent(dev);
-   int prot = dma_info_to_prot(dir, coherent, attrs);
dma_addr_t dma_handle;
 
-   dma_handle = __iommu_dma_map(dev, phys, size, prot, dma_get_mask(dev));
+   dma_handle = __iommu_dma_map(dev, phys, size, dma_get_mask(dev),
+   

[PATCH 0/8] Convert the intel iommu driver to the dma-iommu api

2019-12-21 Thread Tom Murphy
This patchset converts the intel iommu driver to the dma-iommu api.

While converting the driver I exposed a bug in the intel i915 driver which 
causes a huge amount of artifacts on the screen of my laptop. You can see a 
picture of it here:
https://github.com/pippy360/kernelPatches/blob/master/IMG_20191219_225922.jpg

This issue is most likely in the i915 driver and is most likely caused by the 
driver not respecting the return value of the dma_map_ops::map_sg function. You 
can see the driver ignoring the return value here:
https://github.com/torvalds/linux/blob/7e0165b2f1a912a06e381e91f0f4e495f4ac3736/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c#L51

Previously this didn’t cause issues because the intel map_sg always returned 
the same number of elements as the input scatter gather list but with the 
change to this dma-iommu api this is no longer the case. I wasn’t able to track 
the bug down to a specific line of code unfortunately.  

Could someone from the intel team look at this?


I have been testing on a lenovo x1 carbon 5th generation. Let me know if 
there’s any more information you need.

To allow my patch set to be tested I have added a patch (patch 8/8) in this 
series to disable combining sg segments in the dma-iommu api which fixes the 
bug but it doesn't fix the actual problem.

As part of this patch series I copied the intel bounce buffer code to the 
dma-iommu path. The addition of the bounce buffer code took me by surprise. I 
did most of my development on this patch series before the bounce buffer code 
was added and my reimplementation in the dma-iommu path is very rushed and not 
properly tested but I’m running out of time to work on this patch set.

On top of that I also didn’t port over the intel tracing code from this commit:
https://github.com/torvalds/linux/commit/3b53034c268d550d9e8522e613a14ab53b8840d8#diff-6b3e7c4993f05e76331e463ab1fc87e1
So all the work in that commit is now wasted. The code will need to be removed 
and reimplemented in the dma-iommu path. I would like to take the time to do 
this but I really don’t have the time at the moment and I want to get these 
changes out before the iommu code changes any more.


Tom Murphy (8):
  iommu/vt-d: clean up 32bit si_domain assignment
  iommu/vt-d: Use default dma_direct_* mapping functions for direct
mapped devices
  iommu/vt-d: Remove IOVA handling code from non-dma_ops path
  iommu: Handle freelists when using deferred flushing in iommu drivers
  iommu: Add iommu_dma_free_cpu_cached_iovas function
  iommu: allow the dma-iommu api to use bounce buffers
  iommu/vt-d: Convert intel iommu driver to the iommu ops
  DO NOT MERGE: iommu: disable list appending in dma-iommu

 drivers/iommu/Kconfig   |   1 +
 drivers/iommu/amd_iommu.c   |  14 +-
 drivers/iommu/arm-smmu-v3.c |   3 +-
 drivers/iommu/arm-smmu.c|   3 +-
 drivers/iommu/dma-iommu.c   | 183 +--
 drivers/iommu/exynos-iommu.c|   3 +-
 drivers/iommu/intel-iommu.c | 936 
 drivers/iommu/iommu.c   |  39 +-
 drivers/iommu/ipmmu-vmsa.c  |   3 +-
 drivers/iommu/msm_iommu.c   |   3 +-
 drivers/iommu/mtk_iommu.c   |   3 +-
 drivers/iommu/mtk_iommu_v1.c|   3 +-
 drivers/iommu/omap-iommu.c  |   3 +-
 drivers/iommu/qcom_iommu.c  |   3 +-
 drivers/iommu/rockchip-iommu.c  |   3 +-
 drivers/iommu/s390-iommu.c  |   3 +-
 drivers/iommu/tegra-gart.c  |   3 +-
 drivers/iommu/tegra-smmu.c  |   3 +-
 drivers/iommu/virtio-iommu.c|   3 +-
 drivers/vfio/vfio_iommu_type1.c |   2 +-
 include/linux/dma-iommu.h   |   3 +
 include/linux/intel-iommu.h |   1 -
 include/linux/iommu.h   |  32 +-
 23 files changed, 345 insertions(+), 908 deletions(-)

-- 
2.20.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

[PATCH V6 5/5] iommu/amd: Convert AMD iommu driver to the dma-iommu api

2019-09-08 Thread Tom Murphy
Convert the AMD iommu driver to the dma-iommu api. Remove the iova
handling and reserve region code from the AMD iommu driver.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/Kconfig |   1 +
 drivers/iommu/amd_iommu.c | 677 --
 2 files changed, 68 insertions(+), 610 deletions(-)

diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index e15cdcd8cb3c..437428571512 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -138,6 +138,7 @@ config AMD_IOMMU
select PCI_PASID
select IOMMU_API
select IOMMU_IOVA
+   select IOMMU_DMA
depends on X86_64 && PCI && ACPI
---help---
  With this option you can enable support for AMD IOMMU hardware in
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 0e53f9bd2be7..eb4801031a99 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -20,6 +20,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -89,8 +90,6 @@ const struct iommu_ops amd_iommu_ops;
 static ATOMIC_NOTIFIER_HEAD(ppr_notifier);
 int amd_iommu_max_glx_val = -1;
 
-static const struct dma_map_ops amd_iommu_dma_ops;
-
 /*
  * general struct to manage commands send to an IOMMU
  */
@@ -103,21 +102,6 @@ struct kmem_cache *amd_iommu_irq_cache;
 static void update_domain(struct protection_domain *domain);
 static int protection_domain_init(struct protection_domain *domain);
 static void detach_device(struct device *dev);
-static void iova_domain_flush_tlb(struct iova_domain *iovad);
-
-/*
- * Data container for a dma_ops specific protection domain
- */
-struct dma_ops_domain {
-   /* generic protection domain information */
-   struct protection_domain domain;
-
-   /* IOVA RB-Tree */
-   struct iova_domain iovad;
-};
-
-static struct iova_domain reserved_iova_ranges;
-static struct lock_class_key reserved_rbtree_key;
 
 /
  *
@@ -188,12 +172,6 @@ static struct protection_domain *to_pdomain(struct 
iommu_domain *dom)
return container_of(dom, struct protection_domain, domain);
 }
 
-static struct dma_ops_domain* to_dma_ops_domain(struct protection_domain 
*domain)
-{
-   BUG_ON(domain->flags != PD_DMA_OPS_MASK);
-   return container_of(domain, struct dma_ops_domain, domain);
-}
-
 static struct iommu_dev_data *alloc_dev_data(u16 devid)
 {
struct iommu_dev_data *dev_data;
@@ -1267,12 +1245,6 @@ static void domain_flush_pages(struct protection_domain 
*domain,
__domain_flush_pages(domain, address, size, 0);
 }
 
-/* Flush the whole IO/TLB for a given protection domain */
-static void domain_flush_tlb(struct protection_domain *domain)
-{
-   __domain_flush_pages(domain, 0, CMD_INV_IOMMU_ALL_PAGES_ADDRESS, 0);
-}
-
 /* Flush the whole IO/TLB for a given protection domain - including PDE */
 static void domain_flush_tlb_pde(struct protection_domain *domain)
 {
@@ -1674,43 +1646,6 @@ static unsigned long iommu_unmap_page(struct 
protection_domain *dom,
return unmapped;
 }
 
-/
- *
- * The next functions belong to the address allocator for the dma_ops
- * interface functions.
- *
- /
-
-
-static unsigned long dma_ops_alloc_iova(struct device *dev,
-   struct dma_ops_domain *dma_dom,
-   unsigned int pages, u64 dma_mask)
-{
-   unsigned long pfn = 0;
-
-   pages = __roundup_pow_of_two(pages);
-
-   if (dma_mask > DMA_BIT_MASK(32))
-   pfn = alloc_iova_fast(_dom->iovad, pages,
- IOVA_PFN(DMA_BIT_MASK(32)), false);
-
-   if (!pfn)
-   pfn = alloc_iova_fast(_dom->iovad, pages,
- IOVA_PFN(dma_mask), true);
-
-   return (pfn << PAGE_SHIFT);
-}
-
-static void dma_ops_free_iova(struct dma_ops_domain *dma_dom,
- unsigned long address,
- unsigned int pages)
-{
-   pages = __roundup_pow_of_two(pages);
-   address >>= PAGE_SHIFT;
-
-   free_iova_fast(_dom->iovad, address, pages);
-}
-
 /
  *
  * The next functions belong to the domain allocation. A domain is
@@ -1787,38 +1722,23 @@ static void free_gcr3_table(struct protection_domain 
*domain)
free_page((unsigned long)domain->gcr3_tbl);
 }
 
-static void dma_ops_domain_flush_tlb(struct dma_ops_domain *dom)
-{
-   domain_flush_tlb(>domain);
-   domain_flush_complete(>domain);
-}
-
-static void iova_domain_flush_tlb(struct iova_domain *iovad)
-{
-   struct dma_ops_domain *dom;
-
-   dom = container_of(iovad, struct dma_ops_do

[PATCH V6 3/5] iommu/dma-iommu: Handle deferred devices

2019-09-08 Thread Tom Murphy
Handle devices which defer their attach to the iommu in the dma-iommu api

Signed-off-by: Tom Murphy 
Reviewed-by: Robin Murphy 
---
 drivers/iommu/dma-iommu.c | 27 ++-
 1 file changed, 26 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 2712fbc68b28..bd09b6b31c4e 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -22,6 +22,7 @@
 #include 
 #include 
 #include 
+#include 
 
 struct iommu_dma_msi_page {
struct list_headlist;
@@ -351,6 +352,21 @@ static int iommu_dma_init_domain(struct iommu_domain 
*domain, dma_addr_t base,
return iova_reserve_iommu_regions(dev, domain);
 }
 
+static int iommu_dma_deferred_attach(struct device *dev,
+   struct iommu_domain *domain)
+{
+   const struct iommu_ops *ops = domain->ops;
+
+   if (!is_kdump_kernel())
+   return 0;
+
+   if (unlikely(ops->is_attach_deferred &&
+   ops->is_attach_deferred(domain, dev)))
+   return iommu_attach_device(domain, dev);
+
+   return 0;
+}
+
 /**
  * dma_info_to_prot - Translate DMA API directions and attributes to IOMMU API
  *page flags.
@@ -463,6 +479,9 @@ static dma_addr_t __iommu_dma_map(struct device *dev, 
phys_addr_t phys,
size_t iova_off = iova_offset(iovad, phys);
dma_addr_t iova;
 
+   if (unlikely(iommu_dma_deferred_attach(dev, domain)))
+   return DMA_MAPPING_ERROR;
+
size = iova_align(iovad, size + iova_off);
 
iova = iommu_dma_alloc_iova(domain, size, dma_get_mask(dev), dev);
@@ -581,6 +600,9 @@ static void *iommu_dma_alloc_remap(struct device *dev, 
size_t size,
 
*dma_handle = DMA_MAPPING_ERROR;
 
+   if (unlikely(iommu_dma_deferred_attach(dev, domain)))
+   return NULL;
+
min_size = alloc_sizes & -alloc_sizes;
if (min_size < PAGE_SIZE) {
min_size = PAGE_SIZE;
@@ -713,7 +735,7 @@ static dma_addr_t iommu_dma_map_page(struct device *dev, 
struct page *page,
int prot = dma_info_to_prot(dir, coherent, attrs);
dma_addr_t dma_handle;
 
-   dma_handle =__iommu_dma_map(dev, phys, size, prot);
+   dma_handle = __iommu_dma_map(dev, phys, size, prot);
if (!coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
dma_handle != DMA_MAPPING_ERROR)
arch_sync_dma_for_device(dev, phys, size, dir);
@@ -823,6 +845,9 @@ static int iommu_dma_map_sg(struct device *dev, struct 
scatterlist *sg,
unsigned long mask = dma_get_seg_boundary(dev);
int i;
 
+   if (unlikely(iommu_dma_deferred_attach(dev, domain)))
+   return 0;
+
if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC))
iommu_dma_sync_sg_for_device(dev, sg, nents, dir);
 
-- 
2.20.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH V6 1/5] iommu/amd: Remove unnecessary locking from AMD iommu driver

2019-09-08 Thread Tom Murphy
With or without locking it doesn't make sense for two writers to be
writing to the same IOVA range at the same time. Even with locking we
still have a race condition, whoever gets the lock first, so we still
can't be sure what the result will be. With locking the result will be
more sane, it will be correct for the last writer, but still useless
because we can't be sure which writer will get the lock last. It's a
fundamentally broken design to have two writers writing to the same
IOVA range at the same time.

So we can remove the locking and work on the assumption that no two
writers will be writing to the same IOVA range at the same time.

The only exception is when we have to allocate a middle page in the page
tables, the middle page can cover more than just the IOVA range a writer
has been allocated. However this isn't an issue in the AMD driver
because it can atomically allocate middle pages using "cmpxchg64()".

Signed-off-by: Tom Murphy 
---
 drivers/iommu/amd_iommu.c   | 10 +-
 drivers/iommu/amd_iommu_types.h |  1 -
 2 files changed, 1 insertion(+), 10 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 008da21a2592..1948be7ac8f8 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -2858,7 +2858,6 @@ static void protection_domain_free(struct 
protection_domain *domain)
 static int protection_domain_init(struct protection_domain *domain)
 {
spin_lock_init(>lock);
-   mutex_init(>api_lock);
domain->id = domain_id_alloc();
if (!domain->id)
return -ENOMEM;
@@ -3045,9 +3044,7 @@ static int amd_iommu_map(struct iommu_domain *dom, 
unsigned long iova,
if (iommu_prot & IOMMU_WRITE)
prot |= IOMMU_PROT_IW;
 
-   mutex_lock(>api_lock);
ret = iommu_map_page(domain, iova, paddr, page_size, prot, GFP_KERNEL);
-   mutex_unlock(>api_lock);
 
domain_flush_np_cache(domain, iova, page_size);
 
@@ -3058,16 +3055,11 @@ static size_t amd_iommu_unmap(struct iommu_domain *dom, 
unsigned long iova,
   size_t page_size)
 {
struct protection_domain *domain = to_pdomain(dom);
-   size_t unmap_size;
 
if (domain->mode == PAGE_MODE_NONE)
return 0;
 
-   mutex_lock(>api_lock);
-   unmap_size = iommu_unmap_page(domain, iova, page_size);
-   mutex_unlock(>api_lock);
-
-   return unmap_size;
+   return iommu_unmap_page(domain, iova, page_size);
 }
 
 static phys_addr_t amd_iommu_iova_to_phys(struct iommu_domain *dom,
diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h
index 9ac229e92b07..b764e1a73dcf 100644
--- a/drivers/iommu/amd_iommu_types.h
+++ b/drivers/iommu/amd_iommu_types.h
@@ -468,7 +468,6 @@ struct protection_domain {
struct iommu_domain domain; /* generic domain handle used by
   iommu core code */
spinlock_t lock;/* mostly used to lock the page table*/
-   struct mutex api_lock;  /* protect page tables in the iommu-api path */
u16 id; /* the domain id written to the device table */
int mode;   /* paging mode (0-6 levels) */
u64 *pt_root;   /* page table root pointer */
-- 
2.20.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH V6 4/5] iommu/dma-iommu: Use the dev->coherent_dma_mask

2019-09-08 Thread Tom Murphy
Use the dev->coherent_dma_mask when allocating in the dma-iommu ops api.

Signed-off-by: Tom Murphy 
Reviewed-by: Robin Murphy 
Reviewed-by: Christoph Hellwig 
---
 drivers/iommu/dma-iommu.c | 12 +++-
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index bd09b6b31c4e..0cf52fae1471 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -471,7 +471,7 @@ static void __iommu_dma_unmap(struct device *dev, 
dma_addr_t dma_addr,
 }
 
 static dma_addr_t __iommu_dma_map(struct device *dev, phys_addr_t phys,
-   size_t size, int prot)
+   size_t size, int prot, dma_addr_t dma_mask)
 {
struct iommu_domain *domain = iommu_get_dma_domain(dev);
struct iommu_dma_cookie *cookie = domain->iova_cookie;
@@ -484,7 +484,7 @@ static dma_addr_t __iommu_dma_map(struct device *dev, 
phys_addr_t phys,
 
size = iova_align(iovad, size + iova_off);
 
-   iova = iommu_dma_alloc_iova(domain, size, dma_get_mask(dev), dev);
+   iova = iommu_dma_alloc_iova(domain, size, dma_mask, dev);
if (!iova)
return DMA_MAPPING_ERROR;
 
@@ -735,7 +735,7 @@ static dma_addr_t iommu_dma_map_page(struct device *dev, 
struct page *page,
int prot = dma_info_to_prot(dir, coherent, attrs);
dma_addr_t dma_handle;
 
-   dma_handle = __iommu_dma_map(dev, phys, size, prot);
+   dma_handle = __iommu_dma_map(dev, phys, size, prot, dma_get_mask(dev));
if (!coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
dma_handle != DMA_MAPPING_ERROR)
arch_sync_dma_for_device(dev, phys, size, dir);
@@ -938,7 +938,8 @@ static dma_addr_t iommu_dma_map_resource(struct device 
*dev, phys_addr_t phys,
size_t size, enum dma_data_direction dir, unsigned long attrs)
 {
return __iommu_dma_map(dev, phys, size,
-   dma_info_to_prot(dir, false, attrs) | IOMMU_MMIO);
+   dma_info_to_prot(dir, false, attrs) | IOMMU_MMIO,
+   dma_get_mask(dev));
 }
 
 static void iommu_dma_unmap_resource(struct device *dev, dma_addr_t handle,
@@ -1041,7 +1042,8 @@ static void *iommu_dma_alloc(struct device *dev, size_t 
size,
if (!cpu_addr)
return NULL;
 
-   *handle = __iommu_dma_map(dev, page_to_phys(page), size, ioprot);
+   *handle = __iommu_dma_map(dev, page_to_phys(page), size, ioprot,
+   dev->coherent_dma_mask);
if (*handle == DMA_MAPPING_ERROR) {
__iommu_dma_free(dev, size, cpu_addr);
return NULL;
-- 
2.20.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH V6 2/5] iommu: Add gfp parameter to iommu_ops::map

2019-09-08 Thread Tom Murphy
Add a gfp_t parameter to the iommu_ops::map function.
Remove the needless locking in the AMD iommu driver.

The iommu_ops::map function (or the iommu_map function which calls it)
was always supposed to be sleepable (according to Joerg's comment in
this thread: https://lore.kernel.org/patchwork/patch/977520/ ) and so
should probably have had a "might_sleep()" since it was written. However
currently the dma-iommu api can call iommu_map in an atomic context,
which it shouldn't do. This doesn't cause any problems because any iommu
driver which uses the dma-iommu api uses gfp_atomic in it's
iommu_ops::map function. But doing this wastes the memory allocators
atomic pools.

Signed-off-by: Tom Murphy 
Reviewed-by: Robin Murphy 
Reviewed-by: Christoph Hellwig 
---
 drivers/iommu/amd_iommu.c  |  3 ++-
 drivers/iommu/arm-smmu-v3.c|  2 +-
 drivers/iommu/arm-smmu.c   |  2 +-
 drivers/iommu/dma-iommu.c  |  6 ++---
 drivers/iommu/exynos-iommu.c   |  2 +-
 drivers/iommu/intel-iommu.c|  2 +-
 drivers/iommu/iommu.c  | 43 +-
 drivers/iommu/ipmmu-vmsa.c |  2 +-
 drivers/iommu/msm_iommu.c  |  2 +-
 drivers/iommu/mtk_iommu.c  |  2 +-
 drivers/iommu/mtk_iommu_v1.c   |  2 +-
 drivers/iommu/omap-iommu.c |  2 +-
 drivers/iommu/qcom_iommu.c |  2 +-
 drivers/iommu/rockchip-iommu.c |  2 +-
 drivers/iommu/s390-iommu.c |  2 +-
 drivers/iommu/tegra-gart.c |  2 +-
 drivers/iommu/tegra-smmu.c |  2 +-
 drivers/iommu/virtio-iommu.c   |  2 +-
 include/linux/iommu.h  | 21 -
 19 files changed, 77 insertions(+), 26 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 1948be7ac8f8..0e53f9bd2be7 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -3030,7 +3030,8 @@ static int amd_iommu_attach_device(struct iommu_domain 
*dom,
 }
 
 static int amd_iommu_map(struct iommu_domain *dom, unsigned long iova,
-phys_addr_t paddr, size_t page_size, int iommu_prot)
+phys_addr_t paddr, size_t page_size, int iommu_prot,
+gfp_t gfp)
 {
struct protection_domain *domain = to_pdomain(dom);
int prot = 0;
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index e7f49fd1a7ba..acc0eae7963f 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -1975,7 +1975,7 @@ static int arm_smmu_attach_dev(struct iommu_domain 
*domain, struct device *dev)
 }
 
 static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
-   phys_addr_t paddr, size_t size, int prot)
+   phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
 {
struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops;
 
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index aa06498f291d..05f42bdee494 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -1284,7 +1284,7 @@ static int arm_smmu_attach_dev(struct iommu_domain 
*domain, struct device *dev)
 }
 
 static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
-   phys_addr_t paddr, size_t size, int prot)
+   phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
 {
struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops;
struct arm_smmu_device *smmu = to_smmu_domain(domain)->smmu;
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index d991d40f797f..2712fbc68b28 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -469,7 +469,7 @@ static dma_addr_t __iommu_dma_map(struct device *dev, 
phys_addr_t phys,
if (!iova)
return DMA_MAPPING_ERROR;
 
-   if (iommu_map(domain, iova, phys - iova_off, size, prot)) {
+   if (iommu_map_atomic(domain, iova, phys - iova_off, size, prot)) {
iommu_dma_free_iova(cookie, iova, size);
return DMA_MAPPING_ERROR;
}
@@ -613,7 +613,7 @@ static void *iommu_dma_alloc_remap(struct device *dev, 
size_t size,
arch_dma_prep_coherent(sg_page(sg), sg->length);
}
 
-   if (iommu_map_sg(domain, iova, sgt.sgl, sgt.orig_nents, ioprot)
+   if (iommu_map_sg_atomic(domain, iova, sgt.sgl, sgt.orig_nents, ioprot)
< size)
goto out_free_sg;
 
@@ -873,7 +873,7 @@ static int iommu_dma_map_sg(struct device *dev, struct 
scatterlist *sg,
 * We'll leave any physical concatenation to the IOMMU driver's
 * implementation - it knows better than we do.
 */
-   if (iommu_map_sg(domain, iova, sg, nents, prot) < iova_len)
+   if (iommu_map_sg_atomic(domain, iova, sg, nents, prot) < iova_len)
goto out_free_iova;
 
return __finalise_sg(dev, sg, nents, iova);
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/ex

[PATCH v6 0/5] iommu/amd: Convert the AMD iommu driver to the dma-iommu api

2019-09-08 Thread Tom Murphy
Convert the AMD iommu driver to the dma-iommu api. Remove the iova
handling and reserve region code from the AMD iommu driver.

Change-log:
V6:
-add more details to the description of patch 
001-iommu-amd-Remove-unnecessary-locking-from-AMD-iommu-.patch
-rename handle_deferred_device to iommu_dma_deferred_attach
-fix double tabs in 0003-iommu-dma-iommu-Handle-deferred-devices.patch
V5:
-Rebase on top of linux-next
V4:
-Rebase on top of linux-next
-Split the removing of the unnecessary locking in the amd iommu driver into a 
seperate patch
-refactor the "iommu/dma-iommu: Handle deferred devices" patch and address 
comments
v3:
-rename dma_limit to dma_mask
-exit handle_deferred_device early if (!is_kdump_kernel())
-remove pointless calls to handle_deferred_device
v2:
-Rebase on top of this series:
 http://git.infradead.org/users/hch/misc.git/shortlog/refs/heads/dma-iommu-ops.3
-Add a gfp_t parameter to the iommu_ops::map function.
-Made use of the reserve region code inside the dma-iommu api

Tom Murphy (5):
  iommu/amd: Remove unnecessary locking from AMD iommu driver
  iommu: Add gfp parameter to iommu_ops::map
  iommu/dma-iommu: Handle deferred devices
  iommu/dma-iommu: Use the dev->coherent_dma_mask
  iommu/amd: Convert AMD iommu driver to the dma-iommu api

 drivers/iommu/Kconfig   |   1 +
 drivers/iommu/amd_iommu.c   | 690 
 drivers/iommu/amd_iommu_types.h |   1 -
 drivers/iommu/arm-smmu-v3.c |   2 +-
 drivers/iommu/arm-smmu.c|   2 +-
 drivers/iommu/dma-iommu.c   |  43 +-
 drivers/iommu/exynos-iommu.c|   2 +-
 drivers/iommu/intel-iommu.c |   2 +-
 drivers/iommu/iommu.c   |  43 +-
 drivers/iommu/ipmmu-vmsa.c  |   2 +-
 drivers/iommu/msm_iommu.c   |   2 +-
 drivers/iommu/mtk_iommu.c   |   2 +-
 drivers/iommu/mtk_iommu_v1.c|   2 +-
 drivers/iommu/omap-iommu.c  |   2 +-
 drivers/iommu/qcom_iommu.c  |   2 +-
 drivers/iommu/rockchip-iommu.c  |   2 +-
 drivers/iommu/s390-iommu.c  |   2 +-
 drivers/iommu/tegra-gart.c  |   2 +-
 drivers/iommu/tegra-smmu.c  |   2 +-
 drivers/iommu/virtio-iommu.c|   2 +-
 include/linux/iommu.h   |  21 +-
 21 files changed, 178 insertions(+), 651 deletions(-)

-- 
2.20.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH] Remove wrong default domain comments

2019-08-25 Thread Tom Murphy
These comments are wrong. request_default_domain_for_dev doesn't just
handle direct mapped domains.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/iommu.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index ea95080372e7..3b6807e7a2d8 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -2179,7 +2179,6 @@ request_default_domain_for_dev(struct device *dev, 
unsigned long type)
 
mutex_lock(>mutex);
 
-   /* Check if the default domain is already direct mapped */
ret = 0;
if (group->default_domain && group->default_domain->type == type)
goto out;
@@ -2189,7 +2188,6 @@ request_default_domain_for_dev(struct device *dev, 
unsigned long type)
if (iommu_group_device_count(group) != 1)
goto out;
 
-   /* Allocate a direct mapped domain */
ret = -ENOMEM;
domain = __iommu_domain_alloc(dev->bus, type);
if (!domain)
@@ -2204,7 +2202,7 @@ request_default_domain_for_dev(struct device *dev, 
unsigned long type)
 
iommu_group_create_direct_mappings(group, dev);
 
-   /* Make the direct mapped domain the default for this group */
+   /* Make the domain the default for this group */
if (group->default_domain)
iommu_domain_free(group->default_domain);
group->default_domain = domain;
-- 
2.20.1



Re: [PATCH V5 1/5] iommu/amd: Remove unnecessary locking from AMD iommu driver

2019-08-24 Thread Tom Murphy
>I have to admit I don't fully understand the concurrency issues here, but 
>neither do I understand what the mutex you removed might have helped to start 
>with.

Each range in the page tables is protected by the IO virtual address
allocator. The iommu driver allocates an IOVA range using locks before
it writes to a page table range. The IOVA allocator acts like a lock
on a specific range of the page tables. So we can handle most of the
concurrency issues in the IOVA allocator and avoid locking while
writing to a range in the page tables.

However because we have multiple levels of pages we might have to
allocate a middle page (a PMD) which covers more than the IOVA range
we have allocated.
To solve this we could use locks:

//pseudo code
lock_page_table()
if (we need to allocate middle pages) {
 //allocate the page
 //set the PMD value
}
unlock_page_table()

but we can actually avoid having any locking by doing the following:

//pseudo code
if (we need to allocate middle pages) {
 //allocate the page
 //cmpxchg64 to set the PMD if it wasn't already set since we last checked
 if (the PMD was set while since we last checked)
   //free the page we just allocated
}

In this case we can end up doing a pointless page allocate and free
but it's worth it to avoid using locks

You can see this in the intel iommu code here:
https://github.com/torvalds/linux/blob/9140d8bdd4c5a04abe181bb300378355d56990a4/drivers/iommu/intel-iommu.c#L904

>what the mutex you removed might have helped to start with.
The mutex I removed is arguably completely useless.

In the dma ops path we handle the IOVA allocations in the driver so we
can be sure a certain range is protected by the IOVA allocator.

Because the iommu ops path doesn't handle the IOVA allocations it
seems reasonable to lock the page tables to avoid two writers writing
to the same range at the same time. Without the lock it's complete
chaos and all writers can be writing to the same range at the same
time resulting in complete garbage.
BUT the locking doesn't actually make any real difference. Even with
locking we still have a race condition if two writers want to write to
the same range at the same time, the race is just whoever gets the
lock first, we still can't be sure what the result will be. So the
result is still garbage, just slightly more usable garbage because at
least the range is correct for one writer.
It just makes no sense to ever have two writers writing to the same
range and adding a lock doesn't fix that.
Already the Intel iommu ops path doesn't use locks for it's page table
so this isn't a new idea I'm just doing the same for the AMD iommu
driver

Does all that make sense?

On Tue, 20 Aug 2019 at 10:41, Christoph Hellwig  wrote:
>
> On Thu, Aug 15, 2019 at 12:09:39PM +0100, Tom Murphy wrote:
> > We can remove the mutex lock from amd_iommu_map and amd_iommu_unmap.
> > iommu_map doesn’t lock while mapping and so no two calls should touch
> > the same iova range. The AMD driver already handles the page table page
> > allocations without locks so we can safely remove the locks.
>
> I've been looking over the code and trying to understand how the
> synchronization works.  I gues we the cmpxchg64 in free_clear_pte
> is the important point here?  I have to admit I don't fully understand
> the concurrency issues here, but neither do I understand what the
> mutex you removed might have helped to start with.


Re: [PATCH V5 3/5] iommu/dma-iommu: Handle deferred devices

2019-08-17 Thread Tom Murphy
On Sat, 17 Aug 2019 at 04:39, Hillf Danton  wrote:
>
>
> On Thu, 15 Aug 2019 12:09:41 +0100 Tom Murphy wrote:
> >
> > Handle devices which defer their attach to the iommu in the dma-iommu api
> >
> > Signed-off-by: Tom Murphy 
> > ---
> >  drivers/iommu/dma-iommu.c | 27 ++-
> >  1 file changed, 26 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
> > index 2712fbc68b28..906b7fa14d3c 100644
> > --- a/drivers/iommu/dma-iommu.c
> > +++ b/drivers/iommu/dma-iommu.c
> > @@ -22,6 +22,7 @@
> >  #include 
> >  #include 
> >  #include 
> > +#include 
> >
> >  struct iommu_dma_msi_page {
> >   struct list_headlist;
> > @@ -351,6 +352,21 @@ static int iommu_dma_init_domain(struct iommu_domain 
> > *domain, dma_addr_t base,
> >   return iova_reserve_iommu_regions(dev, domain);
> >  }
> >
> > +static int handle_deferred_device(struct device *dev,
> > + struct iommu_domain *domain)
> > +{
> > + const struct iommu_ops *ops = domain->ops;
> > +
> > + if (!is_kdump_kernel())
> > + return 0;
> > +
> > + if (unlikely(ops->is_attach_deferred &&
> > + ops->is_attach_deferred(domain, dev)))
> > + return iommu_attach_device(domain, dev);
> > +
> > + return 0;
> > +}
> > +
> >  /**
> >   * dma_info_to_prot - Translate DMA API directions and attributes to IOMMU 
> > API
> >   *page flags.
> > @@ -463,6 +479,9 @@ static dma_addr_t __iommu_dma_map(struct device *dev, 
> > phys_addr_t phys,
> >   size_t iova_off = iova_offset(iovad, phys);
> >   dma_addr_t iova;
> >
> > + if (unlikely(handle_deferred_device(dev, domain)))
> > + return DMA_MAPPING_ERROR;
> > +
> >   size = iova_align(iovad, size + iova_off);
> >
> >   iova = iommu_dma_alloc_iova(domain, size, dma_get_mask(dev), dev);
>
> iommu_map_atomic() is applied to __iommu_dma_map() in 2/5.
> Is it an atomic context currently given the mutex_lock() in
> iommu_attach_device()?

I don't see your point here. __iommu_dma_map isn't called from
iommu_attach_device, why would we care about a mutex in
iommu_attach_device?

__iommu_dma_map can be called from an atomic context (it isn't always
but it does happen). __iommu_dma_map is called by iommu_dma_alloc
which implements the iommu_dma_ops::alloc function which by design
needs to be callable from an atomic context. Does that answer your
question?

>
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH v4 0/5] iommu/amd: Convert the AMD iommu driver to the dma-iommu api

2019-08-15 Thread Tom Murphy
Done, I just sent it there. I don't have any AMD hardware to test on
while I'm traveling. However the rebase was very straightforward and
the code was tested a month ago on the old linux-next.

I only have the AMD conversion done. I will work on rebasing the intel
one when I get a chance.

On Tue, 13 Aug 2019 at 14:07, Christoph Hellwig  wrote:
>
> On Tue, Aug 13, 2019 at 08:09:26PM +0800, Tom Murphy wrote:
> > Hi Christoph,
> >
> > I quit my job and am having a great time traveling South East Asia.
>
> Enjoy!  I just returned from my vacation.
>
> > I definitely don't want this work to go to waste and I hope to repost it
> > later this week but I can't guarantee it.
> >
> > Let me know if you need this urgently.
>
> It isn't in any strict sense urgent.  I just have various DMA API plans
> that I'd rather just implement in dma-direct and dma-iommu rather than
> also in two additional commonly used iommu drivers.  So on the one had
> the sooner the better, on the other hand no real urgency.
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH V5 5/5] iommu/amd: Convert AMD iommu driver to the dma-iommu api

2019-08-15 Thread Tom Murphy
Convert the AMD iommu driver to the dma-iommu api. Remove the iova
handling and reserve region code from the AMD iommu driver.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/Kconfig |   1 +
 drivers/iommu/amd_iommu.c | 677 --
 2 files changed, 68 insertions(+), 610 deletions(-)

diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index e15cdcd8cb3c..437428571512 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -138,6 +138,7 @@ config AMD_IOMMU
select PCI_PASID
select IOMMU_API
select IOMMU_IOVA
+   select IOMMU_DMA
depends on X86_64 && PCI && ACPI
---help---
  With this option you can enable support for AMD IOMMU hardware in
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 0e53f9bd2be7..eb4801031a99 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -20,6 +20,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -89,8 +90,6 @@ const struct iommu_ops amd_iommu_ops;
 static ATOMIC_NOTIFIER_HEAD(ppr_notifier);
 int amd_iommu_max_glx_val = -1;
 
-static const struct dma_map_ops amd_iommu_dma_ops;
-
 /*
  * general struct to manage commands send to an IOMMU
  */
@@ -103,21 +102,6 @@ struct kmem_cache *amd_iommu_irq_cache;
 static void update_domain(struct protection_domain *domain);
 static int protection_domain_init(struct protection_domain *domain);
 static void detach_device(struct device *dev);
-static void iova_domain_flush_tlb(struct iova_domain *iovad);
-
-/*
- * Data container for a dma_ops specific protection domain
- */
-struct dma_ops_domain {
-   /* generic protection domain information */
-   struct protection_domain domain;
-
-   /* IOVA RB-Tree */
-   struct iova_domain iovad;
-};
-
-static struct iova_domain reserved_iova_ranges;
-static struct lock_class_key reserved_rbtree_key;
 
 /
  *
@@ -188,12 +172,6 @@ static struct protection_domain *to_pdomain(struct 
iommu_domain *dom)
return container_of(dom, struct protection_domain, domain);
 }
 
-static struct dma_ops_domain* to_dma_ops_domain(struct protection_domain 
*domain)
-{
-   BUG_ON(domain->flags != PD_DMA_OPS_MASK);
-   return container_of(domain, struct dma_ops_domain, domain);
-}
-
 static struct iommu_dev_data *alloc_dev_data(u16 devid)
 {
struct iommu_dev_data *dev_data;
@@ -1267,12 +1245,6 @@ static void domain_flush_pages(struct protection_domain 
*domain,
__domain_flush_pages(domain, address, size, 0);
 }
 
-/* Flush the whole IO/TLB for a given protection domain */
-static void domain_flush_tlb(struct protection_domain *domain)
-{
-   __domain_flush_pages(domain, 0, CMD_INV_IOMMU_ALL_PAGES_ADDRESS, 0);
-}
-
 /* Flush the whole IO/TLB for a given protection domain - including PDE */
 static void domain_flush_tlb_pde(struct protection_domain *domain)
 {
@@ -1674,43 +1646,6 @@ static unsigned long iommu_unmap_page(struct 
protection_domain *dom,
return unmapped;
 }
 
-/
- *
- * The next functions belong to the address allocator for the dma_ops
- * interface functions.
- *
- /
-
-
-static unsigned long dma_ops_alloc_iova(struct device *dev,
-   struct dma_ops_domain *dma_dom,
-   unsigned int pages, u64 dma_mask)
-{
-   unsigned long pfn = 0;
-
-   pages = __roundup_pow_of_two(pages);
-
-   if (dma_mask > DMA_BIT_MASK(32))
-   pfn = alloc_iova_fast(_dom->iovad, pages,
- IOVA_PFN(DMA_BIT_MASK(32)), false);
-
-   if (!pfn)
-   pfn = alloc_iova_fast(_dom->iovad, pages,
- IOVA_PFN(dma_mask), true);
-
-   return (pfn << PAGE_SHIFT);
-}
-
-static void dma_ops_free_iova(struct dma_ops_domain *dma_dom,
- unsigned long address,
- unsigned int pages)
-{
-   pages = __roundup_pow_of_two(pages);
-   address >>= PAGE_SHIFT;
-
-   free_iova_fast(_dom->iovad, address, pages);
-}
-
 /
  *
  * The next functions belong to the domain allocation. A domain is
@@ -1787,38 +1722,23 @@ static void free_gcr3_table(struct protection_domain 
*domain)
free_page((unsigned long)domain->gcr3_tbl);
 }
 
-static void dma_ops_domain_flush_tlb(struct dma_ops_domain *dom)
-{
-   domain_flush_tlb(>domain);
-   domain_flush_complete(>domain);
-}
-
-static void iova_domain_flush_tlb(struct iova_domain *iovad)
-{
-   struct dma_ops_domain *dom;
-
-   dom = container_of(iovad, struct dma_ops_do

[PATCH V5 3/5] iommu/dma-iommu: Handle deferred devices

2019-08-15 Thread Tom Murphy
Handle devices which defer their attach to the iommu in the dma-iommu api

Signed-off-by: Tom Murphy 
---
 drivers/iommu/dma-iommu.c | 27 ++-
 1 file changed, 26 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 2712fbc68b28..906b7fa14d3c 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -22,6 +22,7 @@
 #include 
 #include 
 #include 
+#include 
 
 struct iommu_dma_msi_page {
struct list_headlist;
@@ -351,6 +352,21 @@ static int iommu_dma_init_domain(struct iommu_domain 
*domain, dma_addr_t base,
return iova_reserve_iommu_regions(dev, domain);
 }
 
+static int handle_deferred_device(struct device *dev,
+   struct iommu_domain *domain)
+{
+   const struct iommu_ops *ops = domain->ops;
+
+   if (!is_kdump_kernel())
+   return 0;
+
+   if (unlikely(ops->is_attach_deferred &&
+   ops->is_attach_deferred(domain, dev)))
+   return iommu_attach_device(domain, dev);
+
+   return 0;
+}
+
 /**
  * dma_info_to_prot - Translate DMA API directions and attributes to IOMMU API
  *page flags.
@@ -463,6 +479,9 @@ static dma_addr_t __iommu_dma_map(struct device *dev, 
phys_addr_t phys,
size_t iova_off = iova_offset(iovad, phys);
dma_addr_t iova;
 
+   if (unlikely(handle_deferred_device(dev, domain)))
+   return DMA_MAPPING_ERROR;
+
size = iova_align(iovad, size + iova_off);
 
iova = iommu_dma_alloc_iova(domain, size, dma_get_mask(dev), dev);
@@ -581,6 +600,9 @@ static void *iommu_dma_alloc_remap(struct device *dev, 
size_t size,
 
*dma_handle = DMA_MAPPING_ERROR;
 
+   if (unlikely(handle_deferred_device(dev, domain)))
+   return NULL;
+
min_size = alloc_sizes & -alloc_sizes;
if (min_size < PAGE_SIZE) {
min_size = PAGE_SIZE;
@@ -713,7 +735,7 @@ static dma_addr_t iommu_dma_map_page(struct device *dev, 
struct page *page,
int prot = dma_info_to_prot(dir, coherent, attrs);
dma_addr_t dma_handle;
 
-   dma_handle =__iommu_dma_map(dev, phys, size, prot);
+   dma_handle = __iommu_dma_map(dev, phys, size, prot);
if (!coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
dma_handle != DMA_MAPPING_ERROR)
arch_sync_dma_for_device(dev, phys, size, dir);
@@ -823,6 +845,9 @@ static int iommu_dma_map_sg(struct device *dev, struct 
scatterlist *sg,
unsigned long mask = dma_get_seg_boundary(dev);
int i;
 
+   if (unlikely(handle_deferred_device(dev, domain)))
+   return 0;
+
if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC))
iommu_dma_sync_sg_for_device(dev, sg, nents, dir);
 
-- 
2.20.1



[PATCH V5 4/5] iommu/dma-iommu: Use the dev->coherent_dma_mask

2019-08-15 Thread Tom Murphy
Use the dev->coherent_dma_mask when allocating in the dma-iommu ops api.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/dma-iommu.c | 12 +++-
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 906b7fa14d3c..b9a3ab02434b 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -471,7 +471,7 @@ static void __iommu_dma_unmap(struct device *dev, 
dma_addr_t dma_addr,
 }
 
 static dma_addr_t __iommu_dma_map(struct device *dev, phys_addr_t phys,
-   size_t size, int prot)
+   size_t size, int prot, dma_addr_t dma_mask)
 {
struct iommu_domain *domain = iommu_get_dma_domain(dev);
struct iommu_dma_cookie *cookie = domain->iova_cookie;
@@ -484,7 +484,7 @@ static dma_addr_t __iommu_dma_map(struct device *dev, 
phys_addr_t phys,
 
size = iova_align(iovad, size + iova_off);
 
-   iova = iommu_dma_alloc_iova(domain, size, dma_get_mask(dev), dev);
+   iova = iommu_dma_alloc_iova(domain, size, dma_mask, dev);
if (!iova)
return DMA_MAPPING_ERROR;
 
@@ -735,7 +735,7 @@ static dma_addr_t iommu_dma_map_page(struct device *dev, 
struct page *page,
int prot = dma_info_to_prot(dir, coherent, attrs);
dma_addr_t dma_handle;
 
-   dma_handle = __iommu_dma_map(dev, phys, size, prot);
+   dma_handle = __iommu_dma_map(dev, phys, size, prot, dma_get_mask(dev));
if (!coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
dma_handle != DMA_MAPPING_ERROR)
arch_sync_dma_for_device(dev, phys, size, dir);
@@ -938,7 +938,8 @@ static dma_addr_t iommu_dma_map_resource(struct device 
*dev, phys_addr_t phys,
size_t size, enum dma_data_direction dir, unsigned long attrs)
 {
return __iommu_dma_map(dev, phys, size,
-   dma_info_to_prot(dir, false, attrs) | IOMMU_MMIO);
+   dma_info_to_prot(dir, false, attrs) | IOMMU_MMIO,
+   dma_get_mask(dev));
 }
 
 static void iommu_dma_unmap_resource(struct device *dev, dma_addr_t handle,
@@ -1041,7 +1042,8 @@ static void *iommu_dma_alloc(struct device *dev, size_t 
size,
if (!cpu_addr)
return NULL;
 
-   *handle = __iommu_dma_map(dev, page_to_phys(page), size, ioprot);
+   *handle = __iommu_dma_map(dev, page_to_phys(page), size, ioprot,
+   dev->coherent_dma_mask);
if (*handle == DMA_MAPPING_ERROR) {
__iommu_dma_free(dev, size, cpu_addr);
return NULL;
-- 
2.20.1



[PATCH V5 2/5] iommu: Add gfp parameter to iommu_ops::map

2019-08-15 Thread Tom Murphy
Add a gfp_t parameter to the iommu_ops::map function.
Remove the needless locking in the AMD iommu driver.

The iommu_ops::map function (or the iommu_map function which calls it)
was always supposed to be sleepable (according to Joerg's comment in
this thread: https://lore.kernel.org/patchwork/patch/977520/ ) and so
should probably have had a "might_sleep()" since it was written. However
currently the dma-iommu api can call iommu_map in an atomic context,
which it shouldn't do. This doesn't cause any problems because any iommu
driver which uses the dma-iommu api uses gfp_atomic in it's
iommu_ops::map function. But doing this wastes the memory allocators
atomic pools.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/amd_iommu.c  |  3 ++-
 drivers/iommu/arm-smmu-v3.c|  2 +-
 drivers/iommu/arm-smmu.c   |  2 +-
 drivers/iommu/dma-iommu.c  |  6 ++---
 drivers/iommu/exynos-iommu.c   |  2 +-
 drivers/iommu/intel-iommu.c|  2 +-
 drivers/iommu/iommu.c  | 43 +-
 drivers/iommu/ipmmu-vmsa.c |  2 +-
 drivers/iommu/msm_iommu.c  |  2 +-
 drivers/iommu/mtk_iommu.c  |  2 +-
 drivers/iommu/mtk_iommu_v1.c   |  2 +-
 drivers/iommu/omap-iommu.c |  2 +-
 drivers/iommu/qcom_iommu.c |  2 +-
 drivers/iommu/rockchip-iommu.c |  2 +-
 drivers/iommu/s390-iommu.c |  2 +-
 drivers/iommu/tegra-gart.c |  2 +-
 drivers/iommu/tegra-smmu.c |  2 +-
 drivers/iommu/virtio-iommu.c   |  2 +-
 include/linux/iommu.h  | 21 -
 19 files changed, 77 insertions(+), 26 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 1948be7ac8f8..0e53f9bd2be7 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -3030,7 +3030,8 @@ static int amd_iommu_attach_device(struct iommu_domain 
*dom,
 }
 
 static int amd_iommu_map(struct iommu_domain *dom, unsigned long iova,
-phys_addr_t paddr, size_t page_size, int iommu_prot)
+phys_addr_t paddr, size_t page_size, int iommu_prot,
+gfp_t gfp)
 {
struct protection_domain *domain = to_pdomain(dom);
int prot = 0;
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index e7f49fd1a7ba..acc0eae7963f 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -1975,7 +1975,7 @@ static int arm_smmu_attach_dev(struct iommu_domain 
*domain, struct device *dev)
 }
 
 static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
-   phys_addr_t paddr, size_t size, int prot)
+   phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
 {
struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops;
 
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index aa06498f291d..05f42bdee494 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -1284,7 +1284,7 @@ static int arm_smmu_attach_dev(struct iommu_domain 
*domain, struct device *dev)
 }
 
 static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
-   phys_addr_t paddr, size_t size, int prot)
+   phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
 {
struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops;
struct arm_smmu_device *smmu = to_smmu_domain(domain)->smmu;
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index d991d40f797f..2712fbc68b28 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -469,7 +469,7 @@ static dma_addr_t __iommu_dma_map(struct device *dev, 
phys_addr_t phys,
if (!iova)
return DMA_MAPPING_ERROR;
 
-   if (iommu_map(domain, iova, phys - iova_off, size, prot)) {
+   if (iommu_map_atomic(domain, iova, phys - iova_off, size, prot)) {
iommu_dma_free_iova(cookie, iova, size);
return DMA_MAPPING_ERROR;
}
@@ -613,7 +613,7 @@ static void *iommu_dma_alloc_remap(struct device *dev, 
size_t size,
arch_dma_prep_coherent(sg_page(sg), sg->length);
}
 
-   if (iommu_map_sg(domain, iova, sgt.sgl, sgt.orig_nents, ioprot)
+   if (iommu_map_sg_atomic(domain, iova, sgt.sgl, sgt.orig_nents, ioprot)
< size)
goto out_free_sg;
 
@@ -873,7 +873,7 @@ static int iommu_dma_map_sg(struct device *dev, struct 
scatterlist *sg,
 * We'll leave any physical concatenation to the IOMMU driver's
 * implementation - it knows better than we do.
 */
-   if (iommu_map_sg(domain, iova, sg, nents, prot) < iova_len)
+   if (iommu_map_sg_atomic(domain, iova, sg, nents, prot) < iova_len)
goto out_free_iova;
 
return __finalise_sg(dev, sg, nents, iova);
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c
index 1934c16a5abc..b7dd46884692 100644
--- a/drivers/iom

[PATCH V5 1/5] iommu/amd: Remove unnecessary locking from AMD iommu driver

2019-08-15 Thread Tom Murphy
We can remove the mutex lock from amd_iommu_map and amd_iommu_unmap.
iommu_map doesn’t lock while mapping and so no two calls should touch
the same iova range. The AMD driver already handles the page table page
allocations without locks so we can safely remove the locks.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/amd_iommu.c   | 10 +-
 drivers/iommu/amd_iommu_types.h |  1 -
 2 files changed, 1 insertion(+), 10 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 008da21a2592..1948be7ac8f8 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -2858,7 +2858,6 @@ static void protection_domain_free(struct 
protection_domain *domain)
 static int protection_domain_init(struct protection_domain *domain)
 {
spin_lock_init(>lock);
-   mutex_init(>api_lock);
domain->id = domain_id_alloc();
if (!domain->id)
return -ENOMEM;
@@ -3045,9 +3044,7 @@ static int amd_iommu_map(struct iommu_domain *dom, 
unsigned long iova,
if (iommu_prot & IOMMU_WRITE)
prot |= IOMMU_PROT_IW;
 
-   mutex_lock(>api_lock);
ret = iommu_map_page(domain, iova, paddr, page_size, prot, GFP_KERNEL);
-   mutex_unlock(>api_lock);
 
domain_flush_np_cache(domain, iova, page_size);
 
@@ -3058,16 +3055,11 @@ static size_t amd_iommu_unmap(struct iommu_domain *dom, 
unsigned long iova,
   size_t page_size)
 {
struct protection_domain *domain = to_pdomain(dom);
-   size_t unmap_size;
 
if (domain->mode == PAGE_MODE_NONE)
return 0;
 
-   mutex_lock(>api_lock);
-   unmap_size = iommu_unmap_page(domain, iova, page_size);
-   mutex_unlock(>api_lock);
-
-   return unmap_size;
+   return iommu_unmap_page(domain, iova, page_size);
 }
 
 static phys_addr_t amd_iommu_iova_to_phys(struct iommu_domain *dom,
diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h
index 9ac229e92b07..b764e1a73dcf 100644
--- a/drivers/iommu/amd_iommu_types.h
+++ b/drivers/iommu/amd_iommu_types.h
@@ -468,7 +468,6 @@ struct protection_domain {
struct iommu_domain domain; /* generic domain handle used by
   iommu core code */
spinlock_t lock;/* mostly used to lock the page table*/
-   struct mutex api_lock;  /* protect page tables in the iommu-api path */
u16 id; /* the domain id written to the device table */
int mode;   /* paging mode (0-6 levels) */
u64 *pt_root;   /* page table root pointer */
-- 
2.20.1



[PATCH V5 0/5] iommu/amd: Convert the AMD iommu driver to the dma-iommu api

2019-08-15 Thread Tom Murphy
Convert the AMD iommu driver to the dma-iommu api. Remove the iova
handling and reserve region code from the AMD iommu driver.

Change-log:
V5:
-Rebase on top of linux-next
V4:
-Rebase on top of linux-next
-Split the removing of the unnecessary locking in the amd iommu driver into a 
seperate patch
-refactor the "iommu/dma-iommu: Handle deferred devices" patch and address 
comments
v3:
-rename dma_limit to dma_mask
-exit handle_deferred_device early if (!is_kdump_kernel())
-remove pointless calls to handle_deferred_device
v2:
-Rebase on top of this series:
 http://git.infradead.org/users/hch/misc.git/shortlog/refs/heads/dma-iommu-ops.3
-Add a gfp_t parameter to the iommu_ops::map function.
-Made use of the reserve region code inside the dma-iommu api

Tom Murphy (5):
  iommu/amd: Remove unnecessary locking from AMD iommu driver
  iommu: Add gfp parameter to iommu_ops::map
  iommu/dma-iommu: Handle deferred devices
  iommu/dma-iommu: Use the dev->coherent_dma_mask
  iommu/amd: Convert AMD iommu driver to the dma-iommu api

 drivers/iommu/Kconfig   |   1 +
 drivers/iommu/amd_iommu.c   | 690 
 drivers/iommu/amd_iommu_types.h |   1 -
 drivers/iommu/arm-smmu-v3.c |   2 +-
 drivers/iommu/arm-smmu.c|   2 +-
 drivers/iommu/dma-iommu.c   |  43 +-
 drivers/iommu/exynos-iommu.c|   2 +-
 drivers/iommu/intel-iommu.c |   2 +-
 drivers/iommu/iommu.c   |  43 +-
 drivers/iommu/ipmmu-vmsa.c  |   2 +-
 drivers/iommu/msm_iommu.c   |   2 +-
 drivers/iommu/mtk_iommu.c   |   2 +-
 drivers/iommu/mtk_iommu_v1.c|   2 +-
 drivers/iommu/omap-iommu.c  |   2 +-
 drivers/iommu/qcom_iommu.c  |   2 +-
 drivers/iommu/rockchip-iommu.c  |   2 +-
 drivers/iommu/s390-iommu.c  |   2 +-
 drivers/iommu/tegra-gart.c  |   2 +-
 drivers/iommu/tegra-smmu.c  |   2 +-
 drivers/iommu/virtio-iommu.c|   2 +-
 include/linux/iommu.h   |  21 +-
 21 files changed, 178 insertions(+), 651 deletions(-)

-- 
2.20.1



Re: [PATCH v4 0/5] iommu/amd: Convert the AMD iommu driver to the dma-iommu api

2019-08-13 Thread Tom Murphy
Hi Christoph,

I quit my job and am having a great time traveling South East Asia.

I definitely don't want this work to go to waste and I hope to repost it
later this week but I can't guarantee it.

Let me know if you need this urgently.

Thanks,
Tom

On Sat 10 Aug 2019, 3:20 p.m. Christoph Hellwig,  wrote:

> On Sun, Jun 23, 2019 at 11:19:45PM -0700, Christoph Hellwig wrote:
> > Tom,
> >
> > next time please cc Jerg as the AMD IOMMU maintainer.
> >
> > Joerg, any chance you could review this?  Toms patches to convert the
> > AMD and Intel IOMMU drivers to the dma-iommu code are going to make my
> > life in DMA land significantly easier, so I have a vested interest
> > in this series moving forward :)
>
> Tom, can you repost the series?  Seems like there hasn't been any
> news for a month.
>
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

[PATCH v4 5/5] iommu/amd: Convert AMD iommu driver to the dma-iommu api

2019-06-13 Thread Tom Murphy
Convert the AMD iommu driver to the dma-iommu api. Remove the iova
handling and reserve region code from the AMD iommu driver.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/Kconfig |   1 +
 drivers/iommu/amd_iommu.c | 677 --
 2 files changed, 68 insertions(+), 610 deletions(-)

diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index d47913883d1e..19f966db02a8 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -138,6 +138,7 @@ config AMD_IOMMU
select PCI_PASID
select IOMMU_API
select IOMMU_IOVA
+   select IOMMU_DMA
depends on X86_64 && PCI && ACPI
---help---
  With this option you can enable support for AMD IOMMU hardware in
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index fd8da60f7359..ed881c2d8a6b 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -20,6 +20,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -89,8 +90,6 @@ const struct iommu_ops amd_iommu_ops;
 static ATOMIC_NOTIFIER_HEAD(ppr_notifier);
 int amd_iommu_max_glx_val = -1;
 
-static const struct dma_map_ops amd_iommu_dma_ops;
-
 /*
  * general struct to manage commands send to an IOMMU
  */
@@ -103,21 +102,6 @@ struct kmem_cache *amd_iommu_irq_cache;
 static void update_domain(struct protection_domain *domain);
 static int protection_domain_init(struct protection_domain *domain);
 static void detach_device(struct device *dev);
-static void iova_domain_flush_tlb(struct iova_domain *iovad);
-
-/*
- * Data container for a dma_ops specific protection domain
- */
-struct dma_ops_domain {
-   /* generic protection domain information */
-   struct protection_domain domain;
-
-   /* IOVA RB-Tree */
-   struct iova_domain iovad;
-};
-
-static struct iova_domain reserved_iova_ranges;
-static struct lock_class_key reserved_rbtree_key;
 
 /
  *
@@ -188,12 +172,6 @@ static struct protection_domain *to_pdomain(struct 
iommu_domain *dom)
return container_of(dom, struct protection_domain, domain);
 }
 
-static struct dma_ops_domain* to_dma_ops_domain(struct protection_domain 
*domain)
-{
-   BUG_ON(domain->flags != PD_DMA_OPS_MASK);
-   return container_of(domain, struct dma_ops_domain, domain);
-}
-
 static struct iommu_dev_data *alloc_dev_data(u16 devid)
 {
struct iommu_dev_data *dev_data;
@@ -1267,12 +1245,6 @@ static void domain_flush_pages(struct protection_domain 
*domain,
__domain_flush_pages(domain, address, size, 0);
 }
 
-/* Flush the whole IO/TLB for a given protection domain */
-static void domain_flush_tlb(struct protection_domain *domain)
-{
-   __domain_flush_pages(domain, 0, CMD_INV_IOMMU_ALL_PAGES_ADDRESS, 0);
-}
-
 /* Flush the whole IO/TLB for a given protection domain - including PDE */
 static void domain_flush_tlb_pde(struct protection_domain *domain)
 {
@@ -1674,43 +1646,6 @@ static unsigned long iommu_unmap_page(struct 
protection_domain *dom,
return unmapped;
 }
 
-/
- *
- * The next functions belong to the address allocator for the dma_ops
- * interface functions.
- *
- /
-
-
-static unsigned long dma_ops_alloc_iova(struct device *dev,
-   struct dma_ops_domain *dma_dom,
-   unsigned int pages, u64 dma_mask)
-{
-   unsigned long pfn = 0;
-
-   pages = __roundup_pow_of_two(pages);
-
-   if (dma_mask > DMA_BIT_MASK(32))
-   pfn = alloc_iova_fast(_dom->iovad, pages,
- IOVA_PFN(DMA_BIT_MASK(32)), false);
-
-   if (!pfn)
-   pfn = alloc_iova_fast(_dom->iovad, pages,
- IOVA_PFN(dma_mask), true);
-
-   return (pfn << PAGE_SHIFT);
-}
-
-static void dma_ops_free_iova(struct dma_ops_domain *dma_dom,
- unsigned long address,
- unsigned int pages)
-{
-   pages = __roundup_pow_of_two(pages);
-   address >>= PAGE_SHIFT;
-
-   free_iova_fast(_dom->iovad, address, pages);
-}
-
 /
  *
  * The next functions belong to the domain allocation. A domain is
@@ -1787,38 +1722,23 @@ static void free_gcr3_table(struct protection_domain 
*domain)
free_page((unsigned long)domain->gcr3_tbl);
 }
 
-static void dma_ops_domain_flush_tlb(struct dma_ops_domain *dom)
-{
-   domain_flush_tlb(>domain);
-   domain_flush_complete(>domain);
-}
-
-static void iova_domain_flush_tlb(struct iova_domain *iovad)
-{
-   struct dma_ops_domain *dom;
-
-   dom = container_of(iovad, struct dma_ops_do

[PATCH v4 0/5] iommu/amd: Convert the AMD iommu driver to the dma-iommu api

2019-06-13 Thread Tom Murphy
Convert the AMD iommu driver to the dma-iommu api. Remove the iova
handling and reserve region code from the AMD iommu driver.

Change-log:
V4:
-Rebase on top of linux-next
-Split the removing of the unnecessary locking in the amd iommu driver into a 
seperate patch
-refactor the "iommu/dma-iommu: Handle deferred devices" patch and address 
comments
v3:
-rename dma_limit to dma_mask
-exit handle_deferred_device early if (!is_kdump_kernel())
-remove pointless calls to handle_deferred_device
v2:
-Rebase on top of this series:
 http://git.infradead.org/users/hch/misc.git/shortlog/refs/heads/dma-iommu-ops.3
-Add a gfp_t parameter to the iommu_ops::map function.
-Made use of the reserve region code inside the dma-iommu api

Tom Murphy (5):
  iommu/amd: Remove unnecessary locking from AMD iommu driver
  iommu: Add gfp parameter to iommu_ops::map
  iommu/dma-iommu: Handle deferred devices
  iommu/dma-iommu: Use the dev->coherent_dma_mask
  iommu/amd: Convert AMD iommu driver to the dma-iommu api

 drivers/iommu/Kconfig   |   1 +
 drivers/iommu/amd_iommu.c   | 690 
 drivers/iommu/amd_iommu_types.h |   1 -
 drivers/iommu/arm-smmu-v3.c |   2 +-
 drivers/iommu/arm-smmu.c|   2 +-
 drivers/iommu/dma-iommu.c   |  45 ++-
 drivers/iommu/exynos-iommu.c|   2 +-
 drivers/iommu/intel-iommu.c |   2 +-
 drivers/iommu/iommu.c   |  43 +-
 drivers/iommu/ipmmu-vmsa.c  |   2 +-
 drivers/iommu/msm_iommu.c   |   2 +-
 drivers/iommu/mtk_iommu.c   |   2 +-
 drivers/iommu/mtk_iommu_v1.c|   2 +-
 drivers/iommu/omap-iommu.c  |   2 +-
 drivers/iommu/qcom_iommu.c  |   2 +-
 drivers/iommu/rockchip-iommu.c  |   2 +-
 drivers/iommu/s390-iommu.c  |   2 +-
 drivers/iommu/tegra-gart.c  |   2 +-
 drivers/iommu/tegra-smmu.c  |   2 +-
 drivers/iommu/virtio-iommu.c|   2 +-
 include/linux/iommu.h   |  21 +-
 21 files changed, 179 insertions(+), 652 deletions(-)

-- 
2.20.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v4 4/5] iommu/dma-iommu: Use the dev->coherent_dma_mask

2019-06-13 Thread Tom Murphy
Use the dev->coherent_dma_mask when allocating in the dma-iommu ops api.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/dma-iommu.c | 14 --
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index f303bbe20e51..082fb789e3cf 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -471,7 +471,7 @@ static void __iommu_dma_unmap(struct device *dev, 
dma_addr_t dma_addr,
 }
 
 static dma_addr_t __iommu_dma_map(struct device *dev, phys_addr_t phys,
-   size_t size, int prot)
+   size_t size, int prot, dma_addr_t dma_mask)
 {
struct iommu_domain *domain = iommu_get_dma_domain(dev);
struct iommu_dma_cookie *cookie = domain->iova_cookie;
@@ -486,7 +486,7 @@ static dma_addr_t __iommu_dma_map(struct device *dev, 
phys_addr_t phys,
size = iova_align(>iovad, size + iova_off);
}
 
-   iova = iommu_dma_alloc_iova(domain, size, dma_get_mask(dev), dev);
+   iova = iommu_dma_alloc_iova(domain, size, dma_mask, dev);
if (!iova)
return DMA_MAPPING_ERROR;
 
@@ -737,7 +737,7 @@ static dma_addr_t iommu_dma_map_page(struct device *dev, 
struct page *page,
int prot = dma_info_to_prot(dir, coherent, attrs);
dma_addr_t dma_handle;
 
-   dma_handle = __iommu_dma_map(dev, phys, size, prot);
+   dma_handle = __iommu_dma_map(dev, phys, size, prot, dma_get_mask(dev));
if (!coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
dma_handle != DMA_MAPPING_ERROR)
arch_sync_dma_for_device(dev, phys, size, dir);
@@ -940,7 +940,8 @@ static dma_addr_t iommu_dma_map_resource(struct device 
*dev, phys_addr_t phys,
size_t size, enum dma_data_direction dir, unsigned long attrs)
 {
return __iommu_dma_map(dev, phys, size,
-   dma_info_to_prot(dir, false, attrs) | IOMMU_MMIO);
+   dma_info_to_prot(dir, false, attrs) | IOMMU_MMIO,
+   dma_get_mask(dev));
 }
 
 static void iommu_dma_unmap_resource(struct device *dev, dma_addr_t handle,
@@ -1049,7 +1050,8 @@ static void *iommu_dma_alloc(struct device *dev, size_t 
size,
if (!cpu_addr)
return NULL;
 
-   *handle = __iommu_dma_map(dev, page_to_phys(page), size, ioprot);
+   *handle = __iommu_dma_map(dev, page_to_phys(page), size, ioprot,
+   dev->coherent_dma_mask);
if (*handle == DMA_MAPPING_ERROR) {
__iommu_dma_free(dev, size, cpu_addr);
return NULL;
@@ -1178,7 +1180,7 @@ static struct iommu_dma_msi_page 
*iommu_dma_get_msi_page(struct device *dev,
if (!msi_page)
return NULL;
 
-   iova = __iommu_dma_map(dev, msi_addr, size, prot);
+   iova = __iommu_dma_map(dev, msi_addr, size, prot, dma_get_mask(dev));
if (iova == DMA_MAPPING_ERROR)
goto out_free_page;
 
-- 
2.20.1



[PATCH v4 3/5] iommu/dma-iommu: Handle deferred devices

2019-06-13 Thread Tom Murphy
Handle devices which defer their attach to the iommu in the dma-iommu api

Signed-off-by: Tom Murphy 
---
 drivers/iommu/dma-iommu.c | 27 ++-
 1 file changed, 26 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index e64dbbcde63c..f303bbe20e51 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -22,6 +22,7 @@
 #include 
 #include 
 #include 
+#include 
 
 struct iommu_dma_msi_page {
struct list_headlist;
@@ -351,6 +352,21 @@ static int iommu_dma_init_domain(struct iommu_domain 
*domain, dma_addr_t base,
return iova_reserve_iommu_regions(dev, domain);
 }
 
+static int handle_deferred_device(struct device *dev,
+   struct iommu_domain *domain)
+{
+   const struct iommu_ops *ops = domain->ops;
+
+   if (!is_kdump_kernel())
+   return 0;
+
+   if (unlikely(ops->is_attach_deferred &&
+   ops->is_attach_deferred(domain, dev)))
+   return iommu_attach_device(domain, dev);
+
+   return 0;
+}
+
 /**
  * dma_info_to_prot - Translate DMA API directions and attributes to IOMMU API
  *page flags.
@@ -462,6 +478,9 @@ static dma_addr_t __iommu_dma_map(struct device *dev, 
phys_addr_t phys,
size_t iova_off = 0;
dma_addr_t iova;
 
+   if (unlikely(handle_deferred_device(dev, domain)))
+   return DMA_MAPPING_ERROR;
+
if (cookie->type == IOMMU_DMA_IOVA_COOKIE) {
iova_off = iova_offset(>iovad, phys);
size = iova_align(>iovad, size + iova_off);
@@ -583,6 +602,9 @@ static void *iommu_dma_alloc_remap(struct device *dev, 
size_t size,
 
*dma_handle = DMA_MAPPING_ERROR;
 
+   if (unlikely(handle_deferred_device(dev, domain)))
+   return NULL;
+
min_size = alloc_sizes & -alloc_sizes;
if (min_size < PAGE_SIZE) {
min_size = PAGE_SIZE;
@@ -715,7 +737,7 @@ static dma_addr_t iommu_dma_map_page(struct device *dev, 
struct page *page,
int prot = dma_info_to_prot(dir, coherent, attrs);
dma_addr_t dma_handle;
 
-   dma_handle =__iommu_dma_map(dev, phys, size, prot);
+   dma_handle = __iommu_dma_map(dev, phys, size, prot);
if (!coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
dma_handle != DMA_MAPPING_ERROR)
arch_sync_dma_for_device(dev, phys, size, dir);
@@ -825,6 +847,9 @@ static int iommu_dma_map_sg(struct device *dev, struct 
scatterlist *sg,
unsigned long mask = dma_get_seg_boundary(dev);
int i;
 
+   if (unlikely(handle_deferred_device(dev, domain)))
+   return 0;
+
if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC))
iommu_dma_sync_sg_for_device(dev, sg, nents, dir);
 
-- 
2.20.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v4 1/5] iommu/amd: Remove unnecessary locking from AMD iommu driver

2019-06-13 Thread Tom Murphy
We can remove the mutex lock from amd_iommu_map and amd_iommu_unmap.
iommu_map doesn’t lock while mapping and so no two calls should touch
the same iova range. The AMD driver already handles the page table page
allocations without locks so we can safely remove the locks.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/amd_iommu.c   | 10 +-
 drivers/iommu/amd_iommu_types.h |  1 -
 2 files changed, 1 insertion(+), 10 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 73740b969e62..065639e090fe 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -2858,7 +2858,6 @@ static void protection_domain_free(struct 
protection_domain *domain)
 static int protection_domain_init(struct protection_domain *domain)
 {
spin_lock_init(>lock);
-   mutex_init(>api_lock);
domain->id = domain_id_alloc();
if (!domain->id)
return -ENOMEM;
@@ -3045,9 +3044,7 @@ static int amd_iommu_map(struct iommu_domain *dom, 
unsigned long iova,
if (iommu_prot & IOMMU_WRITE)
prot |= IOMMU_PROT_IW;
 
-   mutex_lock(>api_lock);
ret = iommu_map_page(domain, iova, paddr, page_size, prot, GFP_KERNEL);
-   mutex_unlock(>api_lock);
 
domain_flush_np_cache(domain, iova, page_size);
 
@@ -3058,16 +3055,11 @@ static size_t amd_iommu_unmap(struct iommu_domain *dom, 
unsigned long iova,
   size_t page_size)
 {
struct protection_domain *domain = to_pdomain(dom);
-   size_t unmap_size;
 
if (domain->mode == PAGE_MODE_NONE)
return 0;
 
-   mutex_lock(>api_lock);
-   unmap_size = iommu_unmap_page(domain, iova, page_size);
-   mutex_unlock(>api_lock);
-
-   return unmap_size;
+   return iommu_unmap_page(domain, iova, page_size);
 }
 
 static phys_addr_t amd_iommu_iova_to_phys(struct iommu_domain *dom,
diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h
index 52c35d557fad..5d5f5d009b19 100644
--- a/drivers/iommu/amd_iommu_types.h
+++ b/drivers/iommu/amd_iommu_types.h
@@ -461,7 +461,6 @@ struct protection_domain {
struct iommu_domain domain; /* generic domain handle used by
   iommu core code */
spinlock_t lock;/* mostly used to lock the page table*/
-   struct mutex api_lock;  /* protect page tables in the iommu-api path */
u16 id; /* the domain id written to the device table */
int mode;   /* paging mode (0-6 levels) */
u64 *pt_root;   /* page table root pointer */
-- 
2.20.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

[PATCH v4 2/5] iommu: Add gfp parameter to iommu_ops::map

2019-06-13 Thread Tom Murphy
Add a gfp_t parameter to the iommu_ops::map function.
Remove the needless locking in the AMD iommu driver.

The iommu_ops::map function (or the iommu_map function which calls it)
was always supposed to be sleepable (according to Joerg's comment in
this thread: https://lore.kernel.org/patchwork/patch/977520/ ) and so
should probably have had a "might_sleep()" since it was written. However
currently the dma-iommu api can call iommu_map in an atomic context,
which it shouldn't do. This doesn't cause any problems because any iommu
driver which uses the dma-iommu api uses gfp_atomic in it's
iommu_ops::map function. But doing this wastes the memory allocators
atomic pools.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/amd_iommu.c  |  3 ++-
 drivers/iommu/arm-smmu-v3.c|  2 +-
 drivers/iommu/arm-smmu.c   |  2 +-
 drivers/iommu/dma-iommu.c  |  6 ++---
 drivers/iommu/exynos-iommu.c   |  2 +-
 drivers/iommu/intel-iommu.c|  2 +-
 drivers/iommu/iommu.c  | 43 +-
 drivers/iommu/ipmmu-vmsa.c |  2 +-
 drivers/iommu/msm_iommu.c  |  2 +-
 drivers/iommu/mtk_iommu.c  |  2 +-
 drivers/iommu/mtk_iommu_v1.c   |  2 +-
 drivers/iommu/omap-iommu.c |  2 +-
 drivers/iommu/qcom_iommu.c |  2 +-
 drivers/iommu/rockchip-iommu.c |  2 +-
 drivers/iommu/s390-iommu.c |  2 +-
 drivers/iommu/tegra-gart.c |  2 +-
 drivers/iommu/tegra-smmu.c |  2 +-
 drivers/iommu/virtio-iommu.c   |  2 +-
 include/linux/iommu.h  | 21 -
 19 files changed, 77 insertions(+), 26 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 065639e090fe..fd8da60f7359 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -3030,7 +3030,8 @@ static int amd_iommu_attach_device(struct iommu_domain 
*dom,
 }
 
 static int amd_iommu_map(struct iommu_domain *dom, unsigned long iova,
-phys_addr_t paddr, size_t page_size, int iommu_prot)
+phys_addr_t paddr, size_t page_size, int iommu_prot,
+gfp_t gfp)
 {
struct protection_domain *domain = to_pdomain(dom);
int prot = 0;
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 4d5a694f02c2..66dee90877d7 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -1964,7 +1964,7 @@ static int arm_smmu_attach_dev(struct iommu_domain 
*domain, struct device *dev)
 }
 
 static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
-   phys_addr_t paddr, size_t size, int prot)
+   phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
 {
struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops;
 
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 5aeb1dbfaa08..f33ab7ef9049 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -1277,7 +1277,7 @@ static int arm_smmu_attach_dev(struct iommu_domain 
*domain, struct device *dev)
 }
 
 static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
-   phys_addr_t paddr, size_t size, int prot)
+   phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
 {
struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops;
struct arm_smmu_device *smmu = to_smmu_domain(domain)->smmu;
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 0dee374fc64a..e64dbbcde63c 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -471,7 +471,7 @@ static dma_addr_t __iommu_dma_map(struct device *dev, 
phys_addr_t phys,
if (!iova)
return DMA_MAPPING_ERROR;
 
-   if (iommu_map(domain, iova, phys - iova_off, size, prot)) {
+   if (iommu_map_atomic(domain, iova, phys - iova_off, size, prot)) {
iommu_dma_free_iova(cookie, iova, size);
return DMA_MAPPING_ERROR;
}
@@ -615,7 +615,7 @@ static void *iommu_dma_alloc_remap(struct device *dev, 
size_t size,
arch_dma_prep_coherent(sg_page(sg), sg->length);
}
 
-   if (iommu_map_sg(domain, iova, sgt.sgl, sgt.orig_nents, ioprot)
+   if (iommu_map_sg_atomic(domain, iova, sgt.sgl, sgt.orig_nents, ioprot)
< size)
goto out_free_sg;
 
@@ -875,7 +875,7 @@ static int iommu_dma_map_sg(struct device *dev, struct 
scatterlist *sg,
 * We'll leave any physical concatenation to the IOMMU driver's
 * implementation - it knows better than we do.
 */
-   if (iommu_map_sg(domain, iova, sg, nents, prot) < iova_len)
+   if (iommu_map_sg_atomic(domain, iova, sg, nents, prot) < iova_len)
goto out_free_iova;
 
return __finalise_sg(dev, sg, nents, iova);
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c
index 05c6bc099d62..46414234c179 100644
--- a/drivers/iom

[PATCH v3] iommu/amd: Flush not present cache in iommu_map_page

2019-06-13 Thread Tom Murphy
check if there is a not-present cache present and flush it if there is.

Signed-off-by: Tom Murphy 
---
v3:
--applied Qian Cai's "iommu/amd: fix a null-ptr-deref in map_sg()" fix

 drivers/iommu/amd_iommu.c | 20 
 1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index f5d4a0011d25..73740b969e62 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -1295,6 +1295,16 @@ static void domain_flush_complete(struct 
protection_domain *domain)
}
 }
 
+/* Flush the not present cache if it exists */
+static void domain_flush_np_cache(struct protection_domain *domain,
+   dma_addr_t iova, size_t size)
+{
+   if (unlikely(amd_iommu_np_cache)) {
+   domain_flush_pages(domain, iova, size);
+   domain_flush_complete(domain);
+   }
+}
+
 
 /*
  * This function flushes the DTEs for all devices in domain
@@ -2377,10 +2387,7 @@ static dma_addr_t __map_single(struct device *dev,
}
address += offset;
 
-   if (unlikely(amd_iommu_np_cache)) {
-   domain_flush_pages(_dom->domain, address, size);
-   domain_flush_complete(_dom->domain);
-   }
+   domain_flush_np_cache(_dom->domain, address, size);
 
 out:
return address;
@@ -2559,6 +2566,9 @@ static int map_sg(struct device *dev, struct scatterlist 
*sglist,
s->dma_length   = s->length;
}
 
+   if (s)
+   domain_flush_np_cache(domain, s->dma_address, s->dma_length);
+
return nelems;
 
 out_unmap:
@@ -3039,6 +3049,8 @@ static int amd_iommu_map(struct iommu_domain *dom, 
unsigned long iova,
ret = iommu_map_page(domain, iova, paddr, page_size, prot, GFP_KERNEL);
mutex_unlock(>api_lock);
 
+   domain_flush_np_cache(domain, iova, page_size);
+
return ret;
 }
 
-- 
2.20.1



Re: [PATCH -next v2] iommu/amd: fix a null-ptr-deref in map_sg()

2019-06-06 Thread Tom Murphy
Hi Joerg,

Is there anything I need to do to get this patch into linux-next? My
patch to convert the amd iommu driver to use the dma-iommu ops depends
on this patch.

Thanks,
Tom

On Tue, May 7, 2019 at 8:39 AM Joerg Roedel  wrote:
>
> Hi Qian,
>
> On Mon, May 06, 2019 at 12:44:40PM -0400, Qian Cai wrote:
> > The commit 1a1079011da3 ("iommu/amd: Flush not present cache in
> > iommu_map_page") added domain_flush_np_cache() in map_sg() which
> > triggered a crash below during boot. sg_next() could return NULL if
> > sg_is_last() is true, so after for_each_sg(sglist, s, nelems, i), "s"
> > could be NULL which ends up deferencing a NULL pointer later here,
> >
> > domain_flush_np_cache(domain, s->dma_address, s->dma_length);
> >
> > so move domain_flush_np_cache() call inside for_each_sg() to loop over
> > each sg element.
>
> Thanks for the fix, but it is too late to merge it into the tree. I am
> going to revert commit 1a1079011da3 for now and we can try again in the
> next cycle.
>
>
> Thanks,
>
> Joerg
>


Re: [PATCH v3 1/4] iommu: Add gfp parameter to iommu_ops::map

2019-06-04 Thread Tom Murphy
On Tue, Jun 4, 2019 at 7:11 PM Robin Murphy  wrote:
>
> On 06/05/2019 19:52, Tom Murphy wrote:
> > Add a gfp_t parameter to the iommu_ops::map function.
> > Remove the needless locking in the AMD iommu driver.
> >
> > The iommu_ops::map function (or the iommu_map function which calls it)
> > was always supposed to be sleepable (according to Joerg's comment in
> > this thread: https://lore.kernel.org/patchwork/patch/977520/ ) and so
> > should probably have had a "might_sleep()" since it was written. However
> > currently the dma-iommu api can call iommu_map in an atomic context,
> > which it shouldn't do. This doesn't cause any problems because any iommu
> > driver which uses the dma-iommu api uses gfp_atomic in it's
> > iommu_ops::map function. But doing this wastes the memory allocators
> > atomic pools.
>
> Hmm, in some cases iommu_ops::unmap may need to allocate as well,
> primarily if it needs to split a hugepage mapping. Should we pass flags

Are you sure that's the case?

I don't see that in the amd or intel iommu_ops::unmap code and from
looking at the intel code it seems like we actually unmap the whole
large page:
/* Cope with horrid API which requires us to unmap more than the
size argument if it happens to be a large-page mapping. */
BUG_ON(!pfn_to_dma_pte(dmar_domain, iova >> VTD_PAGE_SHIFT, ));

if (size < VTD_PAGE_SIZE << level_to_offset_bits(level))
  size = VTD_PAGE_SIZE << level_to_offset_bits(level);



> through there as well, or are we prepared to assume that that case will
> happen rarely enough that it's fair to just assume GFP_ATOMIC? It won't
> happen for DMA ops, but it's conceivable that other users such as GPU
> drivers might make partial unmaps, and I doubt we could totally rule out
> the wackiest ones doing so from non-sleeping contexts.
>
> Robin.
>
> > We can remove the mutex lock from amd_iommu_map and amd_iommu_unmap.
> > iommu_map doesn’t lock while mapping and so no two calls should touch
> > the same iova range. The AMD driver already handles the page table page
> > allocations without locks so we can safely remove the locks.
> >
> > Signed-off-by: Tom Murphy 
> > ---
> >   drivers/iommu/amd_iommu.c  | 14 ---
> >   drivers/iommu/arm-smmu-v3.c|  2 +-
> >   drivers/iommu/arm-smmu.c   |  2 +-
> >   drivers/iommu/dma-iommu.c  |  6 ++---
> >   drivers/iommu/exynos-iommu.c   |  2 +-
> >   drivers/iommu/intel-iommu.c|  2 +-
> >   drivers/iommu/iommu.c  | 43 +-
> >   drivers/iommu/ipmmu-vmsa.c |  2 +-
> >   drivers/iommu/msm_iommu.c  |  2 +-
> >   drivers/iommu/mtk_iommu.c  |  2 +-
> >   drivers/iommu/mtk_iommu_v1.c   |  2 +-
> >   drivers/iommu/omap-iommu.c |  2 +-
> >   drivers/iommu/qcom_iommu.c |  2 +-
> >   drivers/iommu/rockchip-iommu.c |  2 +-
> >   drivers/iommu/s390-iommu.c |  2 +-
> >   drivers/iommu/tegra-gart.c |  2 +-
> >   drivers/iommu/tegra-smmu.c |  2 +-
> >   include/linux/iommu.h  | 21 -
> >   18 files changed, 78 insertions(+), 34 deletions(-)
> >
> > diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
> > index ebd062522cf5..ea3a5dc61bb0 100644
> > --- a/drivers/iommu/amd_iommu.c
> > +++ b/drivers/iommu/amd_iommu.c
> > @@ -3092,7 +3092,8 @@ static int amd_iommu_attach_device(struct 
> > iommu_domain *dom,
> >   }
> >
> >   static int amd_iommu_map(struct iommu_domain *dom, unsigned long iova,
> > -  phys_addr_t paddr, size_t page_size, int iommu_prot)
> > +  phys_addr_t paddr, size_t page_size, int iommu_prot,
> > +  gfp_t gfp)
> >   {
> >   struct protection_domain *domain = to_pdomain(dom);
> >   int prot = 0;
> > @@ -3106,9 +3107,7 @@ static int amd_iommu_map(struct iommu_domain *dom, 
> > unsigned long iova,
> >   if (iommu_prot & IOMMU_WRITE)
> >   prot |= IOMMU_PROT_IW;
> >
> > - mutex_lock(>api_lock);
> > - ret = iommu_map_page(domain, iova, paddr, page_size, prot, 
> > GFP_KERNEL);
> > - mutex_unlock(>api_lock);
> > + ret = iommu_map_page(domain, iova, paddr, page_size, prot, gfp);
> >
> >   domain_flush_np_cache(domain, iova, page_size);
> >
> > @@ -3119,16 +3118,11 @@ static size_t amd_iommu_unmap(struct iommu_domain 
> > *dom, unsigned long iova,
> >  size_t page_size)
> >   {
> >   struct protection_domain *domain = to_pdomain(dom);
> > - size_t unmap_siz

Re: [PATCH v3 0/4] iommu/amd: Convert the AMD iommu driver to the dma-iommu api

2019-06-03 Thread Tom Murphy via iommu
On Mon, Jun 3, 2019 at 11:52 AM Joerg Roedel  wrote:
>
> Hi Tom,
>
> On Mon, May 06, 2019 at 07:52:02PM +0100, Tom Murphy wrote:
> > Convert the AMD iommu driver to the dma-iommu api. Remove the iova
> > handling and reserve region code from the AMD iommu driver.
>
> Thank you for your work on this! I appreciate that much, but I am not
> sure we are ready to make that move for the AMD and Intel IOMMU drivers
> yet.
>
> My main concern right now is that these changes will add a per-page
> table lock into the fast-path for dma-mapping operations. There has been
> much work in the past to remove all locking from these code-paths and
> make it scalable on x86.

Where is the locking introduced? intel doesn't use a lock in it's
iommu_map function:
https://github.com/torvalds/linux/blob/f2c7c76c5d0a443053e94adb9f0918fa2fb85c3a/drivers/iommu/intel-iommu.c#L5302
because it cleverly uses cmpxchg64 to avoid using locks:
https://github.com/torvalds/linux/blob/f2c7c76c5d0a443053e94adb9f0918fa2fb85c3a/drivers/iommu/intel-iommu.c#L900
And the locking in AMD's iommu_map function can be removed (and i have
removed it in my patch set) because it does that same thing as intel:
https://github.com/torvalds/linux/blob/f2c7c76c5d0a443053e94adb9f0918fa2fb85c3a/drivers/iommu/amd_iommu.c#L1486

Is there something I'm missing?

>
> The dma-ops implementations in the x86 IOMMU drivers have the benefit
> that they can call their page-table manipulation functions directly and
> without locks, because they can make the necessary assumptions. The
> IOMMU-API mapping/unmapping path can't make these assumptions because it
> is also used for non-DMA-API use-cases.
>
> So before we can move the AMD and Intel drivers to the generic DMA-API
> implementation we need to solve this problem to not introduce new
> scalability regressions.
>
> Regards,
>
> Joerg
>
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH v3 2/4] iommu/dma-iommu: Handle deferred devices

2019-05-15 Thread Tom Murphy
like this?

In that case we need to add a call to iommu_dma_alloc_remap.

>From 862aeebb601008cf863e3aff4ff8ed7cefebeefa Mon Sep 17 00:00:00 2001
From: Tom Murphy 
Date: Wed, 15 May 2019 05:43:25 -0700
Subject: [PATCH] iommu/dma-iommu: Handle deferred devices

Handle devices which defer their attach to the iommu in the dma-iommu api

Signed-off-by: Tom Murphy 
---
 drivers/iommu/dma-iommu.c | 27 ++-
 1 file changed, 26 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 7f313cfa9..a48ae906d 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -22,6 +22,7 @@
 #include 
 #include 
 #include 
+#include 

 struct iommu_dma_msi_page {
 struct list_headlist;
@@ -323,6 +324,21 @@ static int iommu_dma_init_domain(struct
iommu_domain *domain, dma_addr_t base,
 return iova_reserve_iommu_regions(dev, domain);
 }

+static int handle_deferred_device(struct device *dev,
+struct iommu_domain *domain)
+{
+const struct iommu_ops *ops = domain->ops;
+
+if (!is_kdump_kernel())
+return 0;
+
+if (unlikely(ops->is_attach_deferred &&
+ops->is_attach_deferred(domain, dev)))
+return iommu_attach_device(domain, dev);
+
+return 0;
+}
+
 /**
  * dma_info_to_prot - Translate DMA API directions and attributes to IOMMU API
  *page flags.
@@ -432,6 +448,9 @@ static dma_addr_t __iommu_dma_map(struct device
*dev, phys_addr_t phys,
 size_t iova_off = 0;
 dma_addr_t iova;

+if (unlikely(handle_deferred_device(dev, domain)))
+return DMA_MAPPING_ERROR;
+
 if (cookie->type == IOMMU_DMA_IOVA_COOKIE) {
 iova_off = iova_offset(>iovad, phys);
 size = iova_align(>iovad, size + iova_off);
@@ -609,6 +628,9 @@ static void *iommu_dma_alloc_remap(struct device
*dev, size_t size,
 dma_addr_t iova;
 void *vaddr;

+if (unlikely(handle_deferred_device(dev, domain)))
+return DMA_MAPPING_ERROR;
+
 *dma_handle = DMA_MAPPING_ERROR;

 min_size = alloc_sizes & -alloc_sizes;
@@ -836,7 +858,7 @@ static dma_addr_t iommu_dma_map_page(struct device
*dev, struct page *page,
 bool coherent = dev_is_dma_coherent(dev);
 dma_addr_t dma_handle;

-dma_handle =__iommu_dma_map(dev, phys, size,
+dma_handle = __iommu_dma_map(dev, phys, size,
 dma_info_to_prot(dir, coherent, attrs),
 iommu_get_dma_domain(dev));
 if (!coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
@@ -954,6 +976,9 @@ static int iommu_dma_map_sg(struct device *dev,
struct scatterlist *sg,
 unsigned long mask = dma_get_seg_boundary(dev);
 int i;

+if (unlikely(handle_deferred_device(dev, domain)))
+return 0;
+
 if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC))
 iommu_dma_sync_sg_for_device(dev, sg, nents, dir);

-- 
2.20.0

On Tue, May 7, 2019 at 7:40 AM Christoph Hellwig  wrote:
>
> On Mon, May 06, 2019 at 07:52:04PM +0100, Tom Murphy wrote:
> > +static int handle_deferred_device(struct device *dev)
> > +{
> > + struct iommu_domain *domain;
> > + const struct iommu_ops *ops;
> > +
> > + if (!is_kdump_kernel())
> > + return 0;
> > +
> > + domain = iommu_get_domain_for_dev(dev);
>
> > - dma_handle =__iommu_dma_map(dev, phys, size,
> > + if (unlikely(handle_deferred_device(dev)))
> > + return DMA_MAPPING_ERROR;
> > +
> > + dma_handle = __iommu_dma_map(dev, phys, size,
>
> __iommu_dma_map already looks up the domain, and as far as I can
> tell all callers need the handle_deferred_device call.  Should we
> just move it to there and pass the domain from the caller?
>
> Also shouldn't the iommu_attach_device call inside
> handle_deferred_device also get an unlikely marker?


[PATCH v3 3/4] iommu/dma-iommu: Use the dev->coherent_dma_mask

2019-05-06 Thread Tom Murphy via iommu
Use the dev->coherent_dma_mask when allocating in the dma-iommu ops api.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/dma-iommu.c | 16 +---
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index b383498e2dc3..2a968afdab10 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -442,7 +442,8 @@ static void __iommu_dma_unmap(struct iommu_domain *domain, 
dma_addr_t dma_addr,
 }
 
 static dma_addr_t __iommu_dma_map(struct device *dev, phys_addr_t phys,
-   size_t size, int prot, struct iommu_domain *domain)
+   size_t size, int prot, struct iommu_domain *domain,
+   dma_addr_t dma_mask)
 {
struct iommu_dma_cookie *cookie = domain->iova_cookie;
size_t iova_off = 0;
@@ -453,7 +454,7 @@ static dma_addr_t __iommu_dma_map(struct device *dev, 
phys_addr_t phys,
size = iova_align(>iovad, size + iova_off);
}
 
-   iova = iommu_dma_alloc_iova(domain, size, dma_get_mask(dev), dev);
+   iova = iommu_dma_alloc_iova(domain, size, dma_mask, dev);
if (!iova)
return DMA_MAPPING_ERROR;
 
@@ -496,7 +497,7 @@ static void *iommu_dma_alloc_contiguous(struct device *dev, 
size_t size,
return NULL;
 
*dma_handle = __iommu_dma_map(dev, page_to_phys(page), size, ioprot,
-   iommu_get_dma_domain(dev));
+   iommu_get_dma_domain(dev), dev->coherent_dma_mask);
if (*dma_handle == DMA_MAPPING_ERROR) {
if (!dma_release_from_contiguous(dev, page, count))
__free_pages(page, page_order);
@@ -766,7 +767,7 @@ static void *iommu_dma_alloc_pool(struct device *dev, 
size_t size,
 
*dma_handle = __iommu_dma_map(dev, page_to_phys(page), size,
dma_info_to_prot(DMA_BIDIRECTIONAL, coherent, attrs),
-   iommu_get_domain_for_dev(dev));
+   iommu_get_domain_for_dev(dev), dev->coherent_dma_mask);
if (*dma_handle == DMA_MAPPING_ERROR) {
dma_free_from_pool(vaddr, PAGE_ALIGN(size));
return NULL;
@@ -857,7 +858,7 @@ static dma_addr_t iommu_dma_map_page(struct device *dev, 
struct page *page,
 
dma_handle = __iommu_dma_map(dev, phys, size,
dma_info_to_prot(dir, coherent, attrs),
-   iommu_get_dma_domain(dev));
+   iommu_get_dma_domain(dev), dma_get_mask(dev));
if (!coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
dma_handle != DMA_MAPPING_ERROR)
arch_sync_dma_for_device(dev, phys, size, dir);
@@ -1067,7 +1068,7 @@ static dma_addr_t iommu_dma_map_resource(struct device 
*dev, phys_addr_t phys,
 {
return __iommu_dma_map(dev, phys, size,
dma_info_to_prot(dir, false, attrs) | IOMMU_MMIO,
-   iommu_get_dma_domain(dev));
+   iommu_get_dma_domain(dev), dma_get_mask(dev));
 }
 
 static void iommu_dma_unmap_resource(struct device *dev, dma_addr_t handle,
@@ -1246,7 +1247,8 @@ static struct iommu_dma_msi_page 
*iommu_dma_get_msi_page(struct device *dev,
if (!msi_page)
return NULL;
 
-   iova = __iommu_dma_map(dev, msi_addr, size, prot, domain);
+   iova = __iommu_dma_map(dev, msi_addr, size, prot, domain,
+   dma_get_mask(dev));
if (iova == DMA_MAPPING_ERROR)
goto out_free_page;
 
-- 
2.17.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v3 0/4] iommu/amd: Convert the AMD iommu driver to the dma-iommu api

2019-05-06 Thread Tom Murphy via iommu
Convert the AMD iommu driver to the dma-iommu api. Remove the iova
handling and reserve region code from the AMD iommu driver.

Change-log:
v3:
-rename dma_limit to dma_mask
-exit handle_deferred_device early if (!is_kdump_kernel())
-remove pointless calls to handle_deferred_device
v2:
-Rebase on top of this series:
 http://git.infradead.org/users/hch/misc.git/shortlog/refs/heads/dma-iommu-ops.3
-Add a gfp_t parameter to the iommu_ops::map function.
-Made use of the reserve region code inside the dma-iommu api

Tom Murphy (4):
  iommu: Add gfp parameter to iommu_ops::map
  iommu/dma-iommu: Handle deferred devices
  iommu/dma-iommu: Use the dev->coherent_dma_mask
  iommu/amd: Convert the AMD iommu driver to the dma-iommu api

 drivers/iommu/Kconfig  |   1 +
 drivers/iommu/amd_iommu.c  | 694 -
 drivers/iommu/arm-smmu-v3.c|   2 +-
 drivers/iommu/arm-smmu.c   |   2 +-
 drivers/iommu/dma-iommu.c  |  50 ++-
 drivers/iommu/exynos-iommu.c   |   2 +-
 drivers/iommu/intel-iommu.c|   2 +-
 drivers/iommu/iommu.c  |  43 +-
 drivers/iommu/ipmmu-vmsa.c |   2 +-
 drivers/iommu/msm_iommu.c  |   2 +-
 drivers/iommu/mtk_iommu.c  |   2 +-
 drivers/iommu/mtk_iommu_v1.c   |   2 +-
 drivers/iommu/omap-iommu.c |   2 +-
 drivers/iommu/qcom_iommu.c |   2 +-
 drivers/iommu/rockchip-iommu.c |   2 +-
 drivers/iommu/s390-iommu.c |   2 +-
 drivers/iommu/tegra-gart.c |   2 +-
 drivers/iommu/tegra-smmu.c |   2 +-
 include/linux/iommu.h  |  21 +-
 19 files changed, 184 insertions(+), 653 deletions(-)

-- 
2.17.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v3 4/4] iommu/amd: Convert AMD iommu driver to the dma-iommu api

2019-05-06 Thread Tom Murphy via iommu
Convert the AMD iommu driver to the dma-iommu api. Remove the iova
handling and reserve region code from the AMD iommu driver.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/Kconfig |   1 +
 drivers/iommu/amd_iommu.c | 680 --
 2 files changed, 70 insertions(+), 611 deletions(-)

diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 6f07f3b21816..5914ba85180b 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -136,6 +136,7 @@ config AMD_IOMMU
select PCI_PASID
select IOMMU_API
select IOMMU_IOVA
+   select IOMMU_DMA
depends on X86_64 && PCI && ACPI
---help---
  With this option you can enable support for AMD IOMMU hardware in
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index ea3a5dc61bb0..366af2b27e7f 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -32,6 +32,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -101,8 +102,6 @@ const struct iommu_ops amd_iommu_ops;
 static ATOMIC_NOTIFIER_HEAD(ppr_notifier);
 int amd_iommu_max_glx_val = -1;
 
-static const struct dma_map_ops amd_iommu_dma_ops;
-
 /*
  * general struct to manage commands send to an IOMMU
  */
@@ -115,21 +114,6 @@ struct kmem_cache *amd_iommu_irq_cache;
 static void update_domain(struct protection_domain *domain);
 static int protection_domain_init(struct protection_domain *domain);
 static void detach_device(struct device *dev);
-static void iova_domain_flush_tlb(struct iova_domain *iovad);
-
-/*
- * Data container for a dma_ops specific protection domain
- */
-struct dma_ops_domain {
-   /* generic protection domain information */
-   struct protection_domain domain;
-
-   /* IOVA RB-Tree */
-   struct iova_domain iovad;
-};
-
-static struct iova_domain reserved_iova_ranges;
-static struct lock_class_key reserved_rbtree_key;
 
 /
  *
@@ -200,12 +184,6 @@ static struct protection_domain *to_pdomain(struct 
iommu_domain *dom)
return container_of(dom, struct protection_domain, domain);
 }
 
-static struct dma_ops_domain* to_dma_ops_domain(struct protection_domain 
*domain)
-{
-   BUG_ON(domain->flags != PD_DMA_OPS_MASK);
-   return container_of(domain, struct dma_ops_domain, domain);
-}
-
 static struct iommu_dev_data *alloc_dev_data(u16 devid)
 {
struct iommu_dev_data *dev_data;
@@ -1279,12 +1257,6 @@ static void domain_flush_pages(struct protection_domain 
*domain,
__domain_flush_pages(domain, address, size, 0);
 }
 
-/* Flush the whole IO/TLB for a given protection domain */
-static void domain_flush_tlb(struct protection_domain *domain)
-{
-   __domain_flush_pages(domain, 0, CMD_INV_IOMMU_ALL_PAGES_ADDRESS, 0);
-}
-
 /* Flush the whole IO/TLB for a given protection domain - including PDE */
 static void domain_flush_tlb_pde(struct protection_domain *domain)
 {
@@ -1686,43 +1658,6 @@ static unsigned long iommu_unmap_page(struct 
protection_domain *dom,
return unmapped;
 }
 
-/
- *
- * The next functions belong to the address allocator for the dma_ops
- * interface functions.
- *
- /
-
-
-static unsigned long dma_ops_alloc_iova(struct device *dev,
-   struct dma_ops_domain *dma_dom,
-   unsigned int pages, u64 dma_mask)
-{
-   unsigned long pfn = 0;
-
-   pages = __roundup_pow_of_two(pages);
-
-   if (dma_mask > DMA_BIT_MASK(32))
-   pfn = alloc_iova_fast(_dom->iovad, pages,
- IOVA_PFN(DMA_BIT_MASK(32)), false);
-
-   if (!pfn)
-   pfn = alloc_iova_fast(_dom->iovad, pages,
- IOVA_PFN(dma_mask), true);
-
-   return (pfn << PAGE_SHIFT);
-}
-
-static void dma_ops_free_iova(struct dma_ops_domain *dma_dom,
- unsigned long address,
- unsigned int pages)
-{
-   pages = __roundup_pow_of_two(pages);
-   address >>= PAGE_SHIFT;
-
-   free_iova_fast(_dom->iovad, address, pages);
-}
-
 /
  *
  * The next functions belong to the domain allocation. A domain is
@@ -1824,40 +1759,25 @@ static void free_gcr3_table(struct protection_domain 
*domain)
free_page((unsigned long)domain->gcr3_tbl);
 }
 
-static void dma_ops_domain_flush_tlb(struct dma_ops_domain *dom)
-{
-   domain_flush_tlb(>domain);
-   domain_flush_complete(>domain);
-}
-
-static void iova_domain_flush_tlb(struct iova_domain *iovad)
-{
-   struct dma_ops_domain *dom;
-
-   dom = container_of(iovad, struct dma_ops_do

[PATCH v3 1/4] iommu: Add gfp parameter to iommu_ops::map

2019-05-06 Thread Tom Murphy via iommu
Add a gfp_t parameter to the iommu_ops::map function.
Remove the needless locking in the AMD iommu driver.

The iommu_ops::map function (or the iommu_map function which calls it)
was always supposed to be sleepable (according to Joerg's comment in
this thread: https://lore.kernel.org/patchwork/patch/977520/ ) and so
should probably have had a "might_sleep()" since it was written. However
currently the dma-iommu api can call iommu_map in an atomic context,
which it shouldn't do. This doesn't cause any problems because any iommu
driver which uses the dma-iommu api uses gfp_atomic in it's
iommu_ops::map function. But doing this wastes the memory allocators
atomic pools.

We can remove the mutex lock from amd_iommu_map and amd_iommu_unmap.
iommu_map doesn’t lock while mapping and so no two calls should touch
the same iova range. The AMD driver already handles the page table page
allocations without locks so we can safely remove the locks.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/amd_iommu.c  | 14 ---
 drivers/iommu/arm-smmu-v3.c|  2 +-
 drivers/iommu/arm-smmu.c   |  2 +-
 drivers/iommu/dma-iommu.c  |  6 ++---
 drivers/iommu/exynos-iommu.c   |  2 +-
 drivers/iommu/intel-iommu.c|  2 +-
 drivers/iommu/iommu.c  | 43 +-
 drivers/iommu/ipmmu-vmsa.c |  2 +-
 drivers/iommu/msm_iommu.c  |  2 +-
 drivers/iommu/mtk_iommu.c  |  2 +-
 drivers/iommu/mtk_iommu_v1.c   |  2 +-
 drivers/iommu/omap-iommu.c |  2 +-
 drivers/iommu/qcom_iommu.c |  2 +-
 drivers/iommu/rockchip-iommu.c |  2 +-
 drivers/iommu/s390-iommu.c |  2 +-
 drivers/iommu/tegra-gart.c |  2 +-
 drivers/iommu/tegra-smmu.c |  2 +-
 include/linux/iommu.h  | 21 -
 18 files changed, 78 insertions(+), 34 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index ebd062522cf5..ea3a5dc61bb0 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -3092,7 +3092,8 @@ static int amd_iommu_attach_device(struct iommu_domain 
*dom,
 }
 
 static int amd_iommu_map(struct iommu_domain *dom, unsigned long iova,
-phys_addr_t paddr, size_t page_size, int iommu_prot)
+phys_addr_t paddr, size_t page_size, int iommu_prot,
+gfp_t gfp)
 {
struct protection_domain *domain = to_pdomain(dom);
int prot = 0;
@@ -3106,9 +3107,7 @@ static int amd_iommu_map(struct iommu_domain *dom, 
unsigned long iova,
if (iommu_prot & IOMMU_WRITE)
prot |= IOMMU_PROT_IW;
 
-   mutex_lock(>api_lock);
-   ret = iommu_map_page(domain, iova, paddr, page_size, prot, GFP_KERNEL);
-   mutex_unlock(>api_lock);
+   ret = iommu_map_page(domain, iova, paddr, page_size, prot, gfp);
 
domain_flush_np_cache(domain, iova, page_size);
 
@@ -3119,16 +3118,11 @@ static size_t amd_iommu_unmap(struct iommu_domain *dom, 
unsigned long iova,
   size_t page_size)
 {
struct protection_domain *domain = to_pdomain(dom);
-   size_t unmap_size;
 
if (domain->mode == PAGE_MODE_NONE)
return 0;
 
-   mutex_lock(>api_lock);
-   unmap_size = iommu_unmap_page(domain, iova, page_size);
-   mutex_unlock(>api_lock);
-
-   return unmap_size;
+   return iommu_unmap_page(domain, iova, page_size);
 }
 
 static phys_addr_t amd_iommu_iova_to_phys(struct iommu_domain *dom,
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index d3880010c6cf..e5c48089b49f 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -1777,7 +1777,7 @@ static int arm_smmu_attach_dev(struct iommu_domain 
*domain, struct device *dev)
 }
 
 static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
-   phys_addr_t paddr, size_t size, int prot)
+   phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
 {
struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops;
 
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 045d93884164..2d50db55b788 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -1286,7 +1286,7 @@ static int arm_smmu_attach_dev(struct iommu_domain 
*domain, struct device *dev)
 }
 
 static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
-   phys_addr_t paddr, size_t size, int prot)
+   phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
 {
struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops;
struct arm_smmu_device *smmu = to_smmu_domain(domain)->smmu;
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index fa5713a4f6f8..7a96c2c8f56b 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -440,7 +440,7 @@ static dma_addr_t __iommu_dma_map(struct device *dev, 
phys_addr_t phys,
   

[PATCH v3 2/4] iommu/dma-iommu: Handle deferred devices

2019-05-06 Thread Tom Murphy via iommu
Handle devices which defer their attach to the iommu in the dma-iommu api

Signed-off-by: Tom Murphy 
---
 drivers/iommu/dma-iommu.c | 28 +++-
 1 file changed, 27 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 7a96c2c8f56b..b383498e2dc3 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -22,6 +22,7 @@
 #include 
 #include 
 #include 
+#include 
 
 struct iommu_dma_msi_page {
struct list_headlist;
@@ -322,6 +323,22 @@ static int iommu_dma_init_domain(struct iommu_domain 
*domain, dma_addr_t base,
return iova_reserve_iommu_regions(dev, domain);
 }
 
+static int handle_deferred_device(struct device *dev)
+{
+   struct iommu_domain *domain;
+   const struct iommu_ops *ops;
+
+   if (!is_kdump_kernel())
+   return 0;
+
+   domain = iommu_get_domain_for_dev(dev);
+   ops = domain->ops;
+   if (ops->is_attach_deferred && ops->is_attach_deferred(domain, dev))
+   return iommu_attach_device(domain, dev);
+
+   return 0;
+}
+
 /**
  * dma_info_to_prot - Translate DMA API directions and attributes to IOMMU API
  *page flags.
@@ -835,7 +852,10 @@ static dma_addr_t iommu_dma_map_page(struct device *dev, 
struct page *page,
bool coherent = dev_is_dma_coherent(dev);
dma_addr_t dma_handle;
 
-   dma_handle =__iommu_dma_map(dev, phys, size,
+   if (unlikely(handle_deferred_device(dev)))
+   return DMA_MAPPING_ERROR;
+
+   dma_handle = __iommu_dma_map(dev, phys, size,
dma_info_to_prot(dir, coherent, attrs),
iommu_get_dma_domain(dev));
if (!coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
@@ -953,6 +973,9 @@ static int iommu_dma_map_sg(struct device *dev, struct 
scatterlist *sg,
unsigned long mask = dma_get_seg_boundary(dev);
int i;
 
+   if (unlikely(handle_deferred_device(dev)))
+   return 0;
+
if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC))
iommu_dma_sync_sg_for_device(dev, sg, nents, dir);
 
@@ -1056,6 +1079,9 @@ static void iommu_dma_unmap_resource(struct device *dev, 
dma_addr_t handle,
 static void *iommu_dma_alloc(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
 {
+   if (unlikely(handle_deferred_device(dev)))
+   return NULL;
+
gfp |= __GFP_ZERO;
 
 #ifdef CONFIG_DMA_DIRECT_REMAP
-- 
2.17.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH v2 3/4] iommu/dma-iommu: Use the dev->coherent_dma_mask

2019-05-06 Thread Tom Murphy via iommu
Just to make this clear, I won't apply Christoph's patch (the one in
this email thread) and instead the only change I will make is to
rename dma_limit to dma_mask.

On Tue, Apr 30, 2019 at 1:05 PM Robin Murphy  wrote:
>
> On 30/04/2019 12:32, Christoph Hellwig wrote:
> > On Tue, Apr 30, 2019 at 12:27:02PM +0100, Robin Murphy wrote:
> >>> Hmm, I don't think we need the DMA mask for the MSI mapping, this
> >>> should probably always use a 64-bit mask.
> >>
> >> If that were true then we wouldn't need DMA masks for regular mappings
> >> either. If we have to map the MSI doorbell at all, then we certainly have 
> >> to
> >> place it at an IOVA that the relevant device is actually capable of
> >> addressing.
> >
> > Well, as shown by the patch below we don't even look at the DMA mask
> > for the MSI page - we just allocate from bottom to top.
>
> In the trivial cookie for unmanaged domains, yes, but in that case the
> responsibility is on VFIO to provide a suitable (i.e. sub-32-bit)
> address range for that cookie in the first place. In the managed case,
> allocation uses the streaming mask via iommu_dma_get_msi_page() calling
> __iommu_dma_map(). Admittedly the mask can then get overlooked when
> reusing an existing mapping, which strictly could pose a problem if you
> have multiple devices with incompatible masks in the same group (and
> such that the PCI stuff doesn't already mitigate it), but that's such an
> obscure corner case that I'm reticent to introduce the complication to
> handle it until it's actually proven necessary.
>
> Robin.
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH v3 1/8] iommu: Add ops entry for supported default domain type

2019-05-06 Thread Tom Murphy via iommu
The AMD driver already solves this problem and uses the generic
iommu_request_dm_for_dev function. It seems like both drivers have the
same problem and could use the same solution. Is there any reason we
can't have use the same solution for the intel and amd driver?

Could we just  copy the implementation of the AMD driver? It would be
nice to have the same behavior across both drivers especially as we
move to make both drivers use more generic code.

On Mon, Apr 29, 2019 at 3:16 AM Lu Baolu  wrote:
>
> This adds an optional ops entry to query the default domain
> types supported by the iommu driver for  a specific device.
> This is necessary in cases where the iommu driver can only
> support a specific type of default domain for a device. In
> normal cases, this ops will return IOMMU_DOMAIN_ANY which
> indicates that the iommu driver supports both IOMMU_DOMAIN_DMA
> and IOMMU_DOMAIN_IDENTITY, hence the static default domain
> type will be used.
>
> Signed-off-by: Lu Baolu 
> ---
>  drivers/iommu/iommu.c | 13 ++---
>  include/linux/iommu.h | 11 +++
>  2 files changed, 21 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
> index acd6830e6e9b..1ad9a1f2e078 100644
> --- a/drivers/iommu/iommu.c
> +++ b/drivers/iommu/iommu.c
> @@ -1097,15 +1097,22 @@ struct iommu_group *iommu_group_get_for_dev(struct 
> device *dev)
>  * IOMMU driver.
>  */
> if (!group->default_domain) {
> +   unsigned int domain_type = IOMMU_DOMAIN_ANY;
> struct iommu_domain *dom;
>
> -   dom = __iommu_domain_alloc(dev->bus, iommu_def_domain_type);
> -   if (!dom && iommu_def_domain_type != IOMMU_DOMAIN_DMA) {
> +   if (ops->def_domain_type)
> +   domain_type = ops->def_domain_type(dev);
> +
> +   if (domain_type == IOMMU_DOMAIN_ANY)
> +   domain_type = iommu_def_domain_type;
> +
> +   dom = __iommu_domain_alloc(dev->bus, domain_type);
> +   if (!dom && domain_type != IOMMU_DOMAIN_DMA) {
> dom = __iommu_domain_alloc(dev->bus, 
> IOMMU_DOMAIN_DMA);
> if (dom) {
> dev_warn(dev,
>  "failed to allocate default IOMMU 
> domain of type %u; falling back to IOMMU_DOMAIN_DMA",
> -iommu_def_domain_type);
> +domain_type);
> }
> }
>
> diff --git a/include/linux/iommu.h b/include/linux/iommu.h
> index 8239ece9fdfc..ba9a5b996a63 100644
> --- a/include/linux/iommu.h
> +++ b/include/linux/iommu.h
> @@ -79,12 +79,16 @@ struct iommu_domain_geometry {
>   * IOMMU_DOMAIN_DMA- Internally used for DMA-API implementations.
>   *   This flag allows IOMMU drivers to implement
>   *   certain optimizations for these domains
> + * IOMMU_DOMAIN_ANY- All domain types defined here
>   */
>  #define IOMMU_DOMAIN_BLOCKED   (0U)
>  #define IOMMU_DOMAIN_IDENTITY  (__IOMMU_DOMAIN_PT)
>  #define IOMMU_DOMAIN_UNMANAGED (__IOMMU_DOMAIN_PAGING)
>  #define IOMMU_DOMAIN_DMA   (__IOMMU_DOMAIN_PAGING |\
>  __IOMMU_DOMAIN_DMA_API)
> +#define IOMMU_DOMAIN_ANY   (IOMMU_DOMAIN_IDENTITY |\
> +IOMMU_DOMAIN_UNMANAGED |   \
> +IOMMU_DOMAIN_DMA)
>
>  struct iommu_domain {
> unsigned type;
> @@ -196,6 +200,11 @@ enum iommu_dev_features {
>   * @dev_feat_enabled: check enabled feature
>   * @aux_attach/detach_dev: aux-domain specific attach/detach entries.
>   * @aux_get_pasid: get the pasid given an aux-domain
> + * @def_domain_type: get per-device default domain type that the IOMMU
> + * driver is able to support. Valid returns values:
> + * - IOMMU_DOMAIN_DMA: only suports non-identity domain
> + * - IOMMU_DOMAIN_IDENTITY: only supports identity domain
> + * - IOMMU_DOMAIN_ANY: supports all
>   * @pgsize_bitmap: bitmap of all possible supported page sizes
>   */
>  struct iommu_ops {
> @@ -251,6 +260,8 @@ struct iommu_ops {
> void (*aux_detach_dev)(struct iommu_domain *domain, struct device 
> *dev);
> int (*aux_get_pasid)(struct iommu_domain *domain, struct device *dev);
>
> +   int (*def_domain_type)(struct device *dev);
> +
> unsigned long pgsize_bitmap;
>  };
>
> --
> 2.17.1
>
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [RFC 1/7] iommu/vt-d: Set the dma_ops per device so we can remove the iommu_no_mapping code

2019-05-06 Thread Tom Murphy via iommu
On Mon, May 6, 2019 at 2:48 AM Lu Baolu  wrote:
>
> Hi,
>
> On 5/4/19 9:23 PM, Tom Murphy wrote:
> > Set the dma_ops per device so we can remove the iommu_no_mapping code.
> >
> > Signed-off-by: Tom Murphy
> > ---
> >   drivers/iommu/intel-iommu.c | 85 +++--
> >   1 file changed, 6 insertions(+), 79 deletions(-)
> >
> > diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
> > index eace915602f0..2db1dc47e7e4 100644
> > --- a/drivers/iommu/intel-iommu.c
> > +++ b/drivers/iommu/intel-iommu.c
> > @@ -2622,17 +2622,6 @@ static int __init si_domain_init(int hw)
> >   return 0;
> >   }
> >
> > -static int identity_mapping(struct device *dev)
> > -{
> > - struct device_domain_info *info;
> > -
> > - info = dev->archdata.iommu;
> > - if (info && info != DUMMY_DEVICE_DOMAIN_INFO)
> > - return (info->domain == si_domain);
> > -
> > - return 0;
> > -}
> > -
> >   static int domain_add_dev_info(struct dmar_domain *domain, struct device 
> > *dev)
> >   {
> >   struct dmar_domain *ndomain;
> > @@ -3270,43 +3259,6 @@ static unsigned long intel_alloc_iova(struct device 
> > *dev,
> >   return iova_pfn;
> >   }
> >
> > -/* Check if the dev needs to go through non-identity map and unmap 
> > process.*/
> > -static int iommu_no_mapping(struct device *dev)
> > -{
> > - int found;
> > -
> > - if (iommu_dummy(dev))
> > - return 1;
> > -
> > - found = identity_mapping(dev);
> > - if (found) {
> > - /*
> > -  * If the device's dma_mask is less than the system's memory
> > -  * size then this is not a candidate for identity mapping.
> > -  */
> > - u64 dma_mask = *dev->dma_mask;
> > -
> > - if (dev->coherent_dma_mask &&
> > - dev->coherent_dma_mask < dma_mask)
> > - dma_mask = dev->coherent_dma_mask;
> > -
> > - if (dma_mask < dma_get_required_mask(dev)) {
> > - /*
> > -  * 32 bit DMA is removed from si_domain and fall back
> > -  * to non-identity mapping.
> > -  */
> > - dmar_remove_one_dev_info(dev);
> > - dev_warn(dev, "32bit DMA uses non-identity 
> > mapping\n");
> > -
> > - return 0;
> > - }
>
> The iommu_no_mapping() also checks whether any 32bit DMA device uses
> identity mapping. The device might not work if the system memory space
> is bigger than 4G.

It looks like their is actually a bug in the v3 of the "iommu/vt-d:
Delegate DMA domain to generic iommu" patch set. I will leave a
message in that email thread. Fixing that bug should also fix this
issue.


>
> Will you add this to other place, or it's unnecessary?
>
> Best regards,
> Lu Baolu
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH v3 5/8] iommu/vt-d: Implement def_domain_type iommu ops entry

2019-05-06 Thread Tom Murphy via iommu
It looks like there is a bug in this code.

The behavior before this patch in __intel_map_single was that
iommu_no_mapping would call remove the attached si_domain for 32 bit
devices  (in the  dmar_remove_one_dev_info(dev) call in
iommu_no_mapping) and then allocate a new domain in
get_valid_domain_for_dev
old:
if (iommu_no_mapping(dev))
   return paddr;
domain = get_valid_domain_for_dev(dev);
if (!domain)
   return DMA_MAPPING_ERROR;

but in the new code we remove the attached si_domain but we WON'T
allocate a new domain and instead just return an error when we call
find_domain
new:
if (iommu_no_mapping(dev))
return paddr;

domain = find_domain(dev);
if (!domain)
return DMA_MAPPING_ERROR;

This is a bug, right?

On Tue, Apr 30, 2019 at 3:18 AM Lu Baolu  wrote:
>
> Hi Christoph,
>
> On 4/30/19 4:03 AM, Christoph Hellwig wrote:
> >> @@ -3631,35 +3607,30 @@ static int iommu_no_mapping(struct device *dev)
> >>  if (iommu_dummy(dev))
> >>  return 1;
> >>
> >> -if (!iommu_identity_mapping)
> >> -return 0;
> >> -
> >
> > FYI, iommu_no_mapping has been refactored in for-next:
> >
> > https://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu.git/commit/?h=x86/vt-d=48b2c937ea37a3bece0094b46450ed5267525289
>
> Oh, yes! Thanks for letting me know this. Will rebase the code.
>
> >
> >>  found = identity_mapping(dev);
> >>  if (found) {
> >> +/*
> >> + * If the device's dma_mask is less than the system's memory
> >> + * size then this is not a candidate for identity mapping.
> >> + */
> >> +u64 dma_mask = *dev->dma_mask;
> >> +
> >> +if (dev->coherent_dma_mask &&
> >> +dev->coherent_dma_mask < dma_mask)
> >> +dma_mask = dev->coherent_dma_mask;
> >> +
> >> +if (dma_mask < dma_get_required_mask(dev)) {
> >
> > I know this is mostly existing code moved around, but it really needs
> > some fixing.  For one dma_get_required_mask is supposed to return the
> > required to not bounce mask for the given device.  E.g. for a device
> > behind an iommu it should always just return 32-bit.  If you really
> > want to check vs system memory please call dma_direct_get_required_mask
> > without the dma_ops indirection.
> >
> > Second I don't even think we need to check the coherent_dma_mask,
> > dma_direct is pretty good at always finding memory even without
> > an iommu.
> >
> > Third this doesn't take take the bus_dma_mask into account.
> >
> > This probably should just be:
> >
> >   if (min(*dev->dma_mask, dev->bus_dma_mask) <
> >   dma_direct_get_required_mask(dev)) {
>
> Agreed and will add this in the next version.
>
> Best regards,
> Lu Baolu
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [RFC 6/7] iommu/vt-d: convert the intel iommu driver to the dma-iommu ops api

2019-05-05 Thread Tom Murphy via iommu
On Sun, May 5, 2019 at 3:44 AM Lu Baolu  wrote:
>
> Hi,
>
> On 5/4/19 9:23 PM, Tom Murphy wrote:
> > static int intel_iommu_add_device(struct device *dev)
> >   {
> > + struct dmar_domain *dmar_domain;
> > + struct iommu_domain *domain;
> >   struct intel_iommu *iommu;
> >   struct iommu_group *group;
> > - struct iommu_domain *domain;
> > + dma_addr_t base;
> >   u8 bus, devfn;
> >
> >   iommu = device_to_iommu(dev, , );
> > @@ -4871,9 +4514,12 @@ static int intel_iommu_add_device(struct device *dev)
> >   if (IS_ERR(group))
> >   return PTR_ERR(group);
> >
> > + base = IOVA_START_PFN << VTD_PAGE_SHIFT;
> >   domain = iommu_get_domain_for_dev(dev);
> > + dmar_domain = to_dmar_domain(domain);
> >   if (domain->type == IOMMU_DOMAIN_DMA)
> > - dev->dma_ops = _dma_ops;
> > + iommu_setup_dma_ops(dev, base,
> > + __DOMAIN_MAX_ADDR(dmar_domain->gaw) - base);
>
> I didn't find the implementation of iommu_setup_dma_ops() in this
> series. Will the iova resource be initialized in this function?

Ah sorry, I should've mentioned this is based on the
http://git.infradead.org/users/hch/misc.git/shortlog/refs/heads/dma-iommu-ops.3
branch with the "iommu/vt-d: Delegate DMA domain to generic iommu" and
"iommu/amd: Convert the AMD iommu driver to the dma-iommu api" patch
sets applied.

>
> If so, will this block iommu_group_create_direct_mappings() which
> reserves and maps the reserved iova ranges.

The reserved regions will be reserved by the
iova_reserve_iommu_regions function instead:
( 
https://github.com/torvalds/linux/blob/6203838dec05352bc357625b1e9ba0a10d3bca35/drivers/iommu/dma-iommu.c#L238
)
iommu_setup_dma_ops calls iommu_dma_init_domain which calls
iova_reserve_iommu_regions.
iommu_group_create_direct_mappings will still execute normally but it
won't be able to call the intel_iommu_apply_resv_region function
because it's been removed in this patchset.
This shouldn't change any behavior and the same regions should be reserved.

>
> >
> >   iommu_group_put(group);
> >   return 0;
> > @@ -5002,19 +4648,6 @@ int intel_iommu_enable_pasid(struct intel_iommu 
> > *iommu, struct intel_svm_dev *sd
> >   return ret;
> >   }
> >
> > -static void intel_iommu_apply_resv_region(struct device *dev,
> > -   struct iommu_domain *domain,
> > -   struct iommu_resv_region *region)
> > -{
> > - struct dmar_domain *dmar_domain = to_dmar_domain(domain);
> > - unsigned long start, end;
> > -
> > - start = IOVA_PFN(region->start);
> > - end   = IOVA_PFN(region->start + region->length - 1);
> > -
> > - WARN_ON_ONCE(!reserve_iova(_domain->iovad, start, end));
> > -}
> > -
> >   struct intel_iommu *intel_svm_device_to_iommu(struct device *dev)
> >   {
> >   struct intel_iommu *iommu;
> > @@ -5050,13 +4683,13 @@ const struct iommu_ops intel_iommu_ops = {
> >   .detach_dev = intel_iommu_detach_device,
> >   .map= intel_iommu_map,
> >   .unmap  = intel_iommu_unmap,
> > + .flush_iotlb_all= iommu_flush_iova,
> >   .flush_iotlb_range  = intel_iommu_flush_iotlb_range,
> >   .iova_to_phys   = intel_iommu_iova_to_phys,
> >   .add_device = intel_iommu_add_device,
> >   .remove_device  = intel_iommu_remove_device,
> >   .get_resv_regions   = intel_iommu_get_resv_regions,
> >   .put_resv_regions   = intel_iommu_put_resv_regions,
> > - .apply_resv_region  = intel_iommu_apply_resv_region,
>
> With this removed, how will iommu_group_create_direct_mappings() work?
>
> Best regards,
> Lu Baolu
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[RFC 5/7] iommu/dma-iommu: add wrapper for iommu_dma_free_cpu_cached_iovas

2019-05-04 Thread Tom Murphy via iommu
Add a wrapper for iommu_dma_free_cpu_cached_iovas in the dma-iommu api
path to help with the intel-iommu driver conversion to the dma-iommu api
path

Signed-off-by: Tom Murphy 
---
 drivers/iommu/dma-iommu.c | 9 +
 include/linux/dma-iommu.h | 3 +++
 2 files changed, 12 insertions(+)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 82ba500886b4..1415b6f068c1 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -49,6 +49,15 @@ struct iommu_dma_cookie {
struct iommu_domain *fq_domain;
 };
 
+void iommu_dma_free_cpu_cached_iovas(unsigned int cpu,
+   struct iommu_domain *domain)
+{
+   struct iommu_dma_cookie *cookie = domain->iova_cookie;
+   struct iova_domain *iovad = >iovad;
+
+   free_cpu_cached_iovas(cpu, iovad);
+}
+
 static void iommu_dma_entry_dtor(unsigned long data)
 {
struct page *freelist = (struct page *)data;
diff --git a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h
index 3fc76918e9bf..1e5bee193feb 100644
--- a/include/linux/dma-iommu.h
+++ b/include/linux/dma-iommu.h
@@ -25,6 +25,9 @@ void iommu_setup_dma_ops(struct device *dev, u64 dma_base, 
u64 size);
 void iommu_dma_map_msi_msg(int irq, struct msi_msg *msg);
 void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list);
 
+void iommu_dma_free_cpu_cached_iovas(unsigned int cpu,
+   struct iommu_domain *domain);
+
 #else /* CONFIG_IOMMU_DMA */
 
 struct iommu_domain;
-- 
2.17.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[RFC 6/7] iommu/vt-d: convert the intel iommu driver to the dma-iommu ops api

2019-05-04 Thread Tom Murphy via iommu
Convert the intel iommu driver to the dma-iommu api to allow us to
remove the iova handling code and the reserved region code

Signed-off-by: Tom Murphy 
---
 drivers/iommu/Kconfig   |   1 +
 drivers/iommu/intel-iommu.c | 405 ++--
 include/linux/intel-iommu.h |   1 -
 3 files changed, 20 insertions(+), 387 deletions(-)

diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 6f07f3b21816..dfed97f55b6e 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -180,6 +180,7 @@ config INTEL_IOMMU
select IOMMU_IOVA
select NEED_DMA_MAP_STATE
select DMAR_TABLE
+   select IOMMU_DMA
help
  DMA remapping (DMAR) devices support enables independent address
  translations for Direct Memory Access (DMA) from devices.
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 87622a28b854..980fc4816d72 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -39,6 +39,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -1180,13 +1181,6 @@ static void dma_free_pagelist(struct page *freelist)
}
 }
 
-static void iova_entry_free(unsigned long data)
-{
-   struct page *freelist = (struct page *)data;
-
-   dma_free_pagelist(freelist);
-}
-
 /* iommu handling */
 static int iommu_alloc_root_entry(struct intel_iommu *iommu)
 {
@@ -1530,16 +1524,14 @@ static inline void __mapping_notify_one(struct 
intel_iommu *iommu,
iommu_flush_write_buffer(iommu);
 }
 
-static void iommu_flush_iova(struct iova_domain *iovad)
+static void iommu_flush_iova(struct iommu_domain *domain)
 {
-   struct dmar_domain *domain;
+   struct dmar_domain *dmar_domain = to_dmar_domain(domain);
int idx;
 
-   domain = container_of(iovad, struct dmar_domain, iovad);
-
-   for_each_domain_iommu(idx, domain) {
+   for_each_domain_iommu(idx, dmar_domain) {
struct intel_iommu *iommu = g_iommus[idx];
-   u16 did = domain->iommu_did[iommu->seq_id];
+   u16 did = dmar_domain->iommu_did[iommu->seq_id];
 
iommu->flush.flush_iotlb(iommu, did, 0, 0, DMA_TLB_DSI_FLUSH);
 
@@ -1804,48 +1796,6 @@ static int domain_detach_iommu(struct dmar_domain 
*domain,
return count;
 }
 
-static struct iova_domain reserved_iova_list;
-static struct lock_class_key reserved_rbtree_key;
-
-static int dmar_init_reserved_ranges(void)
-{
-   struct pci_dev *pdev = NULL;
-   struct iova *iova;
-   int i;
-
-   init_iova_domain(_iova_list, VTD_PAGE_SIZE, IOVA_START_PFN);
-
-   lockdep_set_class(_iova_list.iova_rbtree_lock,
-   _rbtree_key);
-
-   /* IOAPIC ranges shouldn't be accessed by DMA */
-   iova = reserve_iova(_iova_list, IOVA_PFN(IOAPIC_RANGE_START),
-   IOVA_PFN(IOAPIC_RANGE_END));
-   if (!iova) {
-   pr_err("Reserve IOAPIC range failed\n");
-   return -ENODEV;
-   }
-
-   /* Reserve all PCI MMIO to avoid peer-to-peer access */
-   for_each_pci_dev(pdev) {
-   struct resource *r;
-
-   for (i = 0; i < PCI_NUM_RESOURCES; i++) {
-   r = >resource[i];
-   if (!r->flags || !(r->flags & IORESOURCE_MEM))
-   continue;
-   iova = reserve_iova(_iova_list,
-   IOVA_PFN(r->start),
-   IOVA_PFN(r->end));
-   if (!iova) {
-   pci_err(pdev, "Reserve iova for %pR failed\n", 
r);
-   return -ENODEV;
-   }
-   }
-   }
-   return 0;
-}
-
 static inline int guestwidth_to_adjustwidth(int gaw)
 {
int agaw;
@@ -1871,7 +1821,7 @@ static void domain_exit(struct dmar_domain *domain)
 
/* destroy iovas */
if (domain->domain.type == IOMMU_DOMAIN_DMA)
-   put_iova_domain(>iovad);
+   iommu_put_dma_cookie(>domain);
 
freelist = domain_unmap(domain, 0, DOMAIN_MAX_PFN(domain->gaw), NULL);
 
@@ -3213,296 +3163,6 @@ static int __init init_dmars(void)
return ret;
 }
 
-/* This takes a number of _MM_ pages, not VTD pages */
-static unsigned long intel_alloc_iova(struct device *dev,
-struct dmar_domain *domain,
-unsigned long nrpages, uint64_t dma_mask)
-{
-   unsigned long iova_pfn;
-
-   /* Restrict dma_mask to the width that the iommu can handle */
-   dma_mask = min_t(uint64_t, DOMAIN_MAX_ADDR(domain->gaw), dma_mask);
-   /* Ensure we reserve the whole size-aligned region */
-   nrpages = __roundup_pow_of_two(nrpages);
-
-   if (!dmar_forcedac && dma_mask > DMA_BIT_MASK(32)) 

[RFC 7/7] iommu/vt-d: Always set DMA_PTE_READ if the iommu doens't support zero length reads

2019-05-04 Thread Tom Murphy via iommu
To match the dma-ops api path the DMA_PTE_READ should be set if ZLR
isn't supported in the iommu

Signed-off-by: Tom Murphy 
---
 drivers/iommu/intel-iommu.c | 13 -
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 980fc4816d72..e78b056d 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -4378,6 +4378,17 @@ static void intel_iommu_detach_device(struct 
iommu_domain *domain,
dmar_remove_one_dev_info(dev);
 }
 
+static bool supports_zlr(struct dmar_domain *domain)
+{
+   int i;
+
+   for_each_domain_iommu(i, domain) {
+   if (cap_zlr(g_iommus[i]->cap))
+   return true;
+   }
+   return false;
+}
+
 static int intel_iommu_map(struct iommu_domain *domain,
   unsigned long iova, phys_addr_t hpa,
   size_t size, int iommu_prot)
@@ -4391,7 +4402,7 @@ static int intel_iommu_map(struct iommu_domain *domain,
if (dmar_domain == si_domain && hw_pass_through)
return 0;
 
-   if (iommu_prot & IOMMU_READ)
+   if (iommu_prot & IOMMU_READ || !supports_zlr(dmar_domain))
prot |= DMA_PTE_READ;
if (iommu_prot & IOMMU_WRITE)
prot |= DMA_PTE_WRITE;
-- 
2.17.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[RFC 2/7] iommu/vt-d: Remove iova handling code from non-dma ops path

2019-05-04 Thread Tom Murphy via iommu
There is no reason to keep track of the iovas in the non-dma ops path.
All this code seems to be pointless and can be removed.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/intel-iommu.c | 94 +
 1 file changed, 33 insertions(+), 61 deletions(-)

diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 2db1dc47e7e4..77895cd89f29 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -1846,11 +1846,6 @@ static int dmar_init_reserved_ranges(void)
return 0;
 }
 
-static void domain_reserve_special_ranges(struct dmar_domain *domain)
-{
-   copy_reserved_iova(_iova_list, >iovad);
-}
-
 static inline int guestwidth_to_adjustwidth(int gaw)
 {
int agaw;
@@ -1875,7 +1870,8 @@ static void domain_exit(struct dmar_domain *domain)
rcu_read_unlock();
 
/* destroy iovas */
-   put_iova_domain(>iovad);
+   if (domain->domain.type == IOMMU_DOMAIN_DMA)
+   put_iova_domain(>iovad);
 
freelist = domain_unmap(domain, 0, DOMAIN_MAX_PFN(domain->gaw));
 
@@ -2554,19 +2550,9 @@ static struct dmar_domain 
*dmar_insert_one_dev_info(struct intel_iommu *iommu,
 }
 
 static int iommu_domain_identity_map(struct dmar_domain *domain,
-unsigned long long start,
-unsigned long long end)
+unsigned long first_vpfn,
+unsigned long last_vpfn)
 {
-   unsigned long first_vpfn = start >> VTD_PAGE_SHIFT;
-   unsigned long last_vpfn = end >> VTD_PAGE_SHIFT;
-
-   if (!reserve_iova(>iovad, dma_to_mm_pfn(first_vpfn),
- dma_to_mm_pfn(last_vpfn))) {
-   pr_err("Reserving iova failed\n");
-   return -ENOMEM;
-   }
-
-   pr_debug("Mapping reserved region %llx-%llx\n", start, end);
/*
 * RMRR range might have overlap with physical memory range,
 * clear it first
@@ -2613,7 +2599,8 @@ static int __init si_domain_init(int hw)
 
for_each_mem_pfn_range(i, nid, _pfn, _pfn, NULL) {
ret = iommu_domain_identity_map(si_domain,
-   PFN_PHYS(start_pfn), PFN_PHYS(end_pfn));
+   mm_to_dma_pfn(start_pfn),
+   mm_to_dma_pfn(end_pfn));
if (ret)
return ret;
}
@@ -4181,58 +4168,37 @@ static int intel_iommu_memory_notifier(struct 
notifier_block *nb,
   unsigned long val, void *v)
 {
struct memory_notify *mhp = v;
-   unsigned long long start, end;
-   unsigned long start_vpfn, last_vpfn;
+   unsigned long start_vpfn = mm_to_dma_pfn(mhp->start_pfn);
+   unsigned long last_vpfn = mm_to_dma_pfn(mhp->start_pfn +
+   mhp->nr_pages - 1);
 
switch (val) {
case MEM_GOING_ONLINE:
-   start = mhp->start_pfn << PAGE_SHIFT;
-   end = ((mhp->start_pfn + mhp->nr_pages) << PAGE_SHIFT) - 1;
-   if (iommu_domain_identity_map(si_domain, start, end)) {
-   pr_warn("Failed to build identity map for 
[%llx-%llx]\n",
-   start, end);
+   if (iommu_domain_identity_map(si_domain, start_vpfn,
+   last_vpfn)) {
+   pr_warn("Failed to build identity map for [%lx-%lx]\n",
+   start_vpfn, last_vpfn);
return NOTIFY_BAD;
}
break;
 
case MEM_OFFLINE:
case MEM_CANCEL_ONLINE:
-   start_vpfn = mm_to_dma_pfn(mhp->start_pfn);
-   last_vpfn = mm_to_dma_pfn(mhp->start_pfn + mhp->nr_pages - 1);
-   while (start_vpfn <= last_vpfn) {
-   struct iova *iova;
+   {
struct dmar_drhd_unit *drhd;
struct intel_iommu *iommu;
struct page *freelist;
 
-   iova = find_iova(_domain->iovad, start_vpfn);
-   if (iova == NULL) {
-   pr_debug("Failed get IOVA for PFN %lx\n",
-start_vpfn);
-   break;
-   }
-
-   iova = split_and_remove_iova(_domain->iovad, iova,
-start_vpfn, last_vpfn);
-   if (iova == NULL) {
-   pr_warn("Failed to split IOVA PFN [%lx-%lx]\n",
-   start_vpfn, last_vpfn);
-   

[RFC 4/7] iommu/dma-iommu: Handle freelists in the dma-iommu api path

2019-05-04 Thread Tom Murphy via iommu
Currently the iova flush queue implementation in the dma-iommu api path
doesn't handle freelists. Change the unmap_fast code to allow it to
return any freelists which need to be handled.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/dma-iommu.c   | 39 +++--
 drivers/iommu/iommu.c   | 10 +
 drivers/vfio/vfio_iommu_type1.c |  2 +-
 include/linux/iommu.h   |  3 ++-
 4 files changed, 36 insertions(+), 18 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index fa5713a4f6f8..82ba500886b4 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -49,6 +49,18 @@ struct iommu_dma_cookie {
struct iommu_domain *fq_domain;
 };
 
+static void iommu_dma_entry_dtor(unsigned long data)
+{
+   struct page *freelist = (struct page *)data;
+
+   while (freelist != NULL) {
+   unsigned long p = (unsigned long)page_address(freelist);
+
+   freelist = freelist->freelist;
+   free_page(p);
+   }
+}
+
 static inline size_t cookie_msi_granule(struct iommu_dma_cookie *cookie)
 {
if (cookie->type == IOMMU_DMA_IOVA_COOKIE)
@@ -313,7 +325,8 @@ static int iommu_dma_init_domain(struct iommu_domain 
*domain, dma_addr_t base,
if (!cookie->fq_domain && !iommu_domain_get_attr(domain,
DOMAIN_ATTR_DMA_USE_FLUSH_QUEUE, ) && attr) {
cookie->fq_domain = domain;
-   init_iova_flush_queue(iovad, iommu_dma_flush_iotlb_all, NULL);
+   init_iova_flush_queue(iovad, iommu_dma_flush_iotlb_all,
+   iommu_dma_entry_dtor);
}
 
if (!dev)
@@ -393,7 +406,7 @@ static dma_addr_t iommu_dma_alloc_iova(struct iommu_domain 
*domain,
 }
 
 static void iommu_dma_free_iova(struct iommu_dma_cookie *cookie,
-   dma_addr_t iova, size_t size)
+   dma_addr_t iova, size_t size, struct page *freelist)
 {
struct iova_domain *iovad = >iovad;
 
@@ -402,7 +415,8 @@ static void iommu_dma_free_iova(struct iommu_dma_cookie 
*cookie,
cookie->msi_iova -= size;
else if (cookie->fq_domain) /* non-strict mode */
queue_iova(iovad, iova_pfn(iovad, iova),
-   size >> iova_shift(iovad), 0);
+   size >> iova_shift(iovad),
+   (unsigned long) freelist);
else
free_iova_fast(iovad, iova_pfn(iovad, iova),
size >> iova_shift(iovad));
@@ -414,14 +428,15 @@ static void __iommu_dma_unmap(struct iommu_domain 
*domain, dma_addr_t dma_addr,
struct iommu_dma_cookie *cookie = domain->iova_cookie;
struct iova_domain *iovad = >iovad;
size_t iova_off = iova_offset(iovad, dma_addr);
+   struct page *freelist;
 
dma_addr -= iova_off;
size = iova_align(iovad, size + iova_off);
 
-   WARN_ON(iommu_unmap_fast(domain, dma_addr, size) != size);
+   WARN_ON(iommu_unmap_fast(domain, dma_addr, size, ) != size);
if (!cookie->fq_domain)
-   iommu_tlb_sync(domain);
-   iommu_dma_free_iova(cookie, dma_addr, size);
+   iommu_flush_iotlb_range(domain, dma_addr, size, freelist);
+   iommu_dma_free_iova(cookie, dma_addr, size, freelist);
 }
 
 static dma_addr_t __iommu_dma_map(struct device *dev, phys_addr_t phys,
@@ -441,7 +456,7 @@ static dma_addr_t __iommu_dma_map(struct device *dev, 
phys_addr_t phys,
return DMA_MAPPING_ERROR;
 
if (iommu_map(domain, iova, phys - iova_off, size, prot)) {
-   iommu_dma_free_iova(cookie, iova, size);
+   iommu_dma_free_iova(cookie, iova, size, NULL);
return DMA_MAPPING_ERROR;
}
return iova + iova_off;
@@ -600,7 +615,7 @@ static void *iommu_dma_alloc_remap(struct device *dev, 
size_t size,
struct iova_domain *iovad = >iovad;
bool coherent = dev_is_dma_coherent(dev);
int ioprot = dma_info_to_prot(DMA_BIDIRECTIONAL, coherent, attrs);
-   pgprot_t prot = arch_dma_mmap_pgprot(dev, PAGE_KERNEL, attrs);
+   pgprot_t prot = pgprot_noncached(PAGE_KERNEL);
unsigned int count, min_size, alloc_sizes = domain->pgsize_bitmap;
struct page **pages;
struct sg_table sgt;
@@ -659,7 +674,7 @@ static void *iommu_dma_alloc_remap(struct device *dev, 
size_t size,
 out_free_sg:
sg_free_table();
 out_free_iova:
-   iommu_dma_free_iova(cookie, iova, size);
+   iommu_dma_free_iova(cookie, iova, size, NULL);
 out_free_pages:
__iommu_dma_free_pages(pages, count);
return NULL;
@@ -668,7 +683,7 @@ static void *iommu_dma_alloc_remap(struct device *dev, 
size_t size,
 static void *iommu_dma_alloc_contiguous_remap(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t gfp

[RFC 0/7] Convert the Intel iommu driver to the dma-ops api

2019-05-04 Thread Tom Murphy via iommu
Convert the intel iommu driver to the dma-ops api so that we can remove a bunch 
of repeated code.

This patchset depends on the "iommu/vt-d: Delegate DMA domain to generic iommu" 
and
"iommu/amd: Convert the AMD iommu driver to the dma-iommu api" patch sets which 
haven't
yet merged so this is just a RFC to get some feedback before I do more testing.

Tom Murphy (7):
  iommu/vt-d: Set the dma_ops per device so we can remove the
iommu_no_mapping code
  iommu/vt-d: Remove iova handling code from non-dma ops path
  iommu: improve iommu iotlb flushing
  iommu/dma-iommu: Handle freelists in the dma-iommu api path
  iommu/dma-iommu: add wrapper for iommu_dma_free_cpu_cached_iovas
  iommu/vt-d: convert the intel iommu driver to the dma-iommu ops api
  iommu/vt-d: Always set DMA_PTE_READ if the iommu doens't support zero
length reads

 drivers/iommu/Kconfig   |   1 +
 drivers/iommu/amd_iommu.c   |  14 +-
 drivers/iommu/arm-smmu-v3.c |   3 +-
 drivers/iommu/arm-smmu.c|   2 +-
 drivers/iommu/dma-iommu.c   |  48 ++-
 drivers/iommu/exynos-iommu.c|   3 +-
 drivers/iommu/intel-iommu.c | 605 +---
 drivers/iommu/iommu.c   |  21 +-
 drivers/iommu/ipmmu-vmsa.c  |   2 +-
 drivers/iommu/msm_iommu.c   |   2 +-
 drivers/iommu/mtk_iommu.c   |   3 +-
 drivers/iommu/mtk_iommu_v1.c|   3 +-
 drivers/iommu/omap-iommu.c  |   2 +-
 drivers/iommu/qcom_iommu.c  |   2 +-
 drivers/iommu/rockchip-iommu.c  |   2 +-
 drivers/iommu/s390-iommu.c  |   3 +-
 drivers/iommu/tegra-gart.c  |   2 +-
 drivers/iommu/tegra-smmu.c  |   2 +-
 drivers/vfio/vfio_iommu_type1.c |   3 +-
 include/linux/dma-iommu.h   |   3 +
 include/linux/intel-iommu.h |   1 -
 include/linux/iommu.h   |  24 +-
 22 files changed, 175 insertions(+), 576 deletions(-)

-- 
2.17.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[RFC 1/7] iommu/vt-d: Set the dma_ops per device so we can remove the iommu_no_mapping code

2019-05-04 Thread Tom Murphy via iommu
Set the dma_ops per device so we can remove the iommu_no_mapping code.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/intel-iommu.c | 85 +++--
 1 file changed, 6 insertions(+), 79 deletions(-)

diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index eace915602f0..2db1dc47e7e4 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -2622,17 +2622,6 @@ static int __init si_domain_init(int hw)
return 0;
 }
 
-static int identity_mapping(struct device *dev)
-{
-   struct device_domain_info *info;
-
-   info = dev->archdata.iommu;
-   if (info && info != DUMMY_DEVICE_DOMAIN_INFO)
-   return (info->domain == si_domain);
-
-   return 0;
-}
-
 static int domain_add_dev_info(struct dmar_domain *domain, struct device *dev)
 {
struct dmar_domain *ndomain;
@@ -3270,43 +3259,6 @@ static unsigned long intel_alloc_iova(struct device *dev,
return iova_pfn;
 }
 
-/* Check if the dev needs to go through non-identity map and unmap process.*/
-static int iommu_no_mapping(struct device *dev)
-{
-   int found;
-
-   if (iommu_dummy(dev))
-   return 1;
-
-   found = identity_mapping(dev);
-   if (found) {
-   /*
-* If the device's dma_mask is less than the system's memory
-* size then this is not a candidate for identity mapping.
-*/
-   u64 dma_mask = *dev->dma_mask;
-
-   if (dev->coherent_dma_mask &&
-   dev->coherent_dma_mask < dma_mask)
-   dma_mask = dev->coherent_dma_mask;
-
-   if (dma_mask < dma_get_required_mask(dev)) {
-   /*
-* 32 bit DMA is removed from si_domain and fall back
-* to non-identity mapping.
-*/
-   dmar_remove_one_dev_info(dev);
-   dev_warn(dev, "32bit DMA uses non-identity mapping\n");
-
-   return 0;
-   }
-
-   return 1;
-   }
-
-   return 0;
-}
-
 static dma_addr_t __intel_map_single(struct device *dev, phys_addr_t paddr,
 size_t size, int dir, u64 dma_mask)
 {
@@ -3320,9 +3272,6 @@ static dma_addr_t __intel_map_single(struct device *dev, 
phys_addr_t paddr,
 
BUG_ON(dir == DMA_NONE);
 
-   if (iommu_no_mapping(dev))
-   return paddr;
-
domain = find_domain(dev);
if (!domain)
return DMA_MAPPING_ERROR;
@@ -3391,9 +3340,6 @@ static void intel_unmap(struct device *dev, dma_addr_t 
dev_addr, size_t size)
struct intel_iommu *iommu;
struct page *freelist;
 
-   if (iommu_no_mapping(dev))
-   return;
-
domain = find_domain(dev);
BUG_ON(!domain);
 
@@ -3442,9 +3388,7 @@ static void *intel_alloc_coherent(struct device *dev, 
size_t size,
size = PAGE_ALIGN(size);
order = get_order(size);
 
-   if (!iommu_no_mapping(dev))
-   flags &= ~(GFP_DMA | GFP_DMA32);
-   else if (dev->coherent_dma_mask < dma_get_required_mask(dev)) {
+   if (dev->coherent_dma_mask < dma_get_required_mask(dev)) {
if (dev->coherent_dma_mask < DMA_BIT_MASK(32))
flags |= GFP_DMA;
else
@@ -3456,11 +3400,6 @@ static void *intel_alloc_coherent(struct device *dev, 
size_t size,
 
page = dma_alloc_from_contiguous(dev, count, order,
 flags & __GFP_NOWARN);
-   if (page && iommu_no_mapping(dev) &&
-   page_to_phys(page) + size > dev->coherent_dma_mask) {
-   dma_release_from_contiguous(dev, page, count);
-   page = NULL;
-   }
}
 
if (!page)
@@ -3510,20 +3449,6 @@ static void intel_unmap_sg(struct device *dev, struct 
scatterlist *sglist,
intel_unmap(dev, startaddr, nrpages << VTD_PAGE_SHIFT);
 }
 
-static int intel_nontranslate_map_sg(struct device *hddev,
-   struct scatterlist *sglist, int nelems, int dir)
-{
-   int i;
-   struct scatterlist *sg;
-
-   for_each_sg(sglist, sg, nelems, i) {
-   BUG_ON(!sg_page(sg));
-   sg->dma_address = sg_phys(sg);
-   sg->dma_length = sg->length;
-   }
-   return nelems;
-}
-
 static int intel_map_sg(struct device *dev, struct scatterlist *sglist, int 
nelems,
enum dma_data_direction dir, unsigned long attrs)
 {
@@ -3538,8 +3463,6 @@ static int intel_map_sg(struct device *dev, struct 
scatterlist *sglist, int nele
struct intel_iommu *iommu;
 
BUG_ON(dir == DMA_NONE);
-   if (iommu_no_mapping(dev))
-   return intel_no

Re: [PATCH v2 2/4] iommu/dma-iommu: Handle deferred devices

2019-04-30 Thread Tom Murphy via iommu
On Tue, Apr 30, 2019 at 2:42 PM Robin Murphy  wrote:
>
> On 30/04/2019 01:29, Tom Murphy wrote:
> > Handle devices which defer their attach to the iommu in the dma-iommu api
>
> I've just spent a while trying to understand what this is about...
>
> AFAICS it's a kdump thing where the regular default domain attachment
> may lead to ongoing DMA traffic from the crashed kernel raising a fault
> storm, so we put off the "real" attach of a given device until we know
> it's been reset and brought into a sane state, but the only way to
> reliably detect that is to wait until the kdump kernel driver starts
> making new DMA mappings. Is that about right?

That's the impression I got too. The many iterations of the patch
series which contributed this code makes it hard to figure out exactly
why it's doing what it's doing but AFAIK it works how you described
it.

>
> (I note that for SMMUv3 we now handle that situation with the slightly
> more heavy-handed approach of just turning off reporting and letting the
> 'rogue' devices fault silently, but I appreciate that not all IOMMUs may
> have that option)
>
> > Signed-off-by: Tom Murphy 
> > ---
> >   drivers/iommu/dma-iommu.c | 30 ++
> >   1 file changed, 30 insertions(+)
> >
> > diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
> > index 7a96c2c8f56b..c18f74ad1e8b 100644
> > --- a/drivers/iommu/dma-iommu.c
> > +++ b/drivers/iommu/dma-iommu.c
> > @@ -322,6 +322,17 @@ static int iommu_dma_init_domain(struct iommu_domain 
> > *domain, dma_addr_t base,
> >   return iova_reserve_iommu_regions(dev, domain);
> >   }
> >
> > +static int handle_deferred_device(struct device *dev)
> > +{
> > + struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
>
> We don't want iommu_get_domain_for_dev() in fast-paths, as the
> contention on the group refcount has proven to have a surprisingly high
> overhead on some large systems. That's what iommu_get_dma_domain()
> solves, but ideally, can this be wrapped in is_kdump_kernel() such as to
> have no impact at all on the general case?

will do.

>
> > + const struct iommu_ops *ops = domain->ops;
> > +
> > + if (ops->is_attach_deferred && ops->is_attach_deferred(domain, dev))
> > + return iommu_attach_device(domain, dev);
> > +
> > + return 0;
> > +}
> > +
> >   /**
> >* dma_info_to_prot - Translate DMA API directions and attributes to 
> > IOMMU API
> >*page flags.
> > @@ -835,6 +846,8 @@ static dma_addr_t iommu_dma_map_page(struct device 
> > *dev, struct page *page,
> >   bool coherent = dev_is_dma_coherent(dev);
> >   dma_addr_t dma_handle;
> >
> > + handle_deferred_device(dev);
> > +
> >   dma_handle =__iommu_dma_map(dev, phys, size,
> >   dma_info_to_prot(dir, coherent, attrs),
> >   iommu_get_dma_domain(dev));
> > @@ -849,6 +862,8 @@ static void iommu_dma_unmap_page(struct device *dev, 
> > dma_addr_t dma_handle,
> >   {
> >   struct iommu_domain *domain = iommu_get_dma_domain(dev);
> >
> > + handle_deferred_device(dev);
>
> You don't need this - it's completely bogus to make an unmap call
> without having already called the corresponding map function, so we can
> assume it's already been dealt with.
>
> > +
> >   if (!dev_is_dma_coherent(dev) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) {
> >   phys_addr_t phys = iommu_iova_to_phys(domain, dma_handle);
> >
> > @@ -873,6 +888,8 @@ static int __finalise_sg(struct device *dev, struct 
> > scatterlist *sg, int nents,
> >   unsigned int cur_len = 0, max_len = dma_get_max_seg_size(dev);
> >   int i, count = 0;
> >
> > + handle_deferred_device(dev);
>
> Hmm, this should be in iommu_dma_map_sg() - that's the guy that needs a
> valid domain, and it's impossible to get to __finalise_sg() without
> having been through there anyway.
>
> > +
> >   for_each_sg(sg, s, nents, i) {
> >   /* Restore this segment's original unaligned fields first */
> >   unsigned int s_iova_off = sg_dma_address(s);
> > @@ -1022,6 +1039,8 @@ static void iommu_dma_unmap_sg(struct device *dev, 
> > struct scatterlist *sg,
> >   struct scatterlist *tmp;
> >   int i;
> >
> > + handle_deferred_device(dev);
>
> Again, not necessary.
>
> > +
> >   if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
> >   iommu_dma_sync_sg_for_

[PATCH v2 3/4] iommu/dma-iommu: Use the dev->coherent_dma_mask

2019-04-29 Thread Tom Murphy via iommu
Use the dev->coherent_dma_mask when allocating in the dma-iommu ops api.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/dma-iommu.c | 16 +---
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index c18f74ad1e8b..df03104978d7 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -436,7 +436,8 @@ static void __iommu_dma_unmap(struct iommu_domain *domain, 
dma_addr_t dma_addr,
 }
 
 static dma_addr_t __iommu_dma_map(struct device *dev, phys_addr_t phys,
-   size_t size, int prot, struct iommu_domain *domain)
+   size_t size, int prot, struct iommu_domain *domain,
+   dma_addr_t dma_limit)
 {
struct iommu_dma_cookie *cookie = domain->iova_cookie;
size_t iova_off = 0;
@@ -447,7 +448,7 @@ static dma_addr_t __iommu_dma_map(struct device *dev, 
phys_addr_t phys,
size = iova_align(>iovad, size + iova_off);
}
 
-   iova = iommu_dma_alloc_iova(domain, size, dma_get_mask(dev), dev);
+   iova = iommu_dma_alloc_iova(domain, size, dma_limit, dev);
if (!iova)
return DMA_MAPPING_ERROR;
 
@@ -490,7 +491,7 @@ static void *iommu_dma_alloc_contiguous(struct device *dev, 
size_t size,
return NULL;
 
*dma_handle = __iommu_dma_map(dev, page_to_phys(page), size, ioprot,
-   iommu_get_dma_domain(dev));
+   iommu_get_dma_domain(dev), dev->coherent_dma_mask);
if (*dma_handle == DMA_MAPPING_ERROR) {
if (!dma_release_from_contiguous(dev, page, count))
__free_pages(page, page_order);
@@ -760,7 +761,7 @@ static void *iommu_dma_alloc_pool(struct device *dev, 
size_t size,
 
*dma_handle = __iommu_dma_map(dev, page_to_phys(page), size,
dma_info_to_prot(DMA_BIDIRECTIONAL, coherent, attrs),
-   iommu_get_domain_for_dev(dev));
+   iommu_get_domain_for_dev(dev), dev->coherent_dma_mask);
if (*dma_handle == DMA_MAPPING_ERROR) {
dma_free_from_pool(vaddr, PAGE_ALIGN(size));
return NULL;
@@ -850,7 +851,7 @@ static dma_addr_t iommu_dma_map_page(struct device *dev, 
struct page *page,
 
dma_handle =__iommu_dma_map(dev, phys, size,
dma_info_to_prot(dir, coherent, attrs),
-   iommu_get_dma_domain(dev));
+   iommu_get_dma_domain(dev), dma_get_mask(dev));
if (!coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
dma_handle != DMA_MAPPING_ERROR)
arch_sync_dma_for_device(dev, phys, size, dir);
@@ -1065,7 +1066,7 @@ static dma_addr_t iommu_dma_map_resource(struct device 
*dev, phys_addr_t phys,
 
return __iommu_dma_map(dev, phys, size,
dma_info_to_prot(dir, false, attrs) | IOMMU_MMIO,
-   iommu_get_dma_domain(dev));
+   iommu_get_dma_domain(dev), dma_get_mask(dev));
 }
 
 static void iommu_dma_unmap_resource(struct device *dev, dma_addr_t handle,
@@ -1250,7 +1251,8 @@ static struct iommu_dma_msi_page 
*iommu_dma_get_msi_page(struct device *dev,
if (!msi_page)
return NULL;
 
-   iova = __iommu_dma_map(dev, msi_addr, size, prot, domain);
+   iova = __iommu_dma_map(dev, msi_addr, size, prot, domain,
+   dma_get_mask(dev));
if (iova == DMA_MAPPING_ERROR)
goto out_free_page;
 
-- 
2.17.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v2 4/4] iommu/amd: Convert the AMD iommu driver to the dma-iommu api

2019-04-29 Thread Tom Murphy via iommu
Convert the AMD iommu driver to the dma-iommu api. Remove the iova
handling and reserve region code from the AMD iommu driver.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/Kconfig |   1 +
 drivers/iommu/amd_iommu.c | 680 --
 2 files changed, 70 insertions(+), 611 deletions(-)

diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 6f07f3b21816..5914ba85180b 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -136,6 +136,7 @@ config AMD_IOMMU
select PCI_PASID
select IOMMU_API
select IOMMU_IOVA
+   select IOMMU_DMA
depends on X86_64 && PCI && ACPI
---help---
  With this option you can enable support for AMD IOMMU hardware in
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index ea3a5dc61bb0..366af2b27e7f 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -32,6 +32,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -101,8 +102,6 @@ const struct iommu_ops amd_iommu_ops;
 static ATOMIC_NOTIFIER_HEAD(ppr_notifier);
 int amd_iommu_max_glx_val = -1;
 
-static const struct dma_map_ops amd_iommu_dma_ops;
-
 /*
  * general struct to manage commands send to an IOMMU
  */
@@ -115,21 +114,6 @@ struct kmem_cache *amd_iommu_irq_cache;
 static void update_domain(struct protection_domain *domain);
 static int protection_domain_init(struct protection_domain *domain);
 static void detach_device(struct device *dev);
-static void iova_domain_flush_tlb(struct iova_domain *iovad);
-
-/*
- * Data container for a dma_ops specific protection domain
- */
-struct dma_ops_domain {
-   /* generic protection domain information */
-   struct protection_domain domain;
-
-   /* IOVA RB-Tree */
-   struct iova_domain iovad;
-};
-
-static struct iova_domain reserved_iova_ranges;
-static struct lock_class_key reserved_rbtree_key;
 
 /
  *
@@ -200,12 +184,6 @@ static struct protection_domain *to_pdomain(struct 
iommu_domain *dom)
return container_of(dom, struct protection_domain, domain);
 }
 
-static struct dma_ops_domain* to_dma_ops_domain(struct protection_domain 
*domain)
-{
-   BUG_ON(domain->flags != PD_DMA_OPS_MASK);
-   return container_of(domain, struct dma_ops_domain, domain);
-}
-
 static struct iommu_dev_data *alloc_dev_data(u16 devid)
 {
struct iommu_dev_data *dev_data;
@@ -1279,12 +1257,6 @@ static void domain_flush_pages(struct protection_domain 
*domain,
__domain_flush_pages(domain, address, size, 0);
 }
 
-/* Flush the whole IO/TLB for a given protection domain */
-static void domain_flush_tlb(struct protection_domain *domain)
-{
-   __domain_flush_pages(domain, 0, CMD_INV_IOMMU_ALL_PAGES_ADDRESS, 0);
-}
-
 /* Flush the whole IO/TLB for a given protection domain - including PDE */
 static void domain_flush_tlb_pde(struct protection_domain *domain)
 {
@@ -1686,43 +1658,6 @@ static unsigned long iommu_unmap_page(struct 
protection_domain *dom,
return unmapped;
 }
 
-/
- *
- * The next functions belong to the address allocator for the dma_ops
- * interface functions.
- *
- /
-
-
-static unsigned long dma_ops_alloc_iova(struct device *dev,
-   struct dma_ops_domain *dma_dom,
-   unsigned int pages, u64 dma_mask)
-{
-   unsigned long pfn = 0;
-
-   pages = __roundup_pow_of_two(pages);
-
-   if (dma_mask > DMA_BIT_MASK(32))
-   pfn = alloc_iova_fast(_dom->iovad, pages,
- IOVA_PFN(DMA_BIT_MASK(32)), false);
-
-   if (!pfn)
-   pfn = alloc_iova_fast(_dom->iovad, pages,
- IOVA_PFN(dma_mask), true);
-
-   return (pfn << PAGE_SHIFT);
-}
-
-static void dma_ops_free_iova(struct dma_ops_domain *dma_dom,
- unsigned long address,
- unsigned int pages)
-{
-   pages = __roundup_pow_of_two(pages);
-   address >>= PAGE_SHIFT;
-
-   free_iova_fast(_dom->iovad, address, pages);
-}
-
 /
  *
  * The next functions belong to the domain allocation. A domain is
@@ -1824,40 +1759,25 @@ static void free_gcr3_table(struct protection_domain 
*domain)
free_page((unsigned long)domain->gcr3_tbl);
 }
 
-static void dma_ops_domain_flush_tlb(struct dma_ops_domain *dom)
-{
-   domain_flush_tlb(>domain);
-   domain_flush_complete(>domain);
-}
-
-static void iova_domain_flush_tlb(struct iova_domain *iovad)
-{
-   struct dma_ops_domain *dom;
-
-   dom = container_of(iovad, struct dma_ops_do

[PATCH v2 1/4] iommu: Add gfp parameter to iommu_ops::map

2019-04-29 Thread Tom Murphy via iommu
Add a gfp_t parameter to the iommu_ops::map function.
Remove the needless locking in the AMD iommu driver.

The iommu_ops::map function (or the iommu_map function which calls it)
was always supposed to be sleepable (according to Joerg's comment in
this thread: https://lore.kernel.org/patchwork/patch/977520/ ) and so
should probably have had a "might_sleep()" since it was written. However
currently the dma-iommu api can call iommu_map in an atomic context,
which it shouldn't do. This doesn't cause any problems because any iommu
driver which uses the dma-iommu api uses gfp_atomic in it's
iommu_ops::map function. But doing this wastes the memory allocators
atomic pools.

We can remove the mutex lock from amd_iommu_map and amd_iommu_unmap.
iommu_map doesn’t lock while mapping and so no two calls should touch
the same iova range. The AMD driver already handles the page table page
allocations without locks so we can safely remove the locks.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/amd_iommu.c  | 14 ---
 drivers/iommu/arm-smmu-v3.c|  2 +-
 drivers/iommu/arm-smmu.c   |  2 +-
 drivers/iommu/dma-iommu.c  |  6 ++---
 drivers/iommu/exynos-iommu.c   |  2 +-
 drivers/iommu/intel-iommu.c|  2 +-
 drivers/iommu/iommu.c  | 43 +-
 drivers/iommu/ipmmu-vmsa.c |  2 +-
 drivers/iommu/msm_iommu.c  |  2 +-
 drivers/iommu/mtk_iommu.c  |  2 +-
 drivers/iommu/mtk_iommu_v1.c   |  2 +-
 drivers/iommu/omap-iommu.c |  2 +-
 drivers/iommu/qcom_iommu.c |  2 +-
 drivers/iommu/rockchip-iommu.c |  2 +-
 drivers/iommu/s390-iommu.c |  2 +-
 drivers/iommu/tegra-gart.c |  2 +-
 drivers/iommu/tegra-smmu.c |  2 +-
 include/linux/iommu.h  | 21 -
 18 files changed, 78 insertions(+), 34 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index ebd062522cf5..ea3a5dc61bb0 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -3092,7 +3092,8 @@ static int amd_iommu_attach_device(struct iommu_domain 
*dom,
 }
 
 static int amd_iommu_map(struct iommu_domain *dom, unsigned long iova,
-phys_addr_t paddr, size_t page_size, int iommu_prot)
+phys_addr_t paddr, size_t page_size, int iommu_prot,
+gfp_t gfp)
 {
struct protection_domain *domain = to_pdomain(dom);
int prot = 0;
@@ -3106,9 +3107,7 @@ static int amd_iommu_map(struct iommu_domain *dom, 
unsigned long iova,
if (iommu_prot & IOMMU_WRITE)
prot |= IOMMU_PROT_IW;
 
-   mutex_lock(>api_lock);
-   ret = iommu_map_page(domain, iova, paddr, page_size, prot, GFP_KERNEL);
-   mutex_unlock(>api_lock);
+   ret = iommu_map_page(domain, iova, paddr, page_size, prot, gfp);
 
domain_flush_np_cache(domain, iova, page_size);
 
@@ -3119,16 +3118,11 @@ static size_t amd_iommu_unmap(struct iommu_domain *dom, 
unsigned long iova,
   size_t page_size)
 {
struct protection_domain *domain = to_pdomain(dom);
-   size_t unmap_size;
 
if (domain->mode == PAGE_MODE_NONE)
return 0;
 
-   mutex_lock(>api_lock);
-   unmap_size = iommu_unmap_page(domain, iova, page_size);
-   mutex_unlock(>api_lock);
-
-   return unmap_size;
+   return iommu_unmap_page(domain, iova, page_size);
 }
 
 static phys_addr_t amd_iommu_iova_to_phys(struct iommu_domain *dom,
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index d3880010c6cf..e5c48089b49f 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -1777,7 +1777,7 @@ static int arm_smmu_attach_dev(struct iommu_domain 
*domain, struct device *dev)
 }
 
 static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
-   phys_addr_t paddr, size_t size, int prot)
+   phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
 {
struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops;
 
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 045d93884164..2d50db55b788 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -1286,7 +1286,7 @@ static int arm_smmu_attach_dev(struct iommu_domain 
*domain, struct device *dev)
 }
 
 static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
-   phys_addr_t paddr, size_t size, int prot)
+   phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
 {
struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops;
struct arm_smmu_device *smmu = to_smmu_domain(domain)->smmu;
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index fa5713a4f6f8..7a96c2c8f56b 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -440,7 +440,7 @@ static dma_addr_t __iommu_dma_map(struct device *dev, 
phys_addr_t phys,
   

[PATCH v2 0/4] iommu/amd: Convert the AMD iommu driver to the dma-iommu api

2019-04-29 Thread Tom Murphy via iommu
Convert the AMD iommu driver to the dma-iommu api. Remove the iova
handling and reserve region code from the AMD iommu driver.

Change-log:
v2:
-Rebase on top of this series:
 http://git.infradead.org/users/hch/misc.git/shortlog/refs/heads/dma-iommu-ops.3
-Add a gfp_t parameter to the iommu_ops::map function.
-Made use of the reserve region code inside the dma-iommu api

Tom Murphy (4):
  iommu: Add gfp parameter to iommu_ops::map
  iommu/dma-iommu: Handle deferred devices
  iommu/dma-iommu: Use the dev->coherent_dma_mask
  iommu/amd: Convert the AMD iommu driver to the dma-iommu api

 drivers/iommu/Kconfig  |   1 +
 drivers/iommu/amd_iommu.c  | 694 -
 drivers/iommu/arm-smmu-v3.c|   2 +-
 drivers/iommu/arm-smmu.c   |   2 +-
 drivers/iommu/dma-iommu.c  |  52 ++-
 drivers/iommu/exynos-iommu.c   |   2 +-
 drivers/iommu/intel-iommu.c|   2 +-
 drivers/iommu/iommu.c  |  43 +-
 drivers/iommu/ipmmu-vmsa.c |   2 +-
 drivers/iommu/msm_iommu.c  |   2 +-
 drivers/iommu/mtk_iommu.c  |   2 +-
 drivers/iommu/mtk_iommu_v1.c   |   2 +-
 drivers/iommu/omap-iommu.c |   2 +-
 drivers/iommu/qcom_iommu.c |   2 +-
 drivers/iommu/rockchip-iommu.c |   2 +-
 drivers/iommu/s390-iommu.c |   2 +-
 drivers/iommu/tegra-gart.c |   2 +-
 drivers/iommu/tegra-smmu.c |   2 +-
 include/linux/iommu.h  |  21 +-
 19 files changed, 187 insertions(+), 652 deletions(-)

-- 
2.17.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v2 2/4] iommu/dma-iommu: Handle deferred devices

2019-04-29 Thread Tom Murphy via iommu
Handle devices which defer their attach to the iommu in the dma-iommu api

Signed-off-by: Tom Murphy 
---
 drivers/iommu/dma-iommu.c | 30 ++
 1 file changed, 30 insertions(+)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 7a96c2c8f56b..c18f74ad1e8b 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -322,6 +322,17 @@ static int iommu_dma_init_domain(struct iommu_domain 
*domain, dma_addr_t base,
return iova_reserve_iommu_regions(dev, domain);
 }
 
+static int handle_deferred_device(struct device *dev)
+{
+   struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
+   const struct iommu_ops *ops = domain->ops;
+
+   if (ops->is_attach_deferred && ops->is_attach_deferred(domain, dev))
+   return iommu_attach_device(domain, dev);
+
+   return 0;
+}
+
 /**
  * dma_info_to_prot - Translate DMA API directions and attributes to IOMMU API
  *page flags.
@@ -835,6 +846,8 @@ static dma_addr_t iommu_dma_map_page(struct device *dev, 
struct page *page,
bool coherent = dev_is_dma_coherent(dev);
dma_addr_t dma_handle;
 
+   handle_deferred_device(dev);
+
dma_handle =__iommu_dma_map(dev, phys, size,
dma_info_to_prot(dir, coherent, attrs),
iommu_get_dma_domain(dev));
@@ -849,6 +862,8 @@ static void iommu_dma_unmap_page(struct device *dev, 
dma_addr_t dma_handle,
 {
struct iommu_domain *domain = iommu_get_dma_domain(dev);
 
+   handle_deferred_device(dev);
+
if (!dev_is_dma_coherent(dev) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) {
phys_addr_t phys = iommu_iova_to_phys(domain, dma_handle);
 
@@ -873,6 +888,8 @@ static int __finalise_sg(struct device *dev, struct 
scatterlist *sg, int nents,
unsigned int cur_len = 0, max_len = dma_get_max_seg_size(dev);
int i, count = 0;
 
+   handle_deferred_device(dev);
+
for_each_sg(sg, s, nents, i) {
/* Restore this segment's original unaligned fields first */
unsigned int s_iova_off = sg_dma_address(s);
@@ -1022,6 +1039,8 @@ static void iommu_dma_unmap_sg(struct device *dev, struct 
scatterlist *sg,
struct scatterlist *tmp;
int i;
 
+   handle_deferred_device(dev);
+
if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
iommu_dma_sync_sg_for_cpu(dev, sg, nents, dir);
 
@@ -1042,6 +1061,8 @@ static void iommu_dma_unmap_sg(struct device *dev, struct 
scatterlist *sg,
 static dma_addr_t iommu_dma_map_resource(struct device *dev, phys_addr_t phys,
size_t size, enum dma_data_direction dir, unsigned long attrs)
 {
+   handle_deferred_device(dev);
+
return __iommu_dma_map(dev, phys, size,
dma_info_to_prot(dir, false, attrs) | IOMMU_MMIO,
iommu_get_dma_domain(dev));
@@ -1050,12 +1071,15 @@ static dma_addr_t iommu_dma_map_resource(struct device 
*dev, phys_addr_t phys,
 static void iommu_dma_unmap_resource(struct device *dev, dma_addr_t handle,
size_t size, enum dma_data_direction dir, unsigned long attrs)
 {
+   handle_deferred_device(dev);
+
__iommu_dma_unmap(iommu_get_dma_domain(dev), handle, size);
 }
 
 static void *iommu_dma_alloc(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
 {
+   handle_deferred_device(dev);
gfp |= __GFP_ZERO;
 
 #ifdef CONFIG_DMA_DIRECT_REMAP
@@ -1076,6 +1100,8 @@ static void iommu_dma_free(struct device *dev, size_t 
size, void *cpu_addr,
 {
struct page *page;
 
+   handle_deferred_device(dev);
+
/*
 * cpu_addr can be one of 4 things depending on how it was allocated:
 *
@@ -1115,6 +1141,8 @@ static int iommu_dma_mmap(struct device *dev, struct 
vm_area_struct *vma,
unsigned long pfn;
int ret;
 
+   handle_deferred_device(dev);
+
vma->vm_page_prot = arch_dma_mmap_pgprot(dev, vma->vm_page_prot, attrs);
 
if (dma_mmap_from_dev_coherent(dev, vma, cpu_addr, size, ))
@@ -1143,6 +1171,8 @@ static int iommu_dma_get_sgtable(struct device *dev, 
struct sg_table *sgt,
struct page *page;
int ret;
 
+   handle_deferred_device(dev);
+
 #ifdef CONFIG_DMA_DIRECT_REMAP
if (is_vmalloc_addr(cpu_addr)) {
if (!(attrs & DMA_ATTR_FORCE_CONTIGUOUS))
-- 
2.17.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH v1] iommu/amd: flush not present cache in iommu_map_page

2019-04-29 Thread Tom Murphy via iommu
On Mon, Apr 29, 2019 at 12:59 PM Christoph Hellwig  wrote:
>
> On Sat, Apr 27, 2019 at 03:20:35PM +0100, Tom Murphy wrote:
> > I am working on another patch to improve the intel iotlb flushing in
> > the iommu ops patch which should cover this too.
>
> So are you looking into converting the intel-iommu driver to use
> dma-iommu as well?  That would be great!

Yes. My patches depend on the "iommu/vt-d: Delegate DMA domain to
generic iommu" patch which is currently being reviewed.
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH v2] iommu/amd: flush not present cache in iommu_map_page

2019-04-28 Thread Tom Murphy via iommu
check if there is a not-present cache present and flush it if there is.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/amd_iommu.c | 19 +++
 1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index f7cdd2ab7f11..ebd062522cf5 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -1307,6 +1307,16 @@ static void domain_flush_complete(struct 
protection_domain *domain)
}
 }
 
+/* Flush the not present cache if it exists */
+static void domain_flush_np_cache(struct protection_domain *domain,
+   dma_addr_t iova, size_t size)
+{
+   if (unlikely(amd_iommu_np_cache)) {
+   domain_flush_pages(domain, iova, size);
+   domain_flush_complete(domain);
+   }
+}
+
 
 /*
  * This function flushes the DTEs for all devices in domain
@@ -2435,10 +2445,7 @@ static dma_addr_t __map_single(struct device *dev,
}
address += offset;
 
-   if (unlikely(amd_iommu_np_cache)) {
-   domain_flush_pages(_dom->domain, address, size);
-   domain_flush_complete(_dom->domain);
-   }
+   domain_flush_np_cache(_dom->domain, address, size);
 
 out:
return address;
@@ -2617,6 +2624,8 @@ static int map_sg(struct device *dev, struct scatterlist 
*sglist,
s->dma_length   = s->length;
}
 
+   domain_flush_np_cache(domain, s->dma_address, s->dma_length);
+
return nelems;
 
 out_unmap:
@@ -3101,6 +3110,8 @@ static int amd_iommu_map(struct iommu_domain *dom, 
unsigned long iova,
ret = iommu_map_page(domain, iova, paddr, page_size, prot, GFP_KERNEL);
mutex_unlock(>api_lock);
 
+   domain_flush_np_cache(domain, iova, page_size);
+
return ret;
 }
 
-- 
2.17.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH v1] iommu/amd: flush not present cache in iommu_map_page

2019-04-27 Thread Tom Murphy via iommu
> The iommu_map_page function is called once per physical page that is
> mapped, so in the worst case for every 4k mapping established. So it is
> not the right place to put this check in.

Ah, you're right, that was careless of me.

> From a quick glance this check belongs into the map_sg() and the
> amd_iommu_map() function, but without the dom->updated check.
>
> Besides, to really support systems with np-cache in a way that doesn't
> destroy all performance, the driver also needs range-flushes for IOTLBs.

I am working on another patch to improve the intel iotlb flushing in
the iommu ops patch which should cover this too.

> Currently it can only flush a 4k page of the full address space of a
> domain. But that doesn't mean we shouldn't fix the missing flushes now.
>
> So please re-send the patch with the check at the two places I pointed
> out above.

will do
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH v2 3/7] iommu/vt-d: Expose ISA direct mapping region via iommu_get_resv_regions

2019-04-24 Thread Tom Murphy via iommu
I can see two potential problems with these patches that should be addressed:

The default domain of a group can be changed to type
IOMMU_DOMAIN_IDENTITY via the command line. With these patches we are
returning the si_domain for type IOMMU_DOMAIN_IDENTITY. There's a
chance the shared si_domain could be freed while it is still being
used when a group is freed. For example here in the
iommu_group_release:
https://github.com/torvalds/linux/blob/cd8dead0c39457e58ec1d36db93aedca811d48f1/drivers/iommu/iommu.c#L376
"if (group->default_domain)
iommu_domain_free(group->default_domain);"

Also now that a domain is attached to a device earlier we should
implement the is_attach_deferred call-back and use it to defer the
domain attach from iommu driver init to device driver init when iommu
is pre-enabled in kdump kernel. like in this commit:
https://github.com/torvalds/linux/commit/df3f7a6e8e855e4ff533508807cd7c3723faa51f


On Tue, Apr 16, 2019 at 3:24 AM Lu Baolu  wrote:
>
> Hi James,
>
> On 4/15/19 10:16 PM, James Sewart wrote:
> > Hey Lu,
> >
> >> On 10 Apr 2019, at 06:22, Lu Baolu  wrote:
> >>
> >> Hi James,
> >>
> >> On 4/6/19 2:02 AM, James Sewart wrote:
> >>> Hey Lu,
> >>> My bad, did some debugging on my end. The issue was swapping out
> >>> find_domain for iommu_get_domain_for_dev. It seems in some situations the
> >>> domain is not attached to the group but the device is expected to have the
> >>> domain still stored in its archdata.
> >>> I’ve attached the final patch with find_domain unremoved which seems to
> >>> work in my testing.
> >>
> >> Just looked into your v3 patch set and some thoughts from my end posted
> >> here just for your information.
> >>
> >> Let me post the problems we want to address.
> >>
> >> 1. When allocating a new group for a device, how should we determine the
> >> type of the default domain?
> >>
> >> 2. If we need to put a device into an existing group which uses a
> >> different type of domain from what the device desires to use, we might
> >> break the functionality of the device.
> >>
> >> My new thought is letting the iommu generic code to determine the
> >> default domain type (hence my proposed vendor specific default domain
> >> type patches could be dropped).
> >>
> >> If the default domain type is dynamical mapping, and later in 
> >> iommu_no_mapping(), we determines that we must use an identity domain,
> >> we then call iommu_request_dm_for_dev(dev).
> >>
> >> If the default domain type is identity mapping, and later in
> >> iommu_no_mapping(), we determined that we must use a dynamical domain,
> >> we then call iommu_request_dma_domain_for_dev(dev).
> >>
> >> We already have iommu_request_dm_for_dev() in iommu.c. We only need to
> >> implement iommu_request_dma_domain_for_dev().
> >>
> >> With this done, your patch titled "Create an IOMMU group for devices
> >> that require an identity map" could also be dropped.
> >>
> >> Any thoughts?
> >
> > Theres a couple issues I can think of. iommu_request_dm_for_dev changes
> > the domain for all devices within the devices group, if we may have
> > devices with different domain requirements in the same group then only the
> > last initialised device will get the domain it wants. If we want to add
> > iommu_request_dma_domain_for_dev then we would still need another IOMMU
> > group for identity domain devices.
>
> Current solution (a.k.a. v3) for this is to assign a new group to the
> device which requires an identity mapped domain. This will break the
> functionality of device direct pass-through (to user level). The iommu
> group represents the minimum granularity of iommu isolation and
> protection. The requirement of identity mapped domain should not be a
> factor for a new group.
>
> Both iomm_request_dma_domain_for_dev() or iommu_request_dm_for_dev()
> only support groups with a single device in it.
>
> The users could choose between domains of DMA type or IDENTITY type for
> a group. If multiple devices share a single group and both don't work
> for them, they have to disable the IOMMU. This isn't a valid
> configuration from IOMMU's point of view.
>
> >
> > Both with v3 and the proposed method here, iommu_should_identity_map is
> > determining whether the device requires an identity map. In v3 this is
> > called once up front by the generic code to determine for the IOMMU group
> > which domain type to use. In the proposed method I think this would happen
> > after initial domain allocation and would trigger the domain to be
> > replaced with a different domain.
> >
> > I prefer the solution in v3. What do you think?
>
> The only concern of solution in v3 from my point of view is some kind of
> duplication. Even we can ask the Intel IOMMU driver to tell the default
> domain type, we might change it after boot up. For example, for 32-bit
> pci devices, we don't know whether it's 64-bit or 32-bit capable during
> IOMMU initialization, so we might tell iommu.c to use identity mapped
> domain. After boot up, we check 

[PATCH v1] iommu/amd: flush not present cache in iommu_map_page

2019-04-24 Thread Tom Murphy via iommu
check if there is a not-present cache present and flush it if there is.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/amd_iommu.c | 5 +
 1 file changed, 5 insertions(+)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index f7cdd2ab7f11..91fe5cb10f50 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -1637,6 +1637,11 @@ static int iommu_map_page(struct protection_domain *dom,
 
update_domain(dom);
 
+   if (unlikely(amd_iommu_np_cache && !dom->updated)) {
+   domain_flush_pages(dom, bus_addr, page_size);
+   domain_flush_complete(dom);
+   }
+
/* Everything flushed out, free pages now */
free_page_list(freelist);
 
-- 
2.17.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH] iommu/amd: flush not present cache in iommu_map_page

2019-04-24 Thread Tom Murphy via iommu
On Wed, Apr 24, 2019 at 4:55 PM Joerg Roedel  wrote:
>
> On Wed, Apr 24, 2019 at 07:58:19AM -0700, Christoph Hellwig wrote:
> > I'd be tempted to do that.  But lets just ask Joerg if he has
> > any opinion..
>
> The reason was that it is an unlikely path, as hardware implementations
> are not allowed to set this bit. It is purely for emulated AMD IOMMUs.
> I have not measured whether this annotation has any performance
> benefit, but I find it more readable at least.

In that case I will keep it in.

>
> Regards,
>
> Joerg
>
> PS: Why did you drop me from the Cc list of the previous replies?
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH] iommu/amd: flush not present cache in iommu_map_page

2019-04-24 Thread Tom Murphy via iommu
>The two conditions can go into one if statement to make this a little
>more clear.
Ah, yeah of course

>And I'd really like to understand the unlikely - amd_iommu_np_cache
>is set based on a hardware capability, so it seems rather odd to mark
>it unlikely.  Dynamic branch prediction really should do the right thing
>here usually.

Here is the commit which added it without any explanation:
https://github.com/torvalds/linux/commit/270cab2426cdc6307725e4f1f46ecf8ab8e69193

should we remove it seen as there's no explanation given ?


On Wed, Apr 24, 2019 at 3:32 PM Christoph Hellwig  wrote:
>
> On Wed, Apr 24, 2019 at 03:18:59PM +0100, Tom Murphy via iommu wrote:
> > check if there is a not-present cache present and flush it if there is.
> >
> > Signed-off-by: Tom Murphy 
> > ---
> >  drivers/iommu/amd_iommu.c | 6 ++
> >  1 file changed, 6 insertions(+)
> >
> > diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
> > index f7cdd2ab7f11..8ef43224aae0 100644
> > --- a/drivers/iommu/amd_iommu.c
> > +++ b/drivers/iommu/amd_iommu.c
> > @@ -1636,6 +1636,12 @@ static int iommu_map_page(struct protection_domain 
> > *dom,
> >   pte[i] = __pte;
> >
> >   update_domain(dom);
> > + if (!dom->updated) {
> > + if (unlikely(amd_iommu_np_cache)) {
> > + domain_flush_pages(dom, bus_addr, page_size);
> > + domain_flush_complete(dom);
> > + }
> > + }
>
> The two conditions can go into one if statement to make this a little
> more clear.
>
> And I'd really like to understand the unlikely - amd_iommu_np_cache
> is set based on a hardware capability, so it seems rather odd to mark
> it unlikely.  Dynamic branch prediction really should do the right thing
> here usually.
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH] iommu/amd: flush not present cache in iommu_map_page

2019-04-24 Thread Tom Murphy via iommu
check if there is a not-present cache present and flush it if there is.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/amd_iommu.c | 6 ++
 1 file changed, 6 insertions(+)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index f7cdd2ab7f11..8ef43224aae0 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -1636,6 +1636,12 @@ static int iommu_map_page(struct protection_domain *dom,
pte[i] = __pte;
 
update_domain(dom);
+   if (!dom->updated) {
+   if (unlikely(amd_iommu_np_cache)) {
+   domain_flush_pages(dom, bus_addr, page_size);
+   domain_flush_complete(dom);
+   }
+   }
 
/* Everything flushed out, free pages now */
free_page_list(freelist);
-- 
2.17.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH] Remove old no iommu direct mapping code

2019-04-23 Thread Tom Murphy via iommu
These checks were intended to handle devices not mapped by the IOMMU.
Since the AMD IOMMU driver uses per-device dma_ops these functions can
no longer be called by direct mapped devices. So these checks aren't
needed anymore.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/amd_iommu.c | 10 ++
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index b319e51c379b..67cdc9e5304b 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -2503,9 +2503,7 @@ static dma_addr_t map_page(struct device *dev, struct 
page *page,
u64 dma_mask;
 
domain = get_domain(dev);
-   if (PTR_ERR(domain) == -EINVAL)
-   return (dma_addr_t)paddr;
-   else if (IS_ERR(domain))
+   if (IS_ERR(domain))
return DMA_MAPPING_ERROR;
 
dma_mask = *dev->dma_mask;
@@ -2676,11 +2674,7 @@ static void *alloc_coherent(struct device *dev, size_t 
size,
struct page *page;
 
domain = get_domain(dev);
-   if (PTR_ERR(domain) == -EINVAL) {
-   page = alloc_pages(flag, get_order(size));
-   *dma_addr = page_to_phys(page);
-   return page_address(page);
-   } else if (IS_ERR(domain))
+   if (IS_ERR(domain))
return NULL;
 
dma_dom   = to_dma_ops_domain(domain);
-- 
2.17.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH 2/9] iommu/dma-iommu: Add function to flush any cached not present IOTLB entries

2019-04-16 Thread Tom Murphy via iommu
>That said, I've now gone and looked and AFAICS both the Intel...
Ah, I missed that, you're right.

>...and AMD
It doesn't look like it. On AMD the cache is flushed during
iommu_ops::map only if the there are page table pages to free (if
we're allocating a large page and freeing the sub pages), right?
I guess this is a bug in the AMD iommu driver, as you said, it should
be handled in iommu_map(). I'll submit another patch to flush the np
cache on a call to amd iommu_ops::map if amd_iommu_np_cache is set.

>What might be
>worthwhile, though, is seeing if there's scope to refactor those drivers
>to push some of it into an iommu_ops::iotlb_sync_map callback to
>optimise the flushing for multi-page mappings.
I am working on a different patch series to improve the flushing and
page table page freeing for both amd and intel

On Tue, Apr 16, 2019 at 3:01 PM Robin Murphy  wrote:
>
> On 11/04/2019 19:47, Tom Murphy wrote:
> > Both the AMD and Intel drivers can cache not present IOTLB entries. To
> > convert these drivers to the dma-iommu api we need a generic way to
> > flush the NP cache. IOMMU drivers which have a NP cache can implement
> > the .flush_np_cache function in the iommu ops struct. I will implement
> > .flush_np_cache for both the Intel and AMD drivers in later patches.
> >
> > The Intel np-cache is described here:
> > https://software.intel.com/sites/default/files/managed/c5/15/vt-directed-io-spec.pdf#G7.66452
> >
> > And the AMD np-cache is described here:
> > https://developer.amd.com/wordpress/media/2012/10/34434-IOMMU-Rev_1.26_2-11-09.pdf#page=63
>
> Callers expect that once iommu_map() returns successfully, the mapping
> exists and is ready to use - if these drivers aren't handling this
> flushing internally, how are they not already broken for e.g. VFIO?
>
> > Signed-off-by: Tom Murphy 
> > ---
> >   drivers/iommu/dma-iommu.c | 10 ++
> >   include/linux/iommu.h |  3 +++
> >   2 files changed, 13 insertions(+)
> >
> > diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
> > index 1a4bff3f8427..cc5da30d6e58 100644
> > --- a/drivers/iommu/dma-iommu.c
> > +++ b/drivers/iommu/dma-iommu.c
> > @@ -594,6 +594,9 @@ struct page **iommu_dma_alloc(struct device *dev, 
> > size_t size, gfp_t gfp,
> >   < size)
> >   goto out_free_sg;
> >
> > + if (domain->ops->flush_np_cache)
> > + domain->ops->flush_np_cache(domain, iova, size);
> > +
>
> This doesn't scale. At the very least, it should be internal to
> iommu_map() and exposed to be the responsibility of every external
> caller now and forever after.
>
> That said, I've now gone and looked and AFAICS both the Intel and AMD
> drivers *do* appear to handle this in their iommu_ops::map callbacks
> already, so the whole patch does indeed seem bogus. What might be
> worthwhile, though, is seeing if there's scope to refactor those drivers
> to push some of it into an iommu_ops::iotlb_sync_map callback to
> optimise the flushing for multi-page mappings.
>
> Robin.
>
> >   *handle = iova;
> >   sg_free_table();
> >   return pages;
> > @@ -652,6 +655,10 @@ static dma_addr_t __iommu_dma_map(struct device *dev, 
> > phys_addr_t phys,
> >   iommu_dma_free_iova(cookie, iova, size);
> >   return DMA_MAPPING_ERROR;
> >   }
> > +
> > + if (domain->ops->flush_np_cache)
> > + domain->ops->flush_np_cache(domain, iova, size);
> > +
> >   return iova + iova_off;
> >   }
> >
> > @@ -812,6 +819,9 @@ int iommu_dma_map_sg(struct device *dev, struct 
> > scatterlist *sg,
> >   if (iommu_map_sg_atomic(domain, iova, sg, nents, prot) < iova_len)
> >   goto out_free_iova;
> >
> > + if (domain->ops->flush_np_cache)
> > + domain->ops->flush_np_cache(domain, iova, iova_len);
> > +
> >   return __finalise_sg(dev, sg, nents, iova);
> >
> >   out_free_iova:
> > diff --git a/include/linux/iommu.h b/include/linux/iommu.h
> > index 75559918d9bd..47ff8d731d6a 100644
> > --- a/include/linux/iommu.h
> > +++ b/include/linux/iommu.h
> > @@ -173,6 +173,7 @@ struct iommu_resv_region {
> >* @iotlb_sync_map: Sync mappings created recently using @map to the 
> > hardware
> >* @iotlb_sync: Flush all queued ranges from the hardware TLBs and empty 
> > flush
> >*queue
> > + * @flush_np_cache: Flush the non present entry cache
> >* @iova_to_phys: translate iova to physical address
> >

Re: [PATCH 3/9] iommu/dma-iommu: Add iommu_dma_copy_reserved_iova, iommu_dma_apply_resv_region to the dma-iommu api

2019-04-16 Thread Tom Murphy via iommu
I hoped this could be an exception, it's easier to grok without the
line break and isn't crazy long. Because you mentioned it I'll fix it.

On Mon, Apr 15, 2019 at 7:31 AM Christoph Hellwig  wrote:
>
> On Thu, Apr 11, 2019 at 07:47:32PM +0100, Tom Murphy via iommu wrote:
> > +
> > + WARN_ON_ONCE(iommu_dma_reserve_iova(domain, region->start, end) == 
> > NULL);
>
> Overly long line..
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH 5/9] iommu/amd: Implement .flush_np_cache

2019-04-15 Thread Tom Murphy via iommu
This is a cut and paste from the current amd_iommu driver. I really
have no idea if it's a good idea or not. It looks like
joerg.roe...@amd.com might be the person to ask.

@Joerg Roedel should we keep this?

On Mon, Apr 15, 2019 at 7:33 AM Christoph Hellwig  wrote:
>
> > +static void amd_iommu_flush_np_cache(struct iommu_domain *domain,
> > + unsigned long iova, size_t size)
> > +{
> > + struct protection_domain *dom = to_pdomain(domain);
> > +
> > + if (unlikely(amd_iommu_np_cache)) {
>
> Is this case really so unlikely that it needs a static branch prediction
> hint?
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH] iommu/amd: Add allocated domain to global list earlier

2019-04-15 Thread Tom Murphy via iommu
dma_ops_domain_free() expects domain to be in a global list.
Arguably, could be called before protection_domain_init().

Signed-off-by: Dmitry Safonov 
Signed-off-by: Tom Murphy 
---
 drivers/iommu/amd_iommu.c | 10 ++
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 02b351834a3b..bfb021d656aa 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -1801,8 +1801,12 @@ static struct protection_domain 
*dma_ops_domain_alloc(void)
if (!domain)
return NULL;
 
-   if (protection_domain_init(domain))
-   goto free_domain;
+   if (protection_domain_init(domain)) {
+   kfree(domain);
+   return NULL;
+   }
+
+   add_domain_to_list(domain);
 
domain->mode = PAGE_MODE_3_LEVEL;
domain->pt_root = (void *)get_zeroed_page(GFP_KERNEL);
@@ -1824,8 +1828,6 @@ static struct protection_domain 
*dma_ops_domain_alloc(void)
/* Initialize reserved ranges */
iommu_dma_copy_reserved_iova(_iova_ranges, >domain);
 
-   add_domain_to_list(domain);
-
return domain;
 
 free_cookie:
-- 
2.17.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH 9/9] iommu/amd: Add allocated domain to global list earlier

2019-04-15 Thread Tom Murphy via iommu
>This seems like a fix to the existing code and should probably go out first.

I'll send this patch out on it's own now.

On Mon, Apr 15, 2019 at 7:23 AM Christoph Hellwig  wrote:
>
> On Thu, Apr 11, 2019 at 07:47:38PM +0100, Tom Murphy via iommu wrote:
> > dma_ops_domain_free() expects domain to be in a global list.
> > Arguably, could be called before protection_domain_init().
> >
> > Signed-off-by: Dmitry Safonov 
> > Signed-off-by: Tom Murphy 
>
> This seems like a fix to the existing code and should probably go
> out first.
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 8/9] iommu/amd: Clean up unused functions

2019-04-11 Thread Tom Murphy via iommu
Now that we are using the dma-iommu api we have a lot of unused code.
This patch removes all that unused code.

Signed-off-by: Tom Murphy 
---
 drivers/iommu/amd_iommu.c | 209 --
 1 file changed, 209 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 218faf3a6d9c..02b351834a3b 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -116,18 +116,6 @@ struct kmem_cache *amd_iommu_irq_cache;
 static void update_domain(struct protection_domain *domain);
 static int protection_domain_init(struct protection_domain *domain);
 static void detach_device(struct device *dev);
-static void iova_domain_flush_tlb(struct iova_domain *iovad);
-
-/*
- * Data container for a dma_ops specific protection domain
- */
-struct dma_ops_domain {
-   /* generic protection domain information */
-   struct protection_domain domain;
-
-   /* IOVA RB-Tree */
-   struct iova_domain iovad;
-};
 
 static struct iova_domain reserved_iova_ranges;
 static struct lock_class_key reserved_rbtree_key;
@@ -201,12 +189,6 @@ static struct protection_domain *to_pdomain(struct 
iommu_domain *dom)
return container_of(dom, struct protection_domain, domain);
 }
 
-static struct dma_ops_domain* to_dma_ops_domain(struct protection_domain 
*domain)
-{
-   BUG_ON(domain->flags != PD_DMA_OPS_MASK);
-   return container_of(domain, struct dma_ops_domain, domain);
-}
-
 static struct iommu_dev_data *alloc_dev_data(u16 devid)
 {
struct iommu_dev_data *dev_data;
@@ -1280,12 +1262,6 @@ static void domain_flush_pages(struct protection_domain 
*domain,
__domain_flush_pages(domain, address, size, 0);
 }
 
-/* Flush the whole IO/TLB for a given protection domain */
-static void domain_flush_tlb(struct protection_domain *domain)
-{
-   __domain_flush_pages(domain, 0, CMD_INV_IOMMU_ALL_PAGES_ADDRESS, 0);
-}
-
 /* Flush the whole IO/TLB for a given protection domain - including PDE */
 static void domain_flush_tlb_pde(struct protection_domain *domain)
 {
@@ -1689,43 +1665,6 @@ static unsigned long iommu_unmap_page(struct 
protection_domain *dom,
return unmapped;
 }
 
-/
- *
- * The next functions belong to the address allocator for the dma_ops
- * interface functions.
- *
- /
-
-
-static unsigned long dma_ops_alloc_iova(struct device *dev,
-   struct dma_ops_domain *dma_dom,
-   unsigned int pages, u64 dma_mask)
-{
-   unsigned long pfn = 0;
-
-   pages = __roundup_pow_of_two(pages);
-
-   if (dma_mask > DMA_BIT_MASK(32))
-   pfn = alloc_iova_fast(_dom->iovad, pages,
- IOVA_PFN(DMA_BIT_MASK(32)), false);
-
-   if (!pfn)
-   pfn = alloc_iova_fast(_dom->iovad, pages,
- IOVA_PFN(dma_mask), true);
-
-   return (pfn << PAGE_SHIFT);
-}
-
-static void dma_ops_free_iova(struct dma_ops_domain *dma_dom,
- unsigned long address,
- unsigned int pages)
-{
-   pages = __roundup_pow_of_two(pages);
-   address >>= PAGE_SHIFT;
-
-   free_iova_fast(_dom->iovad, address, pages);
-}
-
 /
  *
  * The next functions belong to the domain allocation. A domain is
@@ -1827,21 +1766,6 @@ static void free_gcr3_table(struct protection_domain 
*domain)
free_page((unsigned long)domain->gcr3_tbl);
 }
 
-static void dma_ops_domain_flush_tlb(struct dma_ops_domain *dom)
-{
-   domain_flush_tlb(>domain);
-   domain_flush_complete(>domain);
-}
-
-static void iova_domain_flush_tlb(struct iova_domain *iovad)
-{
-   struct dma_ops_domain *dom;
-
-   dom = container_of(iovad, struct dma_ops_domain, iovad);
-
-   dma_ops_domain_flush_tlb(dom);
-}
-
 /*
  * Free a domain, only used if something went wrong in the
  * allocation path and we need to free an already allocated page table
@@ -2437,100 +2361,6 @@ static int dir2prot(enum dma_data_direction direction)
return 0;
 }
 
-/*
- * This function contains common code for mapping of a physically
- * contiguous memory region into DMA address space. It is used by all
- * mapping functions provided with this IOMMU driver.
- * Must be called with the domain lock held.
- */
-static dma_addr_t __map_single(struct device *dev,
-  struct dma_ops_domain *dma_dom,
-  phys_addr_t paddr,
-  size_t size,
-  enum dma_data_direction direction,
-  u64 dma_mask)
-{
-   dma_addr_t offset = paddr & ~PAGE_MASK;
-   dma_addr_t address

  1   2   >