arm64: mte: Tags-aware aware memcmp_pages() implementation
authorCatalin Marinas <catalin.marinas@arm.com>
Wed, 27 Nov 2019 09:53:44 +0000 (09:53 +0000)
committerCatalin Marinas <catalin.marinas@arm.com>
Fri, 4 Sep 2020 11:46:07 +0000 (12:46 +0100)
When the Memory Tagging Extension is enabled, two pages are identical
only if both their data and tags are identical.

Make the generic memcmp_pages() a __weak function and add an
arm64-specific implementation which returns non-zero if any of the two
pages contain valid MTE tags (PG_mte_tagged set). There isn't much
benefit in comparing the tags of two pages since these are normally used
for heap allocations and likely to differ anyway.

Co-developed-by: Vincenzo Frascino <vincenzo.frascino@arm.com>
Signed-off-by: Vincenzo Frascino <vincenzo.frascino@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
arch/arm64/kernel/mte.c
mm/util.c

index 5bf9bbed5a252e9d93fa520c62bb00b52415fa3a..5f54fd140610a78d61eeb6c5231f0fbd3d33f9a2 100644 (file)
@@ -5,6 +5,7 @@
 
 #include <linux/bitops.h>
 #include <linux/mm.h>
+#include <linux/string.h>
 #include <linux/thread_info.h>
 
 #include <asm/cpufeature.h>
@@ -23,6 +24,31 @@ void mte_sync_tags(pte_t *ptep, pte_t pte)
        }
 }
 
+int memcmp_pages(struct page *page1, struct page *page2)
+{
+       char *addr1, *addr2;
+       int ret;
+
+       addr1 = page_address(page1);
+       addr2 = page_address(page2);
+       ret = memcmp(addr1, addr2, PAGE_SIZE);
+
+       if (!system_supports_mte() || ret)
+               return ret;
+
+       /*
+        * If the page content is identical but at least one of the pages is
+        * tagged, return non-zero to avoid KSM merging. If only one of the
+        * pages is tagged, set_pte_at() may zero or change the tags of the
+        * other page via mte_sync_tags().
+        */
+       if (test_bit(PG_mte_tagged, &page1->flags) ||
+           test_bit(PG_mte_tagged, &page2->flags))
+               return addr1 != addr2;
+
+       return ret;
+}
+
 void flush_mte_state(void)
 {
        if (!system_supports_mte())
index 5ef378a2a038b5a0dba372dffe5bd0fc46a0cbfe..4e21fe7eae278fb57e7efa8e6da2e29c0922b3b3 100644 (file)
--- a/mm/util.c
+++ b/mm/util.c
@@ -957,7 +957,7 @@ out:
        return res;
 }
 
-int memcmp_pages(struct page *page1, struct page *page2)
+int __weak memcmp_pages(struct page *page1, struct page *page2)
 {
        char *addr1, *addr2;
        int ret;