Skip to content

Commit 013bb59

Browse files
pccwilldeacon
authored andcommitted
arm64: mte: handle tags zeroing at page allocation time
Currently, on an anonymous page fault, the kernel allocates a zeroed page and maps it in user space. If the mapping is tagged (PROT_MTE), set_pte_at() additionally clears the tags. It is, however, more efficient to clear the tags at the same time as zeroing the data on allocation. To avoid clearing the tags on any page (which may not be mapped as tagged), only do this if the vma flags contain VM_MTE. This requires introducing a new GFP flag that is used to determine whether to clear the tags. The DC GZVA instruction with a 0 top byte (and 0 tag) requires top-byte-ignore. Set the TCR_EL1.{TBI1,TBID1} bits irrespective of whether KASAN_HW is enabled. Signed-off-by: Peter Collingbourne <pcc@google.com> Co-developed-by: Catalin Marinas <catalin.marinas@arm.com> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> Link: https://linux-review.googlesource.com/id/Id46dc94e30fe11474f7e54f5d65e7658dbdddb26 Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> Reviewed-by: Andrey Konovalov <andreyknvl@gmail.com> Link: https://lore.kernel.org/r/20210602235230.3928842-4-pcc@google.com Signed-off-by: Will Deacon <will@kernel.org>
1 parent 7a3b835 commit 013bb59

9 files changed

Lines changed: 96 additions & 11 deletions

File tree

arch/arm64/include/asm/mte.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ void mte_free_tag_storage(char *storage);
3737
/* track which pages have valid allocation tags */
3838
#define PG_mte_tagged PG_arch_2
3939

40+
void mte_zero_clear_page_tags(void *addr);
4041
void mte_sync_tags(pte_t *ptep, pte_t pte);
4142
void mte_copy_page_tags(void *kto, const void *kfrom);
4243
void mte_thread_init_user(void);
@@ -53,6 +54,9 @@ int mte_ptrace_copy_tags(struct task_struct *child, long request,
5354
/* unused if !CONFIG_ARM64_MTE, silence the compiler */
5455
#define PG_mte_tagged 0
5556

57+
static inline void mte_zero_clear_page_tags(void *addr)
58+
{
59+
}
5660
static inline void mte_sync_tags(pte_t *ptep, pte_t pte)
5761
{
5862
}

arch/arm64/include/asm/page.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#ifndef __ASSEMBLY__
1414

1515
#include <linux/personality.h> /* for READ_IMPLIES_EXEC */
16+
#include <linux/types.h> /* for gfp_t */
1617
#include <asm/pgtable-types.h>
1718

1819
struct page;
@@ -28,10 +29,13 @@ void copy_user_highpage(struct page *to, struct page *from,
2829
void copy_highpage(struct page *to, struct page *from);
2930
#define __HAVE_ARCH_COPY_HIGHPAGE
3031

31-
#define alloc_zeroed_user_highpage_movable(vma, vaddr) \
32-
alloc_page_vma(GFP_HIGHUSER_MOVABLE | __GFP_ZERO, vma, vaddr)
32+
struct page *alloc_zeroed_user_highpage_movable(struct vm_area_struct *vma,
33+
unsigned long vaddr);
3334
#define __HAVE_ARCH_ALLOC_ZEROED_USER_HIGHPAGE_MOVABLE
3435

36+
void tag_clear_highpage(struct page *to);
37+
#define __HAVE_ARCH_TAG_CLEAR_HIGHPAGE
38+
3539
#define clear_user_page(page, vaddr, pg) clear_page(page)
3640
#define copy_user_page(to, from, vaddr, pg) copy_page(to, from)
3741

arch/arm64/lib/mte.S

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,26 @@ SYM_FUNC_START(mte_clear_page_tags)
3636
ret
3737
SYM_FUNC_END(mte_clear_page_tags)
3838

39+
/*
40+
* Zero the page and tags at the same time
41+
*
42+
* Parameters:
43+
* x0 - address to the beginning of the page
44+
*/
45+
SYM_FUNC_START(mte_zero_clear_page_tags)
46+
mrs x1, dczid_el0
47+
and w1, w1, #0xf
48+
mov x2, #4
49+
lsl x1, x2, x1
50+
and x0, x0, #(1 << MTE_TAG_SHIFT) - 1 // clear the tag
51+
52+
1: dc gzva, x0
53+
add x0, x0, x1
54+
tst x0, #(PAGE_SIZE - 1)
55+
b.ne 1b
56+
ret
57+
SYM_FUNC_END(mte_zero_clear_page_tags)
58+
3959
/*
4060
* Copy the tags from the source page to the destination one
4161
* x0 - address of the destination page

arch/arm64/mm/fault.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -921,3 +921,29 @@ void do_debug_exception(unsigned long addr_if_watchpoint, unsigned int esr,
921921
debug_exception_exit(regs);
922922
}
923923
NOKPROBE_SYMBOL(do_debug_exception);
924+
925+
/*
926+
* Used during anonymous page fault handling.
927+
*/
928+
struct page *alloc_zeroed_user_highpage_movable(struct vm_area_struct *vma,
929+
unsigned long vaddr)
930+
{
931+
gfp_t flags = GFP_HIGHUSER_MOVABLE | __GFP_ZERO;
932+
933+
/*
934+
* If the page is mapped with PROT_MTE, initialise the tags at the
935+
* point of allocation and page zeroing as this is usually faster than
936+
* separate DC ZVA and STGM.
937+
*/
938+
if (vma->vm_flags & VM_MTE)
939+
flags |= __GFP_ZEROTAGS;
940+
941+
return alloc_page_vma(flags, vma, vaddr);
942+
}
943+
944+
void tag_clear_highpage(struct page *page)
945+
{
946+
mte_zero_clear_page_tags(page_address(page));
947+
page_kasan_tag_reset(page);
948+
set_bit(PG_mte_tagged, &page->flags);
949+
}

arch/arm64/mm/proc.S

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,13 @@
4646
#endif
4747

4848
#ifdef CONFIG_KASAN_HW_TAGS
49-
#define TCR_KASAN_HW_FLAGS SYS_TCR_EL1_TCMA1 | TCR_TBI1 | TCR_TBID1
49+
#define TCR_MTE_FLAGS SYS_TCR_EL1_TCMA1 | TCR_TBI1 | TCR_TBID1
5050
#else
51-
#define TCR_KASAN_HW_FLAGS 0
51+
/*
52+
* The mte_zero_clear_page_tags() implementation uses DC GZVA, which relies on
53+
* TBI being enabled at EL1.
54+
*/
55+
#define TCR_MTE_FLAGS TCR_TBI1 | TCR_TBID1
5256
#endif
5357

5458
/*
@@ -464,7 +468,7 @@ SYM_FUNC_START(__cpu_setup)
464468
msr_s SYS_TFSRE0_EL1, xzr
465469

466470
/* set the TCR_EL1 bits */
467-
mov_q x10, TCR_KASAN_HW_FLAGS
471+
mov_q x10, TCR_MTE_FLAGS
468472
orr tcr, tcr, x10
469473
1:
470474
#endif

include/linux/gfp.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ struct vm_area_struct;
5353
#define ___GFP_HARDWALL 0x100000u
5454
#define ___GFP_THISNODE 0x200000u
5555
#define ___GFP_ACCOUNT 0x400000u
56+
#define ___GFP_ZEROTAGS 0x800000u
5657
#ifdef CONFIG_LOCKDEP
57-
#define ___GFP_NOLOCKDEP 0x800000u
58+
#define ___GFP_NOLOCKDEP 0x1000000u
5859
#else
5960
#define ___GFP_NOLOCKDEP 0
6061
#endif
@@ -229,16 +230,20 @@ struct vm_area_struct;
229230
* %__GFP_COMP address compound page metadata.
230231
*
231232
* %__GFP_ZERO returns a zeroed page on success.
233+
*
234+
* %__GFP_ZEROTAGS returns a page with zeroed memory tags on success, if
235+
* __GFP_ZERO is set.
232236
*/
233237
#define __GFP_NOWARN ((__force gfp_t)___GFP_NOWARN)
234238
#define __GFP_COMP ((__force gfp_t)___GFP_COMP)
235239
#define __GFP_ZERO ((__force gfp_t)___GFP_ZERO)
240+
#define __GFP_ZEROTAGS ((__force gfp_t)___GFP_ZEROTAGS)
236241

237242
/* Disable lockdep for GFP context tracking */
238243
#define __GFP_NOLOCKDEP ((__force gfp_t)___GFP_NOLOCKDEP)
239244

240245
/* Room for N __GFP_FOO bits */
241-
#define __GFP_BITS_SHIFT (23 + IS_ENABLED(CONFIG_LOCKDEP))
246+
#define __GFP_BITS_SHIFT (24 + IS_ENABLED(CONFIG_LOCKDEP))
242247
#define __GFP_BITS_MASK ((__force gfp_t)((1 << __GFP_BITS_SHIFT) - 1))
243248

244249
/**

include/linux/highmem.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,14 @@ static inline void clear_highpage(struct page *page)
185185
kunmap_atomic(kaddr);
186186
}
187187

188+
#ifndef __HAVE_ARCH_TAG_CLEAR_HIGHPAGE
189+
190+
static inline void tag_clear_highpage(struct page *page)
191+
{
192+
}
193+
194+
#endif
195+
188196
/*
189197
* If we pass in a base or tail page, we can zero up to PAGE_SIZE.
190198
* If we pass in a head page, we can zero up to the size of the compound page.

mm/kasan/hw_tags.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,14 @@ void kasan_alloc_pages(struct page *page, unsigned int order, gfp_t flags)
246246
*/
247247
bool init = !want_init_on_free() && want_init_on_alloc(flags);
248248

249-
kasan_unpoison_pages(page, order, init);
249+
if (flags & __GFP_ZEROTAGS) {
250+
int i;
251+
252+
for (i = 0; i != 1 << order; ++i)
253+
tag_clear_highpage(page + i);
254+
} else {
255+
kasan_unpoison_pages(page, order, init);
256+
}
250257
}
251258

252259
void kasan_free_pages(struct page *page, unsigned int order)

mm/page_alloc.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,10 +1219,16 @@ static int free_tail_pages_check(struct page *head_page, struct page *page)
12191219
return ret;
12201220
}
12211221

1222-
static void kernel_init_free_pages(struct page *page, int numpages)
1222+
static void kernel_init_free_pages(struct page *page, int numpages, bool zero_tags)
12231223
{
12241224
int i;
12251225

1226+
if (zero_tags) {
1227+
for (i = 0; i < numpages; i++)
1228+
tag_clear_highpage(page + i);
1229+
return;
1230+
}
1231+
12261232
/* s390's use of memset() could override KASAN redzones. */
12271233
kasan_disable_current();
12281234
for (i = 0; i < numpages; i++) {
@@ -1314,7 +1320,7 @@ static __always_inline bool free_pages_prepare(struct page *page,
13141320
bool init = want_init_on_free();
13151321

13161322
if (init)
1317-
kernel_init_free_pages(page, 1 << order);
1323+
kernel_init_free_pages(page, 1 << order, false);
13181324
if (!skip_kasan_poison)
13191325
kasan_poison_pages(page, order, init);
13201326
}
@@ -2349,7 +2355,8 @@ inline void post_alloc_hook(struct page *page, unsigned int order,
23492355

23502356
kasan_unpoison_pages(page, order, init);
23512357
if (init)
2352-
kernel_init_free_pages(page, 1 << order);
2358+
kernel_init_free_pages(page, 1 << order,
2359+
gfp_flags & __GFP_ZEROTAGS);
23532360
}
23542361

23552362
set_page_owner(page, order, gfp_flags);

0 commit comments

Comments
 (0)