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 * 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 * 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); 143 ATF_TC_HEAD(seqlock, tc) 144 { 145 atf_tc_set_md_var(tc, "descr", 146 "Verify membar_producer/consumer work for seqlocks"); 147 } 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 184 ATF_TP_ADD_TCS(tp) 185 { 186 187 ATF_TP_ADD_TC(tp, seqlock); 188 return atf_no_error(); 189 } 190