From: Vincenzo Frascino <[email protected]>
This test is specific to MTE and verifies that the GCR_EL1 register
is context switched correctly.
It spawn 1024 processes and each process spawns 5 threads. Each thread
writes a random setting of GCR_EL1 through the prctl() system call and
reads it back verifying that it is the same. If the values are not the
same it reports a failure.
Note: The test has been extended to verify that even SYNC and ASYNC mode
setting is preserved correctly over context switching.
Signed-off-by: Vincenzo Frascino <[email protected]>
Signed-off-by: Andrey Konovalov <[email protected]>
---
Change-Id: Ia917684a2b8e5f29e705ca5cbf360b010df6f61e
---
tools/testing/selftests/arm64/mte/Makefile | 2 +-
.../arm64/mte/check_gcr_el1_cswitch.c | 152 ++++++++++++++++++
2 files changed, 153 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/arm64/mte/check_gcr_el1_cswitch.c
diff --git a/tools/testing/selftests/arm64/mte/Makefile b/tools/testing/selftests/arm64/mte/Makefile
index 2480226dfe57..0b3af552632a 100644
--- a/tools/testing/selftests/arm64/mte/Makefile
+++ b/tools/testing/selftests/arm64/mte/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2020 ARM Limited
-CFLAGS += -std=gnu99 -I.
+CFLAGS += -std=gnu99 -I. -lpthread
SRCS := $(filter-out mte_common_util.c,$(wildcard *.c))
PROGS := $(patsubst %.c,%,$(SRCS))
diff --git a/tools/testing/selftests/arm64/mte/check_gcr_el1_cswitch.c b/tools/testing/selftests/arm64/mte/check_gcr_el1_cswitch.c
new file mode 100644
index 000000000000..55e33d96794c
--- /dev/null
+++ b/tools/testing/selftests/arm64/mte/check_gcr_el1_cswitch.c
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2020 ARM Limited
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/auxv.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "kselftest.h"
+#include "mte_common_util.h"
+
+#define PR_SET_TAGGED_ADDR_CTRL 55
+#define PR_GET_TAGGED_ADDR_CTRL 56
+# define PR_TAGGED_ADDR_ENABLE (1UL << 0)
+# define PR_MTE_TCF_SHIFT 1
+# define PR_MTE_TCF_NONE (0UL << PR_MTE_TCF_SHIFT)
+# define PR_MTE_TCF_SYNC (1UL << PR_MTE_TCF_SHIFT)
+# define PR_MTE_TCF_ASYNC (2UL << PR_MTE_TCF_SHIFT)
+# define PR_MTE_TCF_MASK (3UL << PR_MTE_TCF_SHIFT)
+# define PR_MTE_TAG_SHIFT 3
+# define PR_MTE_TAG_MASK (0xffffUL << PR_MTE_TAG_SHIFT)
+
+#include "mte_def.h"
+
+#define NUM_ITERATIONS 1024
+#define MAX_THREADS 5
+#define THREAD_ITERATIONS 1000
+
+void *execute_thread(void *x)
+{
+ pid_t pid = *((pid_t *)x);
+ pid_t tid = gettid();
+ uint64_t prctl_tag_mask;
+ uint64_t prctl_set;
+ uint64_t prctl_get;
+ uint64_t prctl_tcf;
+
+ srand(time(NULL) ^ (pid << 16) ^ (tid << 16));
+
+ prctl_tag_mask = rand() % 0xffff;
+
+ if (prctl_tag_mask % 2)
+ prctl_tcf = PR_MTE_TCF_SYNC;
+ else
+ prctl_tcf = PR_MTE_TCF_ASYNC;
+
+ prctl_set = PR_TAGGED_ADDR_ENABLE | prctl_tcf | (prctl_tag_mask << PR_MTE_TAG_SHIFT);
+
+ for (int j = 0; j < THREAD_ITERATIONS; j++) {
+ if (prctl(PR_SET_TAGGED_ADDR_CTRL, prctl_set, 0, 0, 0)) {
+ perror("prctl() failed");
+ goto fail;
+ }
+
+ prctl_get = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+
+ if (prctl_set != prctl_get) {
+ ksft_print_msg("Error: prctl_set: 0x%lx != prctl_get: 0x%lx\n",
+ prctl_set, prctl_get);
+ goto fail;
+ }
+ }
+
+ return (void *)KSFT_PASS;
+
+fail:
+ return (void *)KSFT_FAIL;
+}
+
+int execute_test(pid_t pid)
+{
+ pthread_t thread_id[MAX_THREADS];
+ int thread_data[MAX_THREADS];
+
+ for (int i = 0; i < MAX_THREADS; i++)
+ pthread_create(&thread_id[i], NULL,
+ execute_thread, (void *)&pid);
+
+ for (int i = 0; i < MAX_THREADS; i++)
+ pthread_join(thread_id[i], (void *)&thread_data[i]);
+
+ for (int i = 0; i < MAX_THREADS; i++)
+ if (thread_data[i] == KSFT_FAIL)
+ return KSFT_FAIL;
+
+ return KSFT_PASS;
+}
+
+int mte_gcr_fork_test()
+{
+ pid_t pid[NUM_ITERATIONS];
+ int results[NUM_ITERATIONS];
+ pid_t cpid;
+ int res;
+
+ for (int i = 0; i < NUM_ITERATIONS; i++) {
+ pid[i] = fork();
+
+ if (pid[i] == 0) {
+ cpid = getpid();
+
+ res = execute_test(cpid);
+
+ exit(res);
+ }
+ }
+
+ for (int i = 0; i < NUM_ITERATIONS; i++) {
+ wait(&res);
+
+ if(WIFEXITED(res))
+ results[i] = WEXITSTATUS(res);
+ else
+ --i;
+ }
+
+ for (int i = 0; i < NUM_ITERATIONS; i++)
+ if (results[i] == KSFT_FAIL)
+ return KSFT_FAIL;
+
+ return KSFT_PASS;
+}
+
+int main(int argc, char *argv[])
+{
+ int err;
+
+ err = mte_default_setup();
+ if (err)
+ return err;
+
+ ksft_set_plan(1);
+
+ evaluate_test(mte_gcr_fork_test(),
+ "Verify that GCR_EL1 is set correctly on context switch\n");
+
+ mte_restore_setup();
+ ksft_print_cnts();
+
+ return ksft_get_fail_cnt() == 0 ? KSFT_PASS : KSFT_FAIL;
+}
+
--
2.29.2.222.g5d2a92d10f8-goog
On Tue, Nov 10, 2020 at 11:10:41PM +0100, Andrey Konovalov wrote:
> From: Vincenzo Frascino <[email protected]>
>
> This test is specific to MTE and verifies that the GCR_EL1 register
> is context switched correctly.
>
> It spawn 1024 processes and each process spawns 5 threads. Each thread
> writes a random setting of GCR_EL1 through the prctl() system call and
> reads it back verifying that it is the same. If the values are not the
> same it reports a failure.
>
> Note: The test has been extended to verify that even SYNC and ASYNC mode
> setting is preserved correctly over context switching.
>
> Signed-off-by: Vincenzo Frascino <[email protected]>
> Signed-off-by: Andrey Konovalov <[email protected]>
Acked-by: Catalin Marinas <[email protected]>
On Tue, Nov 10, 2020 at 11:12 PM Andrey Konovalov <[email protected]> wrote:
>
> From: Vincenzo Frascino <[email protected]>
>
> This test is specific to MTE and verifies that the GCR_EL1 register
> is context switched correctly.
>
> It spawn 1024 processes and each process spawns 5 threads. Each thread
Nit: "spawns"
> + srand(time(NULL) ^ (pid << 16) ^ (tid << 16));
> +
> + prctl_tag_mask = rand() % 0xffff;
Nit: if you want values between 0 and 0xffff you probably want to use
bitwise AND.
> +
> +int execute_test(pid_t pid)
> +{
> + pthread_t thread_id[MAX_THREADS];
> + int thread_data[MAX_THREADS];
> +
> + for (int i = 0; i < MAX_THREADS; i++)
> + pthread_create(&thread_id[i], NULL,
> + execute_thread, (void *)&pid);
It might be simpler to call getpid() in execute_thread() instead.
> +int mte_gcr_fork_test()
> +{
> + pid_t pid[NUM_ITERATIONS];
> + int results[NUM_ITERATIONS];
> + pid_t cpid;
> + int res;
> +
> + for (int i = 0; i < NUM_ITERATIONS; i++) {
> + pid[i] = fork();
> +
> + if (pid[i] == 0) {
pid[i] isn't used anywhere else. Did you want to keep the pids to
ensure that all children finished the work?
If not, we can probably go with a scalar here.
> + for (int i = 0; i < NUM_ITERATIONS; i++) {
> + wait(&res);
> +
> + if(WIFEXITED(res))
> + results[i] = WEXITSTATUS(res);
> + else
> + --i;
Won't we get stuck in this loop if fork() returns -1 for one of the processes?
> + }
> +
> + for (int i = 0; i < NUM_ITERATIONS; i++)
> + if (results[i] == KSFT_FAIL)
> + return KSFT_FAIL;
> +
> + return KSFT_PASS;
> +}
> +
--
Alexander Potapenko
Software Engineer
Google Germany GmbH
Erika-Mann-Straße, 33
80636 München
Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg
On Thu, Nov 12, 2020 at 5:09 PM Marco Elver <[email protected]> wrote:
>
> On Thu, 12 Nov 2020 at 16:59, Alexander Potapenko <[email protected]> wrote:
> >
> > On Tue, Nov 10, 2020 at 11:12 PM Andrey Konovalov <[email protected]> wrote:
> > >
> > > From: Vincenzo Frascino <[email protected]>
> > >
> > > This test is specific to MTE and verifies that the GCR_EL1 register
> > > is context switched correctly.
> > >
> > > It spawn 1024 processes and each process spawns 5 threads. Each thread
> >
> > Nit: "spawns"
> >
> >
> > > + srand(time(NULL) ^ (pid << 16) ^ (tid << 16));
> > > +
> > > + prctl_tag_mask = rand() % 0xffff;
> >
> > Nit: if you want values between 0 and 0xffff you probably want to use
> > bitwise AND.
>
> Another question would be, is the max here meant to be 0xffff or
> 0xffff-1. Because, as-is now, it's 0xffff-1. Only one of them has a
> trivial conversion to bitwise AND ( x % 2^n == x & (2^n - 1) ).
Yes, that is basically what I meant, assuming that Vincenzo wanted the
max to be 0xffff
--
Alexander Potapenko
Software Engineer
Google Germany GmbH
Erika-Mann-Straße, 33
80636 München
Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg
On Thu, 12 Nov 2020 at 16:59, Alexander Potapenko <[email protected]> wrote:
>
> On Tue, Nov 10, 2020 at 11:12 PM Andrey Konovalov <[email protected]> wrote:
> >
> > From: Vincenzo Frascino <[email protected]>
> >
> > This test is specific to MTE and verifies that the GCR_EL1 register
> > is context switched correctly.
> >
> > It spawn 1024 processes and each process spawns 5 threads. Each thread
>
> Nit: "spawns"
>
>
> > + srand(time(NULL) ^ (pid << 16) ^ (tid << 16));
> > +
> > + prctl_tag_mask = rand() % 0xffff;
>
> Nit: if you want values between 0 and 0xffff you probably want to use
> bitwise AND.
Another question would be, is the max here meant to be 0xffff or
0xffff-1. Because, as-is now, it's 0xffff-1. Only one of them has a
trivial conversion to bitwise AND ( x % 2^n == x & (2^n - 1) ).
Hi Alexander,
thank you for the review.
On 11/12/20 3:59 PM, Alexander Potapenko wrote:
> On Tue, Nov 10, 2020 at 11:12 PM Andrey Konovalov <[email protected]> wrote:
>>
>> From: Vincenzo Frascino <[email protected]>
>>
>> This test is specific to MTE and verifies that the GCR_EL1 register
>> is context switched correctly.
>>
>> It spawn 1024 processes and each process spawns 5 threads. Each thread
>
> Nit: "spawns"
>
I will fix it in the next iteration.
>
>> + srand(time(NULL) ^ (pid << 16) ^ (tid << 16));
>> +
>> + prctl_tag_mask = rand() % 0xffff;
>
> Nit: if you want values between 0 and 0xffff you probably want to use
> bitwise AND.
>
The main goal here is to have a good probability of having a different setting
to the GCR_EL1 register. Hence the difference in between 0xffff and 0xffff-1 is
negligible. Anyway I agree that we should aim to cover all the possible
combinations.
>
>> +
>> +int execute_test(pid_t pid)
>> +{
>> + pthread_t thread_id[MAX_THREADS];
>> + int thread_data[MAX_THREADS];
>> +
>> + for (int i = 0; i < MAX_THREADS; i++)
>> + pthread_create(&thread_id[i], NULL,
>> + execute_thread, (void *)&pid);
>
> It might be simpler to call getpid() in execute_thread() instead.
>
Yes it might, but I would like to avoid another syscall if I can.
>> +int mte_gcr_fork_test()
>> +{
>> + pid_t pid[NUM_ITERATIONS];
>> + int results[NUM_ITERATIONS];
>> + pid_t cpid;
>> + int res;
>> +
>> + for (int i = 0; i < NUM_ITERATIONS; i++) {
>> + pid[i] = fork();
>> +
>> + if (pid[i] == 0) {
>
> pid[i] isn't used anywhere else. Did you want to keep the pids to
> ensure that all children finished the work?
> If not, we can probably go with a scalar here.
>
Yes, I agree, I had some debug code making use of it, but I removed it in the end.
>
>> + for (int i = 0; i < NUM_ITERATIONS; i++) {
>> + wait(&res);
>> +
>> + if(WIFEXITED(res))
>> + results[i] = WEXITSTATUS(res);
>> + else
>> + --i;
>
> Won't we get stuck in this loop if fork() returns -1 for one of the processes?
>
Yes I agree, I forgot to check a condition. We should abort the test in such a
case returning KSFT_FAIL directly.
>> + }
>> +
>> + for (int i = 0; i < NUM_ITERATIONS; i++)
>> + if (results[i] == KSFT_FAIL)
>> + return KSFT_FAIL;
>> +
>> + return KSFT_PASS;
>> +}
>> +
>
>
--
Regards,
Vincenzo