summaryrefslogtreecommitdiff
path: root/arch/arm64/kernel/mte.c
diff options
context:
space:
mode:
authorSteven Price <steven.price@arm.com>2020-05-13 18:37:50 +0300
committerCatalin Marinas <catalin.marinas@arm.com>2020-09-04 14:46:07 +0300
commit36943aba91860269abfba2e736e9534d48e90cae (patch)
tree4fb2f58ccd9e8811cad36b712738f2afc5e7ebb0 /arch/arm64/kernel/mte.c
parent8a84802e2a2b1a682761a31c2685506b9f4e1840 (diff)
downloadlinux-36943aba91860269abfba2e736e9534d48e90cae.tar.xz
arm64: mte: Enable swap of tagged pages
When swapping pages out to disk it is necessary to save any tags that have been set, and restore when swapping back in. Make use of the new page flag (PG_ARCH_2, locally named PG_mte_tagged) to identify pages with tags. When swapping out these pages the tags are stored in memory and later restored when the pages are brought back in. Because shmem can swap pages back in without restoring the userspace PTE it is also necessary to add a hook for shmem. Signed-off-by: Steven Price <steven.price@arm.com> [catalin.marinas@arm.com: move function prototypes to mte.h] [catalin.marinas@arm.com: drop '_tags' from arch_swap_restore_tags()] Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Will Deacon <will@kernel.org>
Diffstat (limited to 'arch/arm64/kernel/mte.c')
-rw-r--r--arch/arm64/kernel/mte.c19
1 files changed, 18 insertions, 1 deletions
diff --git a/arch/arm64/kernel/mte.c b/arch/arm64/kernel/mte.c
index 56e79807006c..52a0638ed967 100644
--- a/arch/arm64/kernel/mte.c
+++ b/arch/arm64/kernel/mte.c
@@ -10,6 +10,8 @@
#include <linux/sched.h>
#include <linux/sched/mm.h>
#include <linux/string.h>
+#include <linux/swap.h>
+#include <linux/swapops.h>
#include <linux/thread_info.h>
#include <linux/uio.h>
@@ -18,15 +20,30 @@
#include <asm/ptrace.h>
#include <asm/sysreg.h>
+static void mte_sync_page_tags(struct page *page, pte_t *ptep, bool check_swap)
+{
+ pte_t old_pte = READ_ONCE(*ptep);
+
+ if (check_swap && is_swap_pte(old_pte)) {
+ swp_entry_t entry = pte_to_swp_entry(old_pte);
+
+ if (!non_swap_entry(entry) && mte_restore_tags(entry, page))
+ return;
+ }
+
+ mte_clear_page_tags(page_address(page));
+}
+
void mte_sync_tags(pte_t *ptep, pte_t pte)
{
struct page *page = pte_page(pte);
long i, nr_pages = compound_nr(page);
+ bool check_swap = nr_pages == 1;
/* if PG_mte_tagged is set, tags have already been initialised */
for (i = 0; i < nr_pages; i++, page++) {
if (!test_and_set_bit(PG_mte_tagged, &page->flags))
- mte_clear_page_tags(page_address(page));
+ mte_sync_page_tags(page, ptep, check_swap);
}
}