Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

MIPS: Add SYNC after cacheflush

On processors with deep write buffers, it is likely that many cycles
will pass between a CACHE instruction and the time the data actually
gets written out to DRAM. Add a SYNC instruction to ensure that the
buffers get emptied before the flush functions return.

Actual problem seen in the wild:

1) dma_alloc_coherent() allocates cached memory

2) memset() is called to clear the new pages

3) dma_cache_wback_inv() is called to flush the zero data out to memory

4) dma_alloc_coherent() returns an uncached (kseg1) pointer to the
freshly allocated pages

5) Caller writes data through the kseg1 pointer

6) Buffered writeback data finally gets flushed out to DRAM

7) Part of caller's data is inexplicably zeroed out

This patch adds SYNC between steps 3 and 4, which fixed the problem.

Signed-off-by: Kevin Cernekee <cernekee@gmail.com>
Cc: linux-mips@linux-mips.org
Cc: linux-kernel@vger.kernel.org
Patchwork:
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>

authored by

Kevin Cernekee and committed by
Ralf Baechle
d0023c4a b6da0ffb

+4
+4
arch/mips/mm/c-r4k.c
··· 604 604 r4k_blast_scache(); 605 605 else 606 606 blast_scache_range(addr, addr + size); 607 + __sync(); 607 608 return; 608 609 } 609 610 ··· 621 620 } 622 621 623 622 bc_wback_inv(addr, size); 623 + __sync(); 624 624 } 625 625 626 626 static void r4k_dma_cache_inv(unsigned long addr, unsigned long size) ··· 649 647 (addr + size - 1) & almask); 650 648 blast_inv_scache_range(addr, addr + size); 651 649 } 650 + __sync(); 652 651 return; 653 652 } 654 653 ··· 666 663 } 667 664 668 665 bc_inv(addr, size); 666 + __sync(); 669 667 } 670 668 #endif /* CONFIG_DMA_NONCOHERENT */ 671 669