t_seqlock.c revision 1.2
11.2Sriastrad/* $NetBSD: t_seqlock.c,v 1.2 2022/04/10 11:36:32 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.2Sriastrad__RCSID("$NetBSD: t_seqlock.c,v 1.2 2022/04/10 11:36:32 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 <stdint.h> 431.1Sriastrad#include <stdio.h> 441.1Sriastrad#include <unistd.h> 451.1Sriastrad 461.1Sriastrad#ifdef BROKEN_PRODUCER 471.1Sriastrad#undef membar_producer 481.1Sriastrad#define membar_producer() asm volatile("" ::: "memory") 491.1Sriastrad#endif /* BROKEN_PRODUCER */ 501.1Sriastrad 511.1Sriastrad#ifdef BROKEN_CONSUMER 521.1Sriastrad#undef membar_consumer 531.1Sriastrad#define membar_consumer() asm volatile("" ::: "memory") 541.1Sriastrad#endif /* BROKEN_CONSUMER */ 551.1Sriastrad 561.1Sriastradvolatile sig_atomic_t times_up; 571.1Sriastrad 581.1Sriastradvolatile unsigned version; 591.1Sriastrad 601.1Sriastradvolatile struct { 611.1Sriastrad uint64_t s; 621.1Sriastrad} __aligned(COHERENCY_UNIT) stats[16]; 631.1Sriastrad 641.1Sriastraduint64_t results[2]; 651.1Sriastrad 661.1Sriastradstatic void * 671.1Sriastradwriter(void *cookie) 681.1Sriastrad{ 691.1Sriastrad uint64_t s; 701.1Sriastrad unsigned i; 711.1Sriastrad 721.1Sriastrad for (s = 0; !times_up; s++) { 731.1Sriastrad version |= 1; 741.1Sriastrad membar_producer(); 751.1Sriastrad for (i = __arraycount(stats); i --> 0;) 761.1Sriastrad stats[i].s = s; 771.1Sriastrad membar_producer(); 781.1Sriastrad version |= 1; 791.1Sriastrad version += 1; 801.1Sriastrad 811.1Sriastrad /* 821.1Sriastrad * Not needed for correctness, but this helps on Cavium 831.1Sriastrad * Octeon cnMIPS CPUs which require issuing a sync 841.1Sriastrad * plunger to unclog store buffers which can otherwise 851.1Sriastrad * stay clogged for hundreds of thousands of cycles, 861.1Sriastrad * giving very little concurrency to this test. 871.1Sriastrad * Without this, the reader spends most of its time 881.1Sriastrad * thinking an update is in progress. 891.1Sriastrad */ 901.1Sriastrad membar_producer(); 911.1Sriastrad } 921.1Sriastrad 931.1Sriastrad return NULL; 941.1Sriastrad} 951.1Sriastrad 961.1Sriastradstatic void * 971.1Sriastradreader(void *cookie) 981.1Sriastrad{ 991.1Sriastrad uint64_t s; 1001.1Sriastrad unsigned v, result, i; 1011.1Sriastrad volatile unsigned *vp = &version; 1021.1Sriastrad volatile uint64_t t; 1031.1Sriastrad 1041.1Sriastrad while (!times_up) { 1051.1Sriastrad /* 1061.1Sriastrad * Prime the cache with possibly stale garbage. 1071.1Sriastrad */ 1081.1Sriastrad t = stats[0].s; 1091.1Sriastrad 1101.1Sriastrad /* 1111.1Sriastrad * Normally we would do 1121.1Sriastrad * 1131.1Sriastrad * while ((v = version) & 1) 1141.1Sriastrad * SPINLOCK_BACKOFF_HOOK; 1151.1Sriastrad * 1161.1Sriastrad * to avoid copying out a version that we know is in 1171.1Sriastrad * flux, but it's not wrong to copy out a version in 1181.1Sriastrad * flux -- just wasteful. 1191.1Sriastrad * 1201.1Sriastrad * Reading the version unconditionally, and then 1211.1Sriastrad * copying out the record, better exercises plausible 1221.1Sriastrad * bugs in PowerPC membars based on `isync' that 1231.1Sriastrad * provide the desired ordering only if separated from 1241.1Sriastrad * the load by a conditional branch based on the load. 1251.1Sriastrad */ 1261.1Sriastrad v = *vp; 1271.1Sriastrad membar_consumer(); 1281.1Sriastrad s = stats[0].s; 1291.1Sriastrad for (result = 0, i = 1; i < __arraycount(stats); i++) 1301.1Sriastrad result |= (s != stats[i].s); 1311.1Sriastrad membar_consumer(); 1321.1Sriastrad if ((v & ~1u) != *vp) 1331.1Sriastrad continue; 1341.1Sriastrad results[result]++; 1351.1Sriastrad } 1361.1Sriastrad 1371.1Sriastrad (void)t; 1381.1Sriastrad 1391.1Sriastrad return NULL; 1401.1Sriastrad} 1411.1Sriastrad 1421.1SriastradATF_TC(seqlock); 1431.1SriastradATF_TC_HEAD(seqlock, tc) 1441.1Sriastrad{ 1451.1Sriastrad atf_tc_set_md_var(tc, "descr", 1461.1Sriastrad "Verify membar_producer/consumer work for seqlocks"); 1471.1Sriastrad} 1481.1SriastradATF_TC_BODY(seqlock, tc) 1491.1Sriastrad{ 1501.1Sriastrad pthread_t t[2]; 1511.1Sriastrad void *(*start[2])(void *) = { &reader, &writer }; 1521.1Sriastrad unsigned i; 1531.1Sriastrad int ncpu; 1541.1Sriastrad size_t ncpulen = sizeof(ncpu); 1551.1Sriastrad int error; 1561.1Sriastrad 1571.2Sriastrad alarm(10); 1581.2Sriastrad 1591.1Sriastrad if (sysctlbyname("hw.ncpu", &ncpu, &ncpulen, NULL, 0) == -1) 1601.1Sriastrad atf_tc_fail("hw.ncpu: (%d) %s", errno, strerror(errno)); 1611.1Sriastrad assert(ncpulen == sizeof(ncpu)); 1621.1Sriastrad if (ncpu == 1) 1631.1Sriastrad atf_tc_skip("membar tests are only for multicore systems"); 1641.1Sriastrad 1651.1Sriastrad for (i = 0; i < 2; i++) { 1661.1Sriastrad error = pthread_create(&t[i], NULL, start[i], 1671.1Sriastrad (void *)(uintptr_t)i); 1681.1Sriastrad if (error) 1691.1Sriastrad errc(1, error, "pthread_create"); 1701.1Sriastrad } 1711.2Sriastrad sleep(5); 1721.2Sriastrad times_up = 1; 1731.1Sriastrad for (i = 0; i < 2; i++) { 1741.1Sriastrad error = pthread_join(t[i], NULL); 1751.1Sriastrad if (error) 1761.1Sriastrad errc(1, error, "pthread_join"); 1771.1Sriastrad } 1781.1Sriastrad ATF_REQUIRE(results[0] != 0); 1791.1Sriastrad ATF_REQUIRE_MSG(results[1] == 0, 1801.1Sriastrad "%"PRIu64" good snapshots, %"PRIu64" bad snapshots", 1811.1Sriastrad results[0], results[1]); 1821.1Sriastrad} 1831.1Sriastrad 1841.1SriastradATF_TP_ADD_TCS(tp) 1851.1Sriastrad{ 1861.1Sriastrad 1871.1Sriastrad ATF_TP_ADD_TC(tp, seqlock); 1881.1Sriastrad return atf_no_error(); 1891.1Sriastrad} 190