dma-mapping: relax warning for per-device areas
authorRobin Murphy <robin.murphy@arm.com>
Mon, 23 Jul 2018 21:42:48 +0000 (22:42 +0100)
committerChristoph Hellwig <hch@lst.de>
Wed, 25 Jul 2018 11:32:58 +0000 (13:32 +0200)
The reasons why dma_free_attrs() should not be called from IRQ context
are not necessarily obvious and somewhat buried in the development
history, so let's start by documenting the warning itself to help anyone
who does happen to hit it and wonder what the deal is.

However, this check turns out to be slightly over-restrictive for the
way that per-device memory has been spliced into the general API, since
for that case we know that dma_declare_coherent_memory() has created an
appropriate CPU mapping for the entire area and nothing dynamic should
be happening. Given that the usage model for per-device memory is often
more akin to streaming DMA than 'real' coherent DMA (e.g. allocating and
freeing space to copy short-lived packets in and out), it is also
somewhat more reasonable for those operations to happen in IRQ handlers
for such devices.

Therefore, let's move the irqs_disabled() check down past the per-device
area hook, so that that gets a chance to resolve the request before we
reach definite "you're doing it wrong" territory.

Reported-by: Fredrik Noring <noring@nocrew.org>
Tested-by: Fredrik Noring <noring@nocrew.org>
Signed-off-by: Robin Murphy <robin.murphy@arm.com>
Signed-off-by: Christoph Hellwig <hch@lst.de>
include/linux/dma-mapping.h

index f9cc309..1db6a6b 100644 (file)
@@ -538,10 +538,17 @@ static inline void dma_free_attrs(struct device *dev, size_t size,
        const struct dma_map_ops *ops = get_dma_ops(dev);
 
        BUG_ON(!ops);
-       WARN_ON(irqs_disabled());
 
        if (dma_release_from_dev_coherent(dev, get_order(size), cpu_addr))
                return;
+       /*
+        * On non-coherent platforms which implement DMA-coherent buffers via
+        * non-cacheable remaps, ops->free() may call vunmap(). Thus getting
+        * this far in IRQ context is a) at risk of a BUG_ON() or trying to
+        * sleep on some machines, and b) an indication that the driver is
+        * probably misusing the coherent API anyway.
+        */
+       WARN_ON(irqs_disabled());
 
        if (!ops->free || !cpu_addr)
                return;