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