/* $NetBSD: t_seqlock.c,v 1.2 2022/04/10 11:36:32 riastradh Exp $ */ /*- * Copyright (c) 2022 The NetBSD Foundation, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __RCSID("$NetBSD: t_seqlock.c,v 1.2 2022/04/10 11:36:32 riastradh Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include #ifdef BROKEN_PRODUCER #undef membar_producer #define membar_producer() asm volatile("" ::: "memory") #endif /* BROKEN_PRODUCER */ #ifdef BROKEN_CONSUMER #undef membar_consumer #define membar_consumer() asm volatile("" ::: "memory") #endif /* BROKEN_CONSUMER */ volatile sig_atomic_t times_up; volatile unsigned version; volatile struct { uint64_t s; } __aligned(COHERENCY_UNIT) stats[16]; uint64_t results[2]; static void * writer(void *cookie) { uint64_t s; unsigned i; for (s = 0; !times_up; s++) { version |= 1; membar_producer(); for (i = __arraycount(stats); i --> 0;) stats[i].s = s; membar_producer(); version |= 1; version += 1; /* * Not needed for correctness, but this helps on Cavium * Octeon cnMIPS CPUs which require issuing a sync * plunger to unclog store buffers which can otherwise * stay clogged for hundreds of thousands of cycles, * giving very little concurrency to this test. * Without this, the reader spends most of its time * thinking an update is in progress. */ membar_producer(); } return NULL; } static void * reader(void *cookie) { uint64_t s; unsigned v, result, i; volatile unsigned *vp = &version; volatile uint64_t t; while (!times_up) { /* * Prime the cache with possibly stale garbage. */ t = stats[0].s; /* * Normally we would do * * while ((v = version) & 1) * SPINLOCK_BACKOFF_HOOK; * * to avoid copying out a version that we know is in * flux, but it's not wrong to copy out a version in * flux -- just wasteful. * * Reading the version unconditionally, and then * copying out the record, better exercises plausible * bugs in PowerPC membars based on `isync' that * provide the desired ordering only if separated from * the load by a conditional branch based on the load. */ v = *vp; membar_consumer(); s = stats[0].s; for (result = 0, i = 1; i < __arraycount(stats); i++) result |= (s != stats[i].s); membar_consumer(); if ((v & ~1u) != *vp) continue; results[result]++; } (void)t; return NULL; } ATF_TC(seqlock); ATF_TC_HEAD(seqlock, tc) { atf_tc_set_md_var(tc, "descr", "Verify membar_producer/consumer work for seqlocks"); } ATF_TC_BODY(seqlock, tc) { pthread_t t[2]; void *(*start[2])(void *) = { &reader, &writer }; unsigned i; int ncpu; size_t ncpulen = sizeof(ncpu); int error; alarm(10); if (sysctlbyname("hw.ncpu", &ncpu, &ncpulen, NULL, 0) == -1) atf_tc_fail("hw.ncpu: (%d) %s", errno, strerror(errno)); assert(ncpulen == sizeof(ncpu)); if (ncpu == 1) atf_tc_skip("membar tests are only for multicore systems"); for (i = 0; i < 2; i++) { error = pthread_create(&t[i], NULL, start[i], (void *)(uintptr_t)i); if (error) errc(1, error, "pthread_create"); } sleep(5); times_up = 1; for (i = 0; i < 2; i++) { error = pthread_join(t[i], NULL); if (error) errc(1, error, "pthread_join"); } ATF_REQUIRE(results[0] != 0); ATF_REQUIRE_MSG(results[1] == 0, "%"PRIu64" good snapshots, %"PRIu64" bad snapshots", results[0], results[1]); } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, seqlock); return atf_no_error(); }