Thread (45 messages) 45 messages, 7 authors, 2015-02-05

[PATCH v4 3/6] of: fix size when dma-range is not used

From: catalin.marinas@arm.com (Catalin Marinas)
Date: 2015-01-29 01:43:14
Also in: linux-devicetree, linux-iommu, linux-pci, lkml

On Wed, Jan 28, 2015 at 03:45:19PM +0000, Rob Herring wrote:
On Wed, Jan 28, 2015 at 5:05 AM, Catalin Marinas
[off-list ref] wrote:
quoted
On Tue, Jan 27, 2015 at 06:55:15PM +0000, Murali Karicheri wrote:
quoted
How about having the logic like this?

      ret = of_dma_get_range(np, &dma_addr, &paddr, &size);
      if (ret < 0) {
              dma_addr = offset = 0;
              size = dev->coherent_dma_mask + 1;
      } else {
              offset = PFN_DOWN(paddr - dma_addr);
              dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", offset);
      }

      if (is_power_of_2(size + 1))
              size = size + 1;
      else if (!is_power_of_2(size))
      {
              dev_err(dev, "invalid size\n");
              return;
      }
In of_dma_configure(), we currently assume that the default coherent
mask is 32-bit. In this thread:

http://article.gmane.org/gmane.linux.kernel/1835096

we talked about setting the coherent mask based on size automatically.
I'm not sure about the size but I think we can assume is 32-bit mask + 1
if it is not specified in the DT. Instead of just assuming a default
mask, let's assume a default size and create the mask based on this
(untested):
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 5b33c6a21807..9ff8d1286b44 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -170,10 +170,10 @@ static void of_dma_configure(struct device *dev)
        struct iommu_ops *iommu;

        /*
-        * Set default dma-mask to 32 bit. Drivers are expected to setup
-        * the correct supported dma_mask.
+        * Set default size to cover the 32-bit. Drivers are expected to setup
+        * the correct size and dma_mask.
         */
-       dev->coherent_dma_mask = DMA_BIT_MASK(32);
+       size = 1ULL << 32;

        /*
         * Set it to coherent_dma_mask by default if the architecture
@@ -185,13 +185,24 @@ static void of_dma_configure(struct device *dev)
        ret = of_dma_get_range(dev->of_node, &dma_addr, &paddr, &size);
        if (ret < 0) {
                dma_addr = offset = 0;
-               size = dev->coherent_dma_mask;
Are we assuming dma_addr, paddr and size are not touched on error? If
so, we can get rid of this clause entirely.
We can if we initialise dma_addr and offset to 0 when declared in this
function. The dma_addr and size variables are later passed to the
arch_setup_dma_ops(), so they need to have some sane values independent
of the presence of dma-ranges in the DT.
quoted
        } else {
                offset = PFN_DOWN(paddr - dma_addr);
                dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", dev->dma_pfn_offset);
        }
        dev->dma_pfn_offset = offset;

+       /*
+        * Workaround for DTs setting the size to a mask or 0.
+        */
+       if (is_power_of_2(size + 1))
+               size += 1;
As I mentioned, I think power of 2 is too restrictive (from a DT
perspective even though the kernel may require a power of 2 here for
the mask). Just checking bit0 set should be enough.
The power of 2 was mainly to cover the case where the size is wrongly
written as a mask in the DT. If the size is deliberately not a power of
two and not a full mask, the above check won't change it. With my
proposal, ilog2 gets rid of extra bits in size, only that if the size
was a mask because of DT error, we lose a bit in the coherent_dma_mask.
Also, we need a WARN here so DTs get fixed.
I agree.

-- 
Catalin
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help