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