#include <sys/cdefs.h>
__RCSID("$NetBSD: t_seqlock.c,v 1.2 2022/04/10 11:36:32 riastradh Exp $");
#include <sys/atomic.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <assert.h>
#include <atf-c.h>
#include <err.h>
#include <errno.h>
#include <inttypes.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#ifdef BROKEN_PRODUCER
#undef membar_producer
#define membar_producer() asm volatile("" ::: "memory")
#endif
#ifdef BROKEN_CONSUMER
#undef membar_consumer
#define membar_consumer() asm volatile("" ::: "memory")
#endif
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;
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) {
t = stats[0].s;
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();
}