[PATCH v11 07/11] device-mapping: Introduce DMA range map, supplanting dma_pfn_offset

Nicolas Saenz Julienne nsaenzjulienne at suse.de
Mon Sep 7 18:19:43 UTC 2020


On Mon, 2020-09-07 at 13:40 -0400, Jim Quinlan wrote:
> On Mon, Sep 7, 2020 at 11:01 AM Nicolas Saenz Julienne
> <nsaenzjulienne at suse.de> wrote:
> > > 
> > > Hi Nicolas,
> > > 
> > > Can you please help us out here?  It appears that your commit
> > 
> > It's dma_offset_from_dma_addr() that's causing trouble. It goes over all the
> > dma regions and, if no region matches the phys address range, it returns 0.
> > This isn't acceptable as is. In the past, we used to pass the offset
> > nonetheless, which would make the phys address grow out of the dma memory area
> > and fail the dma_capable() test.
> > 
> > For example, RPi4 devices attached to the old interconnect see phys [0x0
> > 0x3fffffff] at [0xc0000000 0xffffffff]. So say you're trying to convert
> > physical address 0xfa000000, you'll get 0 from dma_offset_from_phys_addr()
> > (since your dma range only covers the first GB) to then test if 0xfa000000 is
> > dma_capable(), which it is, but for the wrong reasons. Causing DMA issues
> > further down the line.
> > 
> > I don't have a proper suggestion on how to solve this but here's the solution I
> > used:
> > 
> > diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h
> > index 4c4646761afe..40fe3c97f2be 100644
> > --- a/include/linux/dma-mapping.h
> > +++ b/include/linux/dma-mapping.h
> > @@ -217,11 +217,19 @@ static inline u64 dma_offset_from_phys_addr(struct device *dev,
> >  {
> >         const struct bus_dma_region *m = dev->dma_range_map;
> > 
> > -       if (m)
> > +       if (m) {
> >                 for (; m->size; m++)
> >                         if (paddr >= m->cpu_start &&
> >                             paddr - m->cpu_start < m->size)
> >                                 return m->offset;
> > +
> > +               /*
> > +                * The physical address doesn't fit any of the DMA regions,
> > +                * return an impossible to fulfill offset.
> > +                */
> > +               return -(1ULL << 46);
> > +       }
> > +
> >         return 0;
> >  }
> Hi Nicolas,
> 
> Thanks for looking into this.  The concern I have with your solution
> is that returning an arbitrarily large offset might overlap with an
> improbable but valid usage.  AFAIK there is nothing that disallows
> mapping a device to anywhere within the 64bit range of PCIe space,
> including up to say 0xffffffffffffffff.

Indeed, that's why I wasn't all that happy with my solution.

As an alternative, how about returning '-dev->bus_dma_limit' instead of 0? It's
always 0 for the devices without bus_dma_regions, and, I think, an always
unattainable offset for devices that do (I tested it on RPi4 with the 30bit
limitd mmc controller and it seems to work alright).

--- a/include/linux/dma-mapping.h
+++ b/include/linux/dma-mapping.h
@@ -222,7 +222,8 @@ static inline u64 dma_offset_from_phys_addr(struct device *dev,
                        if (paddr >= m->cpu_start &&
                            paddr - m->cpu_start < m->size)
                                return m->offset;
-       return 0;
+
+       return -dev->bus_dma_limit;
 }

> As an alternative perhaps changing dma_capable() so that if the
> dma_range_map is present then it validates that both ends of the
> prospective DMA region get "hits" on one of the offset regions in the
> map.  Christoph, if you are okay with this I can quickly post a patch.

IIUC, by the time you enter dma_capable() you already converted the physical
address to DMA address and the damage is done.

Regards,
Nicolas

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 484 bytes
Desc: This is a digitally signed message part
URL: <https://lists.freedesktop.org/archives/dri-devel/attachments/20200907/f406aac5/attachment.sig>


More information about the dri-devel mailing list