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