diff options
Diffstat (limited to 'target/linux/generic')
-rwxr-xr-x | target/linux/generic/pending-4.14/103-MIPS-c-r4k-fix-data-corruption-related-to-cache-coherence.patch | 92 |
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; |