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