xref: /netbsd-src/tests/lib/libc/membar/t_seqlock.c (revision ea43e3d5a060c14963260a83096fdcc54a9e3672)
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