aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xtarget/linux/generic/pending-4.14/103-MIPS-c-r4k-fix-data-corruption-related-to-cache-coherence.patch92
1 files changed, 92 insertions, 0 deletions
diff --git a/target/linux/generic/pending-4.14/103-MIPS-c-r4k-fix-data-corruption-related-to-cache-coherence.patch b/target/linux/generic/pending-4.14/103-MIPS-c-r4k-fix-data-corruption-related-to-cache-coherence.patch
new file mode 100755
index 0000000000..3cfbd2ce91
--- /dev/null
+++ b/target/linux/generic/pending-4.14/103-MIPS-c-r4k-fix-data-corruption-related-to-cache-coherence.patch
@@ -0,0 +1,92 @@
+From patchwork Thu Apr 26 23:28:34 2018
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v2] MIPS: c-r4k: fix data corruption related to cache coherence.
+X-Patchwork-Submitter: NeilBrown <neil@brown.name>
+X-Patchwork-Id: 19259
+Message-Id: <87vacdlf8d.fsf@notabene.neil.brown.name>
+To: James Hogan <jhogan@kernel.org>
+Cc: Ralf Baechle <ralf@linux-mips.org>,
+ Paul Burton <paul.burton@mips.com>, linux-mips@linux-mips.org,
+ linux-kernel@vger.kernel.org
+Date: Fri, 27 Apr 2018 09:28:34 +1000
+From: NeilBrown <neil@brown.name>
+List-Id: linux-mips <linux-mips.eddie.linux-mips.org>
+
+When DMA will be performed to a MIPS32 1004K CPS, the
+L1-cache for the range needs to be flushed and invalidated
+first.
+The code currently takes one of two approaches.
+1/ If the range is less than the size of the dcache, then
+ HIT type requests flush/invalidate cache lines for the
+ particular addresses. HIT-type requests a globalised
+ by the CPS so this is safe on SMP.
+
+2/ If the range is larger than the size of dcache, then
+ INDEX type requests flush/invalidate the whole cache.
+ INDEX type requests affect the local cache only. CPS
+ does not propagate them in any way. So this invalidation
+ is not safe on SMP CPS systems.
+
+Data corruption due to '2' can quite easily be demonstrated by
+repeatedly "echo 3 > /proc/sys/vm/drop_caches" and then sha1sum
+a file that is several times the size of available memory.
+Dropping caches means that large contiguous extents (large than
+dcache) are more likely.
+
+This was not a problem before Linux-4.8 because option 2 was
+never used if CONFIG_MIPS_CPS was defined. The commit
+which removed that apparently didn't appreciate the full
+consequence of the change.
+
+We could, in theory, globalize the INDEX based flush by sending an IPI
+to other cores. These cache invalidation routines can be called with
+interrupts disabled and synchronous IPI require interrupts to be
+enabled. Asynchronous IPI may not trigger writeback soon enough.
+So we cannot use IPI in practice.
+
+We can already test is IPI would be needed for an INDEX operation
+with r4k_op_needs_ipi(R4K_INDEX). If this is True then we mustn't try
+the INDEX approach as we cannot use IPI. If this is False (e.g. when
+there is only one core and hence one L1 cache) then it is safe to
+use the INDEX approach without IPI.
+
+This patch avoids options 2 if r4k_op_needs_ipi(R4K_INDEX), and so
+eliminates the corruption.
+
+Fixes: c00ab4896ed5 ("MIPS: Remove cpu_has_safe_index_cacheops")
+Cc: stable@vger.kernel.org # v4.8+
+Signed-off-by: NeilBrown <neil@brown.name>
+---
+ arch/mips/mm/c-r4k.c | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c
+index 6f534b209971..e12dfa48b478 100644
+--- a/arch/mips/mm/c-r4k.c
++++ b/arch/mips/mm/c-r4k.c
+@@ -851,9 +851,12 @@ static void r4k_dma_cache_wback_inv(unsigned long addr, unsigned long size)
+ /*
+ * Either no secondary cache or the available caches don't have the
+ * subset property so we have to flush the primary caches
+- * explicitly
++ * explicitly.
++ * If we would need IPI to perform an INDEX-type operation, then
++ * we have to use the HIT-type alternative as IPI cannot be used
++ * here due to interrupts possibly being disabled.
+ */
+- if (size >= dcache_size) {
++ if (!r4k_op_needs_ipi(R4K_INDEX) && size >= dcache_size) {
+ r4k_blast_dcache();
+ } else {
+ R4600_HIT_CACHEOP_WAR_IMPL;
+@@ -890,7 +893,7 @@ static void r4k_dma_cache_inv(unsigned long addr, unsigned long size)
+ return;
+ }
+
+- if (size >= dcache_size) {
++ if (!r4k_op_needs_ipi(R4K_INDEX) && size >= dcache_size) {
+ r4k_blast_dcache();
+ } else {
+ R4600_HIT_CACHEOP_WAR_IMPL;