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