t_seqlock.c revision 1.1
11.1Sriastrad/* $NetBSD: t_seqlock.c,v 1.1 2022/04/08 23:35:52 riastradh Exp $ */ 21.1Sriastrad 31.1Sriastrad/*- 41.1Sriastrad * Copyright (c) 2022 The NetBSD Foundation, Inc. 51.1Sriastrad * All rights reserved. 61.1Sriastrad * 71.1Sriastrad * Redistribution and use in source and binary forms, with or without 81.1Sriastrad * modification, are permitted provided that the following conditions 91.1Sriastrad * are met: 101.1Sriastrad * 1. Redistributions of source code must retain the above copyright 111.1Sriastrad * notice, this list of conditions and the following disclaimer. 121.1Sriastrad * 2. Redistributions in binary form must reproduce the above copyright 131.1Sriastrad * notice, this list of conditions and the following disclaimer in the 141.1Sriastrad * documentation and/or other materials provided with the distribution. 151.1Sriastrad * 161.1Sriastrad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 171.1Sriastrad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 181.1Sriastrad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 191.1Sriastrad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 201.1Sriastrad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 211.1Sriastrad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 221.1Sriastrad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 231.1Sriastrad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 241.1Sriastrad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 251.1Sriastrad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 261.1Sriastrad * POSSIBILITY OF SUCH DAMAGE. 271.1Sriastrad */ 281.1Sriastrad 291.1Sriastrad#include <sys/cdefs.h> 301.1Sriastrad__RCSID("$NetBSD: t_seqlock.c,v 1.1 2022/04/08 23:35:52 riastradh Exp $"); 311.1Sriastrad 321.1Sriastrad#include <sys/atomic.h> 331.1Sriastrad#include <sys/param.h> 341.1Sriastrad#include <sys/sysctl.h> 351.1Sriastrad 361.1Sriastrad#include <assert.h> 371.1Sriastrad#include <atf-c.h> 381.1Sriastrad#include <err.h> 391.1Sriastrad#include <errno.h> 401.1Sriastrad#include <inttypes.h> 411.1Sriastrad#include <pthread.h> 421.1Sriastrad#include <signal.h> 431.1Sriastrad#include <stdint.h> 441.1Sriastrad#include <stdio.h> 451.1Sriastrad#include <unistd.h> 461.1Sriastrad 471.1Sriastrad#ifdef BROKEN_PRODUCER 481.1Sriastrad#undef membar_producer 491.1Sriastrad#define membar_producer() asm volatile("" ::: "memory") 501.1Sriastrad#endif /* BROKEN_PRODUCER */ 511.1Sriastrad 521.1Sriastrad#ifdef BROKEN_CONSUMER 531.1Sriastrad#undef membar_consumer 541.1Sriastrad#define membar_consumer() asm volatile("" ::: "memory") 551.1Sriastrad#endif /* BROKEN_CONSUMER */ 561.1Sriastrad 571.1Sriastradvolatile sig_atomic_t times_up; 581.1Sriastrad 591.1Sriastradvolatile unsigned version; 601.1Sriastrad 611.1Sriastradvolatile struct { 621.1Sriastrad uint64_t s; 631.1Sriastrad} __aligned(COHERENCY_UNIT) stats[16]; 641.1Sriastrad 651.1Sriastraduint64_t results[2]; 661.1Sriastrad 671.1Sriastradstatic void 681.1Sriastradalarm_handler(int signo) 691.1Sriastrad{ 701.1Sriastrad 711.1Sriastrad (void)signo; 721.1Sriastrad times_up = 1; 731.1Sriastrad} 741.1Sriastrad 751.1Sriastradstatic void * 761.1Sriastradwriter(void *cookie) 771.1Sriastrad{ 781.1Sriastrad uint64_t s; 791.1Sriastrad unsigned i; 801.1Sriastrad 811.1Sriastrad for (s = 0; !times_up; s++) { 821.1Sriastrad version |= 1; 831.1Sriastrad membar_producer(); 841.1Sriastrad for (i = __arraycount(stats); i --> 0;) 851.1Sriastrad stats[i].s = s; 861.1Sriastrad membar_producer(); 871.1Sriastrad version |= 1; 881.1Sriastrad version += 1; 891.1Sriastrad 901.1Sriastrad /* 911.1Sriastrad * Not needed for correctness, but this helps on Cavium 921.1Sriastrad * Octeon cnMIPS CPUs which require issuing a sync 931.1Sriastrad * plunger to unclog store buffers which can otherwise 941.1Sriastrad * stay clogged for hundreds of thousands of cycles, 951.1Sriastrad * giving very little concurrency to this test. 961.1Sriastrad * Without this, the reader spends most of its time 971.1Sriastrad * thinking an update is in progress. 981.1Sriastrad */ 991.1Sriastrad membar_producer(); 1001.1Sriastrad } 1011.1Sriastrad 1021.1Sriastrad return NULL; 1031.1Sriastrad} 1041.1Sriastrad 1051.1Sriastradstatic void * 1061.1Sriastradreader(void *cookie) 1071.1Sriastrad{ 1081.1Sriastrad uint64_t s; 1091.1Sriastrad unsigned v, result, i; 1101.1Sriastrad volatile unsigned *vp = &version; 1111.1Sriastrad volatile uint64_t t; 1121.1Sriastrad 1131.1Sriastrad while (!times_up) { 1141.1Sriastrad /* 1151.1Sriastrad * Prime the cache with possibly stale garbage. 1161.1Sriastrad */ 1171.1Sriastrad t = stats[0].s; 1181.1Sriastrad 1191.1Sriastrad /* 1201.1Sriastrad * Normally we would do 1211.1Sriastrad * 1221.1Sriastrad * while ((v = version) & 1) 1231.1Sriastrad * SPINLOCK_BACKOFF_HOOK; 1241.1Sriastrad * 1251.1Sriastrad * to avoid copying out a version that we know is in 1261.1Sriastrad * flux, but it's not wrong to copy out a version in 1271.1Sriastrad * flux -- just wasteful. 1281.1Sriastrad * 1291.1Sriastrad * Reading the version unconditionally, and then 1301.1Sriastrad * copying out the record, better exercises plausible 1311.1Sriastrad * bugs in PowerPC membars based on `isync' that 1321.1Sriastrad * provide the desired ordering only if separated from 1331.1Sriastrad * the load by a conditional branch based on the load. 1341.1Sriastrad */ 1351.1Sriastrad v = *vp; 1361.1Sriastrad membar_consumer(); 1371.1Sriastrad s = stats[0].s; 1381.1Sriastrad for (result = 0, i = 1; i < __arraycount(stats); i++) 1391.1Sriastrad result |= (s != stats[i].s); 1401.1Sriastrad membar_consumer(); 1411.1Sriastrad if ((v & ~1u) != *vp) 1421.1Sriastrad continue; 1431.1Sriastrad results[result]++; 1441.1Sriastrad } 1451.1Sriastrad 1461.1Sriastrad (void)t; 1471.1Sriastrad 1481.1Sriastrad return NULL; 1491.1Sriastrad} 1501.1Sriastrad 1511.1SriastradATF_TC(seqlock); 1521.1SriastradATF_TC_HEAD(seqlock, tc) 1531.1Sriastrad{ 1541.1Sriastrad atf_tc_set_md_var(tc, "descr", 1551.1Sriastrad "Verify membar_producer/consumer work for seqlocks"); 1561.1Sriastrad} 1571.1SriastradATF_TC_BODY(seqlock, tc) 1581.1Sriastrad{ 1591.1Sriastrad pthread_t t[2]; 1601.1Sriastrad void *(*start[2])(void *) = { &reader, &writer }; 1611.1Sriastrad unsigned i; 1621.1Sriastrad int ncpu; 1631.1Sriastrad size_t ncpulen = sizeof(ncpu); 1641.1Sriastrad int error; 1651.1Sriastrad 1661.1Sriastrad if (sysctlbyname("hw.ncpu", &ncpu, &ncpulen, NULL, 0) == -1) 1671.1Sriastrad atf_tc_fail("hw.ncpu: (%d) %s", errno, strerror(errno)); 1681.1Sriastrad assert(ncpulen == sizeof(ncpu)); 1691.1Sriastrad if (ncpu == 1) 1701.1Sriastrad atf_tc_skip("membar tests are only for multicore systems"); 1711.1Sriastrad 1721.1Sriastrad if (signal(SIGALRM, alarm_handler) == SIG_ERR) 1731.1Sriastrad err(1, "signal(SIGALRM"); 1741.1Sriastrad alarm(5); 1751.1Sriastrad for (i = 0; i < 2; i++) { 1761.1Sriastrad error = pthread_create(&t[i], NULL, start[i], 1771.1Sriastrad (void *)(uintptr_t)i); 1781.1Sriastrad if (error) 1791.1Sriastrad errc(1, error, "pthread_create"); 1801.1Sriastrad } 1811.1Sriastrad for (i = 0; i < 2; i++) { 1821.1Sriastrad error = pthread_join(t[i], NULL); 1831.1Sriastrad if (error) 1841.1Sriastrad errc(1, error, "pthread_join"); 1851.1Sriastrad } 1861.1Sriastrad ATF_REQUIRE(results[0] != 0); 1871.1Sriastrad ATF_REQUIRE_MSG(results[1] == 0, 1881.1Sriastrad "%"PRIu64" good snapshots, %"PRIu64" bad snapshots", 1891.1Sriastrad results[0], results[1]); 1901.1Sriastrad} 1911.1Sriastrad 1921.1SriastradATF_TP_ADD_TCS(tp) 1931.1Sriastrad{ 1941.1Sriastrad 1951.1Sriastrad ATF_TP_ADD_TC(tp, seqlock); 1961.1Sriastrad return atf_no_error(); 1971.1Sriastrad} 198