1*49cdfc7eSAndroid Build Coastguard Worker // SPDX-License-Identifier: GPL-2.0-or-later
2*49cdfc7eSAndroid Build Coastguard Worker /*
3*49cdfc7eSAndroid Build Coastguard Worker * Copyright (c) 2017 Richard Palethorpe <[email protected]>
4*49cdfc7eSAndroid Build Coastguard Worker */
5*49cdfc7eSAndroid Build Coastguard Worker
6*49cdfc7eSAndroid Build Coastguard Worker /*
7*49cdfc7eSAndroid Build Coastguard Worker * A basic regression test for tst_atomic_{load,store}. Also provides a
8*49cdfc7eSAndroid Build Coastguard Worker * limited check that atomic stores and loads order non-atomic memory
9*49cdfc7eSAndroid Build Coastguard Worker * accesses. That is, we are checking that they implement memory fences or
10*49cdfc7eSAndroid Build Coastguard Worker * barriers.
11*49cdfc7eSAndroid Build Coastguard Worker *
12*49cdfc7eSAndroid Build Coastguard Worker * Many architectures/machines will still pass the test even if you remove the
13*49cdfc7eSAndroid Build Coastguard Worker * atomic functions. X86 in particular has strong memory ordering by default
14*49cdfc7eSAndroid Build Coastguard Worker * so that should always pass (if you use volatile). However Aarch64
15*49cdfc7eSAndroid Build Coastguard Worker * (Raspberry Pi 3 Model B) has been observed to fail without the atomic
16*49cdfc7eSAndroid Build Coastguard Worker * functions.
17*49cdfc7eSAndroid Build Coastguard Worker *
18*49cdfc7eSAndroid Build Coastguard Worker * A failure can occur if an update to seq_n is not made globally visible by
19*49cdfc7eSAndroid Build Coastguard Worker * the time the next thread needs to use it.
20*49cdfc7eSAndroid Build Coastguard Worker */
21*49cdfc7eSAndroid Build Coastguard Worker
22*49cdfc7eSAndroid Build Coastguard Worker #include <stdint.h>
23*49cdfc7eSAndroid Build Coastguard Worker #include <pthread.h>
24*49cdfc7eSAndroid Build Coastguard Worker #include "tst_test.h"
25*49cdfc7eSAndroid Build Coastguard Worker #include "tst_atomic.h"
26*49cdfc7eSAndroid Build Coastguard Worker
27*49cdfc7eSAndroid Build Coastguard Worker #define THREADS 64
28*49cdfc7eSAndroid Build Coastguard Worker #define FILLER (1 << 20)
29*49cdfc7eSAndroid Build Coastguard Worker
30*49cdfc7eSAndroid Build Coastguard Worker /* Uncomment these to see what happens without atomics. To prevent the compiler
31*49cdfc7eSAndroid Build Coastguard Worker * from removing/reording atomic and seq_n, mark them as volatile.
32*49cdfc7eSAndroid Build Coastguard Worker */
33*49cdfc7eSAndroid Build Coastguard Worker /* #define tst_atomic_load(v) (*(v)) */
34*49cdfc7eSAndroid Build Coastguard Worker /* #define tst_atomic_store(i, v) *(v) = (i) */
35*49cdfc7eSAndroid Build Coastguard Worker
36*49cdfc7eSAndroid Build Coastguard Worker struct block {
37*49cdfc7eSAndroid Build Coastguard Worker int seq_n;
38*49cdfc7eSAndroid Build Coastguard Worker intptr_t id;
39*49cdfc7eSAndroid Build Coastguard Worker intptr_t filler[FILLER];
40*49cdfc7eSAndroid Build Coastguard Worker };
41*49cdfc7eSAndroid Build Coastguard Worker
42*49cdfc7eSAndroid Build Coastguard Worker static int atomic;
43*49cdfc7eSAndroid Build Coastguard Worker /* Instead of storing seq_n on the stack (probably next to the atomic variable
44*49cdfc7eSAndroid Build Coastguard Worker * above), we store it in the middle of some anonymous mapped memory and keep
45*49cdfc7eSAndroid Build Coastguard Worker * a pointer to it. This should decrease the probability that the value of
46*49cdfc7eSAndroid Build Coastguard Worker * seq_n will be synchronised between processors as a byproduct of the atomic
47*49cdfc7eSAndroid Build Coastguard Worker * variable being updated.
48*49cdfc7eSAndroid Build Coastguard Worker */
49*49cdfc7eSAndroid Build Coastguard Worker static int *seq_n;
50*49cdfc7eSAndroid Build Coastguard Worker static struct block *m;
51*49cdfc7eSAndroid Build Coastguard Worker
worker_load_store(void * aid)52*49cdfc7eSAndroid Build Coastguard Worker static void *worker_load_store(void *aid)
53*49cdfc7eSAndroid Build Coastguard Worker {
54*49cdfc7eSAndroid Build Coastguard Worker int id = (intptr_t)aid, i;
55*49cdfc7eSAndroid Build Coastguard Worker
56*49cdfc7eSAndroid Build Coastguard Worker for (i = tst_atomic_load(&atomic);
57*49cdfc7eSAndroid Build Coastguard Worker i != id;
58*49cdfc7eSAndroid Build Coastguard Worker i = tst_atomic_load(&atomic))
59*49cdfc7eSAndroid Build Coastguard Worker ;
60*49cdfc7eSAndroid Build Coastguard Worker
61*49cdfc7eSAndroid Build Coastguard Worker (m + (*seq_n))->id = id;
62*49cdfc7eSAndroid Build Coastguard Worker *seq_n += 1;
63*49cdfc7eSAndroid Build Coastguard Worker tst_atomic_store(i + 1, &atomic);
64*49cdfc7eSAndroid Build Coastguard Worker
65*49cdfc7eSAndroid Build Coastguard Worker return NULL;
66*49cdfc7eSAndroid Build Coastguard Worker }
67*49cdfc7eSAndroid Build Coastguard Worker
68*49cdfc7eSAndroid Build Coastguard Worker /* Attempt to stress the memory transport so that memory operations are
69*49cdfc7eSAndroid Build Coastguard Worker * contended and less predictable. This should increase the likelyhood of a
70*49cdfc7eSAndroid Build Coastguard Worker * failure if a memory fence is missing.
71*49cdfc7eSAndroid Build Coastguard Worker */
mem_spam(void * vp LTP_ATTRIBUTE_UNUSED)72*49cdfc7eSAndroid Build Coastguard Worker static void *mem_spam(void *vp LTP_ATTRIBUTE_UNUSED)
73*49cdfc7eSAndroid Build Coastguard Worker {
74*49cdfc7eSAndroid Build Coastguard Worker intptr_t i = 0, j;
75*49cdfc7eSAndroid Build Coastguard Worker struct block *cur = m;
76*49cdfc7eSAndroid Build Coastguard Worker
77*49cdfc7eSAndroid Build Coastguard Worker tst_res(TINFO, "Memory spammer started");
78*49cdfc7eSAndroid Build Coastguard Worker while (tst_atomic_load(&atomic) > 0) {
79*49cdfc7eSAndroid Build Coastguard Worker for (j = 0; j < FILLER; j++)
80*49cdfc7eSAndroid Build Coastguard Worker cur->filler[j] = j;
81*49cdfc7eSAndroid Build Coastguard Worker
82*49cdfc7eSAndroid Build Coastguard Worker if (i < THREADS - 1) {
83*49cdfc7eSAndroid Build Coastguard Worker cur = m + (++i);
84*49cdfc7eSAndroid Build Coastguard Worker } else {
85*49cdfc7eSAndroid Build Coastguard Worker i = 0;
86*49cdfc7eSAndroid Build Coastguard Worker cur = m;
87*49cdfc7eSAndroid Build Coastguard Worker }
88*49cdfc7eSAndroid Build Coastguard Worker }
89*49cdfc7eSAndroid Build Coastguard Worker
90*49cdfc7eSAndroid Build Coastguard Worker return NULL;
91*49cdfc7eSAndroid Build Coastguard Worker }
92*49cdfc7eSAndroid Build Coastguard Worker
do_test(void)93*49cdfc7eSAndroid Build Coastguard Worker static void do_test(void)
94*49cdfc7eSAndroid Build Coastguard Worker {
95*49cdfc7eSAndroid Build Coastguard Worker intptr_t i, id;
96*49cdfc7eSAndroid Build Coastguard Worker pthread_t threads[THREADS + 1];
97*49cdfc7eSAndroid Build Coastguard Worker
98*49cdfc7eSAndroid Build Coastguard Worker atomic = 0;
99*49cdfc7eSAndroid Build Coastguard Worker m = SAFE_MMAP(NULL, sizeof(*m) * THREADS,
100*49cdfc7eSAndroid Build Coastguard Worker PROT_READ | PROT_WRITE,
101*49cdfc7eSAndroid Build Coastguard Worker MAP_PRIVATE | MAP_ANONYMOUS,
102*49cdfc7eSAndroid Build Coastguard Worker -1, 0);
103*49cdfc7eSAndroid Build Coastguard Worker seq_n = &((m + THREADS / 2)->seq_n);
104*49cdfc7eSAndroid Build Coastguard Worker
105*49cdfc7eSAndroid Build Coastguard Worker pthread_create(&threads[THREADS], NULL, mem_spam, NULL);
106*49cdfc7eSAndroid Build Coastguard Worker for (i = THREADS - 1; i >= 0; i--)
107*49cdfc7eSAndroid Build Coastguard Worker pthread_create(&threads[i], NULL, worker_load_store, (void *)i);
108*49cdfc7eSAndroid Build Coastguard Worker
109*49cdfc7eSAndroid Build Coastguard Worker for (i = 0; i < THREADS; i++) {
110*49cdfc7eSAndroid Build Coastguard Worker tst_res(TINFO, "Joining thread %li", i);
111*49cdfc7eSAndroid Build Coastguard Worker pthread_join(threads[i], NULL);
112*49cdfc7eSAndroid Build Coastguard Worker }
113*49cdfc7eSAndroid Build Coastguard Worker tst_atomic_store(-1, &atomic);
114*49cdfc7eSAndroid Build Coastguard Worker pthread_join(threads[THREADS], NULL);
115*49cdfc7eSAndroid Build Coastguard Worker
116*49cdfc7eSAndroid Build Coastguard Worker tst_res(TINFO, "Expected\tFound");
117*49cdfc7eSAndroid Build Coastguard Worker for (i = 0; i < THREADS; i++) {
118*49cdfc7eSAndroid Build Coastguard Worker id = (m + i)->id;
119*49cdfc7eSAndroid Build Coastguard Worker if (id != i)
120*49cdfc7eSAndroid Build Coastguard Worker tst_res(TFAIL, "%d\t\t%d", (int)i, (int)id);
121*49cdfc7eSAndroid Build Coastguard Worker else
122*49cdfc7eSAndroid Build Coastguard Worker tst_res(TPASS, "%d\t\t%d", (int)i, (int)id);
123*49cdfc7eSAndroid Build Coastguard Worker }
124*49cdfc7eSAndroid Build Coastguard Worker
125*49cdfc7eSAndroid Build Coastguard Worker SAFE_MUNMAP(m, sizeof(*m) * THREADS);
126*49cdfc7eSAndroid Build Coastguard Worker }
127*49cdfc7eSAndroid Build Coastguard Worker
128*49cdfc7eSAndroid Build Coastguard Worker static struct tst_test test = {
129*49cdfc7eSAndroid Build Coastguard Worker .test_all = do_test,
130*49cdfc7eSAndroid Build Coastguard Worker };
131