Commit 4dcfa60071b3d23f0181f27d8519f12e37cefbb9

Authored by Russell King
1 parent 42536b9f9c

ARM: DMA-API: better handing of DMA masks for coherent allocations

We need to start treating DMA masks as something which is specific to
the bus that the device resides on, otherwise we're going to hit all
sorts of nasty issues with LPAE and 32-bit DMA controllers in >32-bit
systems, where memory is offset from PFN 0.

In order to start doing this, we convert the DMA mask to a PFN using
the device specific dma_to_pfn() macro.  This is the reverse of the
pfn_to_dma() macro which is used to get the DMA address for the device.

This gives us a PFN mask, which we can then check against the PFN
limit of the DMA zone.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Showing 3 changed files with 49 additions and 6 deletions Side-by-side Diff

arch/arm/mm/dma-mapping.c
... ... @@ -159,7 +159,7 @@
159 159  
160 160 static u64 get_coherent_dma_mask(struct device *dev)
161 161 {
162   - u64 mask = (u64)arm_dma_limit;
  162 + u64 mask = (u64)DMA_BIT_MASK(32);
163 163  
164 164 if (dev) {
165 165 mask = dev->coherent_dma_mask;
166 166  
... ... @@ -173,12 +173,32 @@
173 173 return 0;
174 174 }
175 175  
176   - if ((~mask) & (u64)arm_dma_limit) {
177   - dev_warn(dev, "coherent DMA mask %#llx is smaller "
178   - "than system GFP_DMA mask %#llx\n",
179   - mask, (u64)arm_dma_limit);
  176 + /*
  177 + * If the mask allows for more memory than we can address,
  178 + * and we actually have that much memory, then fail the
  179 + * allocation.
  180 + */
  181 + if (sizeof(mask) != sizeof(dma_addr_t) &&
  182 + mask > (dma_addr_t)~0 &&
  183 + dma_to_pfn(dev, ~0) > arm_dma_pfn_limit) {
  184 + dev_warn(dev, "Coherent DMA mask %#llx is larger than dma_addr_t allows\n",
  185 + mask);
  186 + dev_warn(dev, "Driver did not use or check the return value from dma_set_coherent_mask()?\n");
180 187 return 0;
181 188 }
  189 +
  190 + /*
  191 + * Now check that the mask, when translated to a PFN,
  192 + * fits within the allowable addresses which we can
  193 + * allocate.
  194 + */
  195 + if (dma_to_pfn(dev, mask) < arm_dma_pfn_limit) {
  196 + dev_warn(dev, "Coherent DMA mask %#llx (pfn %#lx-%#lx) covers a smaller range of system memory than the DMA zone pfn 0x0-%#lx\n",
  197 + mask,
  198 + dma_to_pfn(dev, 0), dma_to_pfn(dev, mask) + 1,
  199 + arm_dma_pfn_limit + 1);
  200 + return 0;
  201 + }
182 202 }
183 203  
184 204 return mask;
185 205  
... ... @@ -1007,8 +1027,27 @@
1007 1027 */
1008 1028 int dma_supported(struct device *dev, u64 mask)
1009 1029 {
1010   - if (mask < (u64)arm_dma_limit)
  1030 + unsigned long limit;
  1031 +
  1032 + /*
  1033 + * If the mask allows for more memory than we can address,
  1034 + * and we actually have that much memory, then we must
  1035 + * indicate that DMA to this device is not supported.
  1036 + */
  1037 + if (sizeof(mask) != sizeof(dma_addr_t) &&
  1038 + mask > (dma_addr_t)~0 &&
  1039 + dma_to_pfn(dev, ~0) > arm_dma_pfn_limit)
1011 1040 return 0;
  1041 +
  1042 + /*
  1043 + * Translate the device's DMA mask to a PFN limit. This
  1044 + * PFN number includes the page which we can DMA to.
  1045 + */
  1046 + limit = dma_to_pfn(dev, mask);
  1047 +
  1048 + if (limit < arm_dma_pfn_limit)
  1049 + return 0;
  1050 +
1012 1051 return 1;
1013 1052 }
1014 1053 EXPORT_SYMBOL(dma_supported);
... ... @@ -218,6 +218,7 @@
218 218 * so a successful GFP_DMA allocation will always satisfy this.
219 219 */
220 220 phys_addr_t arm_dma_limit;
  221 +unsigned long arm_dma_pfn_limit;
221 222  
222 223 static void __init arm_adjust_dma_zone(unsigned long *size, unsigned long *hole,
223 224 unsigned long dma_size)
... ... @@ -240,6 +241,7 @@
240 241 arm_dma_limit = PHYS_OFFSET + arm_dma_zone_size - 1;
241 242 } else
242 243 arm_dma_limit = 0xffffffff;
  244 + arm_dma_pfn_limit = arm_dma_limit >> PAGE_SHIFT;
243 245 #endif
244 246 }
245 247  
... ... @@ -81,8 +81,10 @@
81 81  
82 82 #ifdef CONFIG_ZONE_DMA
83 83 extern phys_addr_t arm_dma_limit;
  84 +extern unsigned long arm_dma_pfn_limit;
84 85 #else
85 86 #define arm_dma_limit ((phys_addr_t)~0)
  87 +#define arm_dma_pfn_limit (~0ul >> PAGE_SHIFT)
86 88 #endif
87 89  
88 90 extern phys_addr_t arm_lowmem_limit;