[PATCH/RFC] powerpc: prevent memory corruption due to cache invalidation of unaligned DMA buffer

2008-06-26 Thread Andrew Lewis
On PowerPC processors with non-coherent cache architectures the DMA
subsystem calls invalidate_dcache_range() before performing a DMA read
operation.  If the address and length of the DMA buffer are not aligned
to a cache-line boundary this can result in memory outside of the DMA
buffer being invalidated in the cache.  If this memory has an
uncommitted store then the data will be lost and a subsequent read of
that address will result in an old value being returned from main memory.

Only when the DMA buffer starts on a cache-line boundary and is an exact
mutiple of the cache-line size can invalidate_dcache_range() be called,
otherwise flush_dcache_range() must be called.  flush_dcache_range()
will first flush uncommitted writes, and then invalidate the cache.

Signed-off-by: Andrew Lewis andrew-lewis at netspace.net.au
---
This problem was originally observed using SLUB on the ADS5121
development kit for the Freescale MPC5121e SoC.  When booting with a USB
flash disk connected there was a highly intermittent kernel panic where
an attempt was made to dereference an address containing 0x6b6b6b6b, the
SLUB poison marker.

Tracing the origin of the corruption was difficult as modifying the
kernel would often result in the corruption vanishing.  Eventually it
was determined that the SCSI request pointer us-srb-request was being
corrupted from a valid value to 0x6b6b6b6b somewhere in the call to
us-proto_handler(us-srb, us) in usb_stor_control_thread()
(drivers/usb/storage/usb.c).

Further tracing revealed the corruption to be occuring when
invalidate_dcache_range() was called.

The address of us-srb-request was 0xCFAC81EC, while the parameters
passed to invalidate_dcache_range() were start=0xCFAC81F0 and
end=0xCFAC8202.

As these addresses are not on a cache-line boundary,
invalidate_dcache_range() actually invalidates from 0xCFAC81E0 to
0xCFAC82E0.  This results in an uncommitted store at address 0xCFAC81EC
being discarded to be replaced with 0x6b6b6b6b from the SLUB poisoning
which had been committed to main memory.

Applying this patch also corrected random crashes observed when
connecting and disconnecting a ZD1121 Wireless LAN USB adapter.

 dma-noncoherent.c |   11 +--
 1 files changed, 9 insertions(+), 2 deletions(-)

diff -upNr linux-2.6.24.6/arch/powerpc/lib/dma-noncoherent.c 
wk-linux-2.6.24.6/arch/powerpc/lib/dma-noncoherent.c
--- linux-2.6.24.6/arch/powerpc/lib/dma-noncoherent.c   2008-05-02 
05:50:00.0 +0800
+++ wk-linux-2.6.24.6/arch/powerpc/lib/dma-noncoherent.c2008-06-25 
15:11:25.0 +0800
@@ -348,8 +348,15 @@ void __dma_sync(void *vaddr, size_t size
switch (direction) {
case DMA_NONE:
BUG();
-   case DMA_FROM_DEVICE:   /* invalidate only */
-   invalidate_dcache_range(start, end);
+   case DMA_FROM_DEVICE:   
+   /*
+* invalidate only when cache-line aligned otherwise there is
+* the potential for discarding uncommitted data from the cache
+*/
+   if ((start  (L1_CACHE_BYTES - 1)) || (size  (L1_CACHE_BYTES - 
1)))
+   flush_dcache_range(start, end);
+   else
+   invalidate_dcache_range(start, end);
break;
case DMA_TO_DEVICE: /* writeback only */
clean_dcache_range(start, end);

___
Linuxppc-dev mailing list
Linuxppc-dev@ozlabs.org
https://ozlabs.org/mailman/listinfo/linuxppc-dev


Re: [PATCH/RFC] powerpc: prevent memory corruption due to cache invalidation of unaligned DMA buffer

2008-06-26 Thread Gerhard Pircher

 Original-Nachricht 
 Datum: Thu, 26 Jun 2008 17:29:05 +0800
 Von: Andrew Lewis [EMAIL PROTECTED]
 An: linuxppc-dev@ozlabs.org
 CC: \'Andrew Lewis\' [EMAIL PROTECTED], [EMAIL PROTECTED]
 Betreff: [PATCH/RFC] powerpc: prevent memory corruption due to cache  
 invalidation of unaligned DMA buffer

 On PowerPC processors with non-coherent cache architectures the DMA
 subsystem calls invalidate_dcache_range() before performing a DMA read
 operation.  If the address and length of the DMA buffer are not aligned
 to a cache-line boundary this can result in memory outside of the DMA
 buffer being invalidated in the cache.  If this memory has an
 uncommitted store then the data will be lost and a subsequent read of
 that address will result in an old value being returned from main memory.
 
 Only when the DMA buffer starts on a cache-line boundary and is an exact
 mutiple of the cache-line size can invalidate_dcache_range() be called,
 otherwise flush_dcache_range() must be called.  flush_dcache_range()
 will first flush uncommitted writes, and then invalidate the cache.
I have a similar problem, but with a network driver (which uses cacheable
skbuffers as DMA memory). The SLUB allocator should return cache line
aligned memory, but that doesn't prevent data corruption. I fixed it by
setting NET_SKB_PAD (default 16)  to L1_CACHE_BYTES (32 on my 7455
non coherent DMA system).
It looks like your patch could solve my problem without changing
NET_SKB_PAD. I'll have to try that out.

regards,

Gerhard
-- 
Ist Ihr Browser Vista-kompatibel? Jetzt die neuesten 
Browser-Versionen downloaden: http://www.gmx.net/de/go/browser
___
Linuxppc-dev mailing list
Linuxppc-dev@ozlabs.org
https://ozlabs.org/mailman/listinfo/linuxppc-dev


Re: [PATCH/RFC] powerpc: prevent memory corruption due to cache invalidation of unaligned DMA buffer

2008-06-26 Thread Benjamin Herrenschmidt
On Thu, 2008-06-26 at 17:29 +0800, Andrew Lewis wrote:
 On PowerPC processors with non-coherent cache architectures the DMA
 subsystem calls invalidate_dcache_range() before performing a DMA read
 operation.  If the address and length of the DMA buffer are not aligned
 to a cache-line boundary this can result in memory outside of the DMA
 buffer being invalidated in the cache.  If this memory has an
 uncommitted store then the data will be lost and a subsequent read of
 that address will result in an old value being returned from main memory.

Thanks for providing a fix. The problem is old and in fact was discussed
a while ago, unfortunately, it looks like the SCSI folks didn't fix it
yet.

In theory, on non-cache coherent architecture, buffers that can be
DMA'ed should not share cache lines with other objects. This is violated
by the SCSI code due to recent changes to the way the sense buffer is
allocated. Various solutions were proposed, including a __dma_buffer
alignment directive, but back then, the wind blew more toward changing
the way the buffer is allocated in the SCSI stack.

Some people started working on that and I lost track. It looks like it
wasn't properly fixed :-(

I'll ack your patch for now as workaround, though it would be good to
have some kind of debug option to make it WARN_ON when the unaligned
case happen, to track them down and help fix them. I'll try to catch up
with the SCSI folks to see what's up on this side.

In the meantime:

Acked-by: Benjamin Herrenschmidt [EMAIL PROTECTED]


___
Linuxppc-dev mailing list
Linuxppc-dev@ozlabs.org
https://ozlabs.org/mailman/listinfo/linuxppc-dev