1 /* $NetBSD: t_seqlock.c,v 1.2 2022/04/10 11:36:32 riastradh Exp $ */
2
3 /*-
4 * Copyright (c) 2022 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __RCSID("$NetBSD: t_seqlock.c,v 1.2 2022/04/10 11:36:32 riastradh Exp $");
31
32 #include <sys/atomic.h>
33 #include <sys/param.h>
34 #include <sys/sysctl.h>
35
36 #include <assert.h>
37 #include <atf-c.h>
38 #include <err.h>
39 #include <errno.h>
40 #include <inttypes.h>
41 #include <pthread.h>
42 #include <stdint.h>
43 #include <stdio.h>
44 #include <unistd.h>
45
46 #ifdef BROKEN_PRODUCER
47 #undef membar_producer
48 #define membar_producer() asm volatile("" ::: "memory")
49 #endif /* BROKEN_PRODUCER */
50
51 #ifdef BROKEN_CONSUMER
52 #undef membar_consumer
53 #define membar_consumer() asm volatile("" ::: "memory")
54 #endif /* BROKEN_CONSUMER */
55
56 volatile sig_atomic_t times_up;
57
58 volatile unsigned version;
59
60 volatile struct {
61 uint64_t s;
62 } __aligned(COHERENCY_UNIT) stats[16];
63
64 uint64_t results[2];
65
66 static void *
writer(void * cookie)67 writer(void *cookie)
68 {
69 uint64_t s;
70 unsigned i;
71
72 for (s = 0; !times_up; s++) {
73 version |= 1;
74 membar_producer();
75 for (i = __arraycount(stats); i --> 0;)
76 stats[i].s = s;
77 membar_producer();
78 version |= 1;
79 version += 1;
80
81 /*
82 * Not needed for correctness, but this helps on Cavium
83 * Octeon cnMIPS CPUs which require issuing a sync
84 * plunger to unclog store buffers which can otherwise
85 * stay clogged for hundreds of thousands of cycles,
86 * giving very little concurrency to this test.
87 * Without this, the reader spends most of its time
88 * thinking an update is in progress.
89 */
90 membar_producer();
91 }
92
93 return NULL;
94 }
95
96 static void *
reader(void * cookie)97 reader(void *cookie)
98 {
99 uint64_t s;
100 unsigned v, result, i;
101 volatile unsigned *vp = &version;
102 volatile uint64_t t;
103
104 while (!times_up) {
105 /*
106 * Prime the cache with possibly stale garbage.
107 */
108 t = stats[0].s;
109
110 /*
111 * Normally we would do
112 *
113 * while ((v = version) & 1)
114 * SPINLOCK_BACKOFF_HOOK;
115 *
116 * to avoid copying out a version that we know is in
117 * flux, but it's not wrong to copy out a version in
118 * flux -- just wasteful.
119 *
120 * Reading the version unconditionally, and then
121 * copying out the record, better exercises plausible
122 * bugs in PowerPC membars based on `isync' that
123 * provide the desired ordering only if separated from
124 * the load by a conditional branch based on the load.
125 */
126 v = *vp;
127 membar_consumer();
128 s = stats[0].s;
129 for (result = 0, i = 1; i < __arraycount(stats); i++)
130 result |= (s != stats[i].s);
131 membar_consumer();
132 if ((v & ~1u) != *vp)
133 continue;
134 results[result]++;
135 }
136
137 (void)t;
138
139 return NULL;
140 }
141
142 ATF_TC(seqlock);
ATF_TC_HEAD(seqlock,tc)143 ATF_TC_HEAD(seqlock, tc)
144 {
145 atf_tc_set_md_var(tc, "descr",
146 "Verify membar_producer/consumer work for seqlocks");
147 }
ATF_TC_BODY(seqlock,tc)148 ATF_TC_BODY(seqlock, tc)
149 {
150 pthread_t t[2];
151 void *(*start[2])(void *) = { &reader, &writer };
152 unsigned i;
153 int ncpu;
154 size_t ncpulen = sizeof(ncpu);
155 int error;
156
157 alarm(10);
158
159 if (sysctlbyname("hw.ncpu", &ncpu, &ncpulen, NULL, 0) == -1)
160 atf_tc_fail("hw.ncpu: (%d) %s", errno, strerror(errno));
161 assert(ncpulen == sizeof(ncpu));
162 if (ncpu == 1)
163 atf_tc_skip("membar tests are only for multicore systems");
164
165 for (i = 0; i < 2; i++) {
166 error = pthread_create(&t[i], NULL, start[i],
167 (void *)(uintptr_t)i);
168 if (error)
169 errc(1, error, "pthread_create");
170 }
171 sleep(5);
172 times_up = 1;
173 for (i = 0; i < 2; i++) {
174 error = pthread_join(t[i], NULL);
175 if (error)
176 errc(1, error, "pthread_join");
177 }
178 ATF_REQUIRE(results[0] != 0);
179 ATF_REQUIRE_MSG(results[1] == 0,
180 "%"PRIu64" good snapshots, %"PRIu64" bad snapshots",
181 results[0], results[1]);
182 }
183
ATF_TP_ADD_TCS(tp)184 ATF_TP_ADD_TCS(tp)
185 {
186
187 ATF_TP_ADD_TC(tp, seqlock);
188 return atf_no_error();
189 }
190