From patchwork Wed Dec 19 18:39:02 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Alex_Benn=C3=A9e?= X-Patchwork-Id: 154291 Delivered-To: patch@linaro.org Received: by 2002:a2e:299d:0:0:0:0:0 with SMTP id p29-v6csp5262574ljp; Wed, 19 Dec 2018 10:40:36 -0800 (PST) X-Google-Smtp-Source: AFSGD/XQ0V9IPRuS22vmoWyYXzMBQQtBYfzGzSrNdG/vWlhjy+gV2fV4MXNQ8PywCqxdoNiVFE2v X-Received: by 2002:a37:1909:: with SMTP id k9mr21407164qkh.61.1545244836500; Wed, 19 Dec 2018 10:40:36 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1545244836; cv=none; d=google.com; s=arc-20160816; b=kibZnl/+VRdquvrSiZbLJOTmdozBKV4V2w30H4Ms7pCLa3bTtAc7k9x6zDZkGPW0II 2GmYBMvApwZCErecu9yjlZxD3kvg7703tTbgsh8a7c2Gs8qPrTumXcAB8//POw6bvPuV zUfQWzhWnUO1kBnASBS1qe1erEIPn74bYwcJ51f75W+3pMo+DgIpAaxUJdXYubdCgheQ kUCfgTJIjRGKVyaHpoveWcLA4doChx+w4ZCSllsAdh25KSdQzvY1DiZmIRN4d5oUOzOT BUGBriYoI1UrVfITys2i9BwOjJOK7c9M6CFE7jj2MDe3Gb8P5TgPVDtEsAzKG+PCau/g D6cA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:cc:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:subject :content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:to:from:dkim-signature; bh=HBrV7YG5/9XQgCoGFSr/gagY69FKjWJnxPaztOeq888=; b=yzMFwBqWnRokqa4E3bYT3B2G2EuH9sQVu7KmLzl32Yider3uA4bZU+Psab+dkIDCFJ NvD92QKnTP5bQC9po0cIxWuRRhVrNqI+outGQ5iXNEu1QvWVCbPNVz6wVY0tlOdappam cd7iGXSDcw+GU+aJeVWSq2tYuNyhbB7qxoOzm6jFjv+MsWx0iNywxWtt4Ia/iwFg12E7 oR60+uyv3x0FQhfC1/B96sZG/AVZWCCCfrEQ9HvOfGfTFp9FrMsHJMyc5yE+jcvkkPgp N3nBGNBRjSVClBkqvFYp7ULIcyfb586D0uMaGRVNnJkarXMMg3bVSQI4CoJw51FE+Q/y gktQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=fail header.i=@linaro.org header.s=google header.b=F7fz3sQe; spf=pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 2001:4830:134:3::11 as permitted sender) smtp.mailfrom="qemu-devel-bounces+patch=linaro.org@nongnu.org"; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from lists.gnu.org (lists.gnu.org. [2001:4830:134:3::11]) by mx.google.com with ESMTPS id g14si2253606qtp.314.2018.12.19.10.40.36 for (version=TLS1 cipher=AES128-SHA bits=128/128); Wed, 19 Dec 2018 10:40:36 -0800 (PST) Received-SPF: pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 2001:4830:134:3::11 as permitted sender) client-ip=2001:4830:134:3::11; Authentication-Results: mx.google.com; dkim=fail header.i=@linaro.org header.s=google header.b=F7fz3sQe; spf=pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 2001:4830:134:3::11 as permitted sender) smtp.mailfrom="qemu-devel-bounces+patch=linaro.org@nongnu.org"; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: from localhost ([::1]:33545 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gZgln-0004iG-SP for patch@linaro.org; Wed, 19 Dec 2018 13:40:35 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:36510) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gZgkT-0004Iv-TK for qemu-devel@nongnu.org; Wed, 19 Dec 2018 13:39:15 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gZgkQ-0007CC-EO for qemu-devel@nongnu.org; Wed, 19 Dec 2018 13:39:13 -0500 Received: from mail-wm1-x343.google.com ([2a00:1450:4864:20::343]:40191) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1gZgkO-0007BI-Mp for qemu-devel@nongnu.org; Wed, 19 Dec 2018 13:39:10 -0500 Received: by mail-wm1-x343.google.com with SMTP id f188so6802136wmf.5 for ; Wed, 19 Dec 2018 10:39:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=HBrV7YG5/9XQgCoGFSr/gagY69FKjWJnxPaztOeq888=; b=F7fz3sQeDvO+3Upgph35jx38iWnA75Zauxs2NoKEeS29YzbjJ7VYx7voMs67hYB/Zm 9oAczm55cZQlOaj+KF1C6/Mm5SnZ7tYh1J5kZF8GEueXMhX/QYBZW9eX3M/8TYFlgof9 n2ixBYZi/s6xT4OIYoUCLgzBZmcKyJZ9XeG9s= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=HBrV7YG5/9XQgCoGFSr/gagY69FKjWJnxPaztOeq888=; b=kt0la5W5/hu8ROVTP8r1xV8OkxYz5oTejf/6yAdmBfs/kTwAIzQhYV2++r4YWPxZEP BN0/Xw9ai369KbQlVC+jKy1PWcH1ToZobNsnQtPQeOv40kCag66aUuw1bLz/WSFL3DCC yRzdYVc9vCzrkhreXqfXXZN7voXRiE6KG5rUq6quzMz7RRFkgpauwyaxPFIJB2uaZMnR M+l2Bfe6ripaEmHhLYjshMdXKXEiTDzY0OTC7UE/PoS37afQJeVedQk3myEvWQmq4xhk zinuCpKXBIYDWBYlpRnN1biaFc6FSkwJy0dlISfsPmg0wtkTysupvBBeoyEf+bLh0hw8 a43w== X-Gm-Message-State: AA+aEWYa/CWe/MZP9Dt9Sv2OPBtZ0f2Nl+kFzAmc+sT0OXqyyi30KA8p kU/h35J5bC22ouN57gpzshhxYw== X-Received: by 2002:a1c:8c05:: with SMTP id o5mr7740155wmd.29.1545244747421; Wed, 19 Dec 2018 10:39:07 -0800 (PST) Received: from zen.linaro.local ([81.128.185.34]) by smtp.gmail.com with ESMTPSA id x12sm3866565wmc.37.2018.12.19.10.39.06 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Wed, 19 Dec 2018 10:39:06 -0800 (PST) Received: from zen.linaroharston (localhost [127.0.0.1]) by zen.linaro.local (Postfix) with ESMTP id 641473E0380; Wed, 19 Dec 2018 18:39:06 +0000 (GMT) From: =?utf-8?q?Alex_Benn=C3=A9e?= To: qemu-devel@nongnu.org Date: Wed, 19 Dec 2018 18:39:02 +0000 Message-Id: <20181219183902.27273-1-alex.bennee@linaro.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181210152850.435-1-peter.maydell@linaro.org> References: <20181210152850.435-1-peter.maydell@linaro.org> MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::343 Subject: [Qemu-devel] [PATCH] tests/tcg: add barrier test for ARM X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: peter.maydell@linaro.org, qemu-arm@nongnu.org, =?utf-8?q?Alex_Benn?= =?utf-8?b?w6ll?= Errors-To: qemu-devel-bounces+patch=linaro.org@nongnu.org Sender: "Qemu-devel" This is a port of my kvm-unit-tests barrier test. A couple of things are done in a more user-space friendly way but the tests are the same. Signed-off-by: Alex Bennée --- tests/tcg/arm/Makefile.target | 6 +- tests/tcg/arm/barrier.c | 472 ++++++++++++++++++++++++++++++++++ 2 files changed, 477 insertions(+), 1 deletion(-) create mode 100644 tests/tcg/arm/barrier.c -- 2.17.1 diff --git a/tests/tcg/arm/Makefile.target b/tests/tcg/arm/Makefile.target index aa4e4e3782..389616cb19 100644 --- a/tests/tcg/arm/Makefile.target +++ b/tests/tcg/arm/Makefile.target @@ -10,7 +10,7 @@ VPATH += $(ARM_SRC) ARM_TESTS=hello-arm test-arm-iwmmxt -TESTS += $(ARM_TESTS) fcvt +TESTS += $(ARM_TESTS) fcvt barrier hello-arm: CFLAGS+=-marm -ffreestanding hello-arm: LDFLAGS+=-nostdlib @@ -30,3 +30,7 @@ endif # On ARM Linux only supports 4k pages EXTRA_RUNS+=run-test-mmap-4096 + +# Barrier tests need atomic definitions, steal QEMUs +barrier: CFLAGS+=-I$(SRC_PATH)/include/qemu +barrier: LDFLAGS+=-lpthread diff --git a/tests/tcg/arm/barrier.c b/tests/tcg/arm/barrier.c new file mode 100644 index 0000000000..ef47911e36 --- /dev/null +++ b/tests/tcg/arm/barrier.c @@ -0,0 +1,472 @@ +/* + * ARM Barrier Litmus Tests + * + * This test provides a framework for testing barrier conditions on + * the processor. It's simpler than the more involved barrier testing + * frameworks as we are looking for simple failures of QEMU's TCG not + * weird edge cases the silicon gets wrong. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Memory barriers from atomic.h + * + * While it would be nice to include atomic.h directly that + * complicates the build. However we can assume a modern compilers + * with the full set of __atomic C11 primitives. + */ + +#define barrier() ({ asm volatile("" ::: "memory"); (void)0; }) +#define smp_mb() ({ barrier(); __atomic_thread_fence(__ATOMIC_SEQ_CST); }) +#define smp_mb_release() ({ barrier(); __atomic_thread_fence(__ATOMIC_RELEASE); }) +#define smp_mb_acquire() ({ barrier(); __atomic_thread_fence(__ATOMIC_ACQUIRE); }) + +#define smp_wmb() smp_mb_release() +#define smp_rmb() smp_mb_acquire() + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +#define MAX_THREADS 2 + +/* Array size and access controls */ +static int array_size = 100000; +static int wait_if_ahead = 0; + +/* + * These test_array_* structures are a contiguous array modified by two or more + * competing CPUs. The padding is to ensure the variables do not share + * cache lines. + * + * All structures start zeroed. + */ + +typedef struct test_array +{ + volatile unsigned int x; + uint8_t dummy[64]; + volatile unsigned int y; + uint8_t dummy2[64]; + volatile unsigned int r[MAX_THREADS]; +} test_array; + +/* Test definition structure + * + * The first function will always run on the primary CPU, it is + * usually the one that will detect any weirdness and trigger the + * failure of the test. + */ + +typedef int (*test_fn)(void *arg); +typedef void * (*thread_fn)(void *arg); + +typedef struct { + char *test_name; + bool should_pass; + test_fn main_fn; + thread_fn secondary_fn; +} test_descr_t; + +/* + * Synchronisation Helpers + */ + +pthread_barrier_t sync_barrier; + +static void init_sync_point(void) +{ + pthread_barrier_init(&sync_barrier, NULL, 2); + smp_mb(); +} + +static inline void wait_for_main_thread() +{ + pthread_barrier_wait(&sync_barrier); +} + +static inline void wake_up_secondary_thread() +{ + pthread_barrier_wait(&sync_barrier); +} + +/* + * Litmus tests + */ + +/* Simple Message Passing + * + * x is the message data + * y is the flag to indicate the data is ready + * + * Reading x == 0 when y == 1 is a failure. + */ + +static void * message_passing_write(void *arg) +{ + int i; + test_array *array = (test_array *) arg; + + wait_for_main_thread(); + + for (i = 0; i < array_size; i++) { + volatile test_array *entry = &array[i]; + entry->x = 1; + entry->y = 1; + } + + return NULL; +} + +static int message_passing_read(void *arg) +{ + int i; + int errors = 0, ready = 0; + test_array *array = (test_array *) arg; + + wake_up_secondary_thread(); + + for (i = 0; i < array_size; i++) { + volatile test_array *entry = &array[i]; + unsigned int x,y; + y = entry->y; + x = entry->x; + + if (y && !x) + errors++; + ready += y; + } + + return errors; +} + +/* Simple Message Passing with barriers */ +static void * message_passing_write_barrier(void *arg) +{ + int i; + test_array *array = (test_array *) arg; + + wait_for_main_thread(); + + for (i = 0; i< array_size; i++) { + volatile test_array *entry = &array[i]; + entry->x = 1; + smp_wmb(); + entry->y = 1; + } + + return NULL; +} + +static int message_passing_read_barrier(void *arg) +{ + int i; + int errors = 0, ready = 0, not_ready = 0; + test_array *array = (test_array *) arg; + + wake_up_secondary_thread(); + + for (i = 0; i < array_size; i++) { + volatile test_array *entry = &array[i]; + unsigned int x, y; + y = entry->y; + smp_rmb(); + x = entry->x; + + if (y && !x) + errors++; + + if (y) { + ready++; + } else { + not_ready++; + + if (not_ready > 2) { + entry = &array[i+1]; + do { + not_ready = 0; + } while (wait_if_ahead && !entry->y); + } + } + } + + return errors; +} + +/* Simple Message Passing with Acquire/Release */ +static void * message_passing_write_release(void *arg) +{ + int i; + test_array *array = (test_array *) arg; + + for (i=0; i< array_size; i++) { + volatile test_array *entry = &array[i]; + entry->x = 1; + __atomic_store_n(&entry->y, 1, __ATOMIC_RELEASE); + } + + return NULL; +} + +static int message_passing_read_acquire(void *arg) +{ + int i; + int errors = 0, ready = 0, not_ready = 0; + test_array *array = (test_array *) arg; + + for (i = 0; i < array_size; i++) { + volatile test_array *entry = &array[i]; + unsigned int x, y; + __atomic_load(&entry->y, &y, __ATOMIC_ACQUIRE); + x = entry->x; + + if (y && !x) + errors++; + + if (y) { + ready++; + } else { + not_ready++; + + if (not_ready > 2) { + entry = &array[i+1]; + do { + not_ready = 0; + } while (wait_if_ahead && !entry->y); + } + } + } + + return errors; +} + +/* + * Store after load + * + * T1: write 1 to x, load r from y + * T2: write 1 to y, load r from x + * + * Without memory fence r[0] && r[1] == 0 + * With memory fence both == 0 should be impossible + */ + +static int check_store_and_load_results(const char *name, int thread, test_array *array) +{ + int i; + int neither = 0; + int only_first = 0; + int only_second = 0; + int both = 0; + + for (i=0; i< array_size; i++) { + volatile test_array *entry = &array[i]; + if (entry->r[0] == 0 && + entry->r[1] == 0) { + neither++; + } else if (entry->r[0] && + entry->r[1]) { + both++; + } else if (entry->r[0]) { + only_first++; + } else { + only_second++; + } + } + + printf("T%d: neither=%d only_t1=%d only_t2=%d both=%d\n", thread, + neither, only_first, only_second, both); + + return neither; +} + +/* + * This attempts to synchronise the start of both threads to roughly + * the same time. On real hardware there is a little latency as the + * secondary vCPUs are powered up however this effect it much more + * exaggerated on a TCG host. + * + * Busy waits until the we pass a future point in time, returns final + * start time. + */ + +static int store_and_load_1(void *arg) +{ + int i; + test_array *array = (test_array *) arg; + + wake_up_secondary_thread(); + + for (i = 0; i < array_size; i++) { + volatile test_array *entry = &array[i]; + unsigned int r; + entry->x = 1; + r = entry->y; + entry->r[0] = r; + } + + return check_store_and_load_results("sal", 1, array); +} + +static void * store_and_load_2(void *arg) +{ + int i; + test_array *array = (test_array *) arg; + + wait_for_main_thread(); + + for (i = 0; i < array_size; i++) { + volatile test_array *entry = &array[i]; + unsigned int r; + entry->y = 1; + r = entry->x; + entry->r[1] = r; + } + + check_store_and_load_results("sal", 2, array); + + return NULL; +} + +static int store_and_load_barrier_1(void *arg) +{ + int i; + test_array *array = (test_array *) arg; + + wake_up_secondary_thread(); + + for (i = 0; i < array_size; i++) { + volatile test_array *entry = &array[i]; + unsigned int r; + entry->x = 1; + smp_mb(); + r = entry->y; + entry->r[0] = r; + } + + smp_mb(); + + return check_store_and_load_results("sal_barrier", 1, array); +} + +static void * store_and_load_barrier_2(void *arg) +{ + int i; + test_array *array = (test_array *) arg; + + wait_for_main_thread(); + + for (i = 0; i < array_size; i++) { + volatile test_array *entry = &array[i]; + unsigned int r; + entry->y = 1; + smp_mb(); + r = entry->x; + entry->r[1] = r; + } + + check_store_and_load_results("sal_barrier", 2, array); + + return NULL; +} + + +/* Test array */ +static test_descr_t tests[] = { + + { "mp", false, + message_passing_read, + message_passing_write + }, + + { "mp_barrier", true, + message_passing_read_barrier, + message_passing_write_barrier + }, + + { "mp_acqrel", true, + message_passing_read_acquire, + message_passing_write_release + }, + + { "sal", false, + store_and_load_1, + store_and_load_2 + }, + + { "sal_barrier", true, + store_and_load_barrier_1, + store_and_load_barrier_2 + }, +}; + + +int setup_and_run_litmus(test_descr_t *test) +{ + pthread_t tid1; + int res; + test_array *array; + + printf("Running test: %s\n", test->test_name); + array = calloc(array_size, sizeof(test_array)); + printf("Allocated test array @ %p\n", array); + + init_sync_point(); + + if (array) { + pthread_create(&tid1, NULL, test->secondary_fn, array); + res = test->main_fn(array); + } else { + printf("%s: failed to allocate memory", test->test_name); + res = 1; + } + + /* ensure secondary thread has finished */ + pthread_join(tid1, NULL); + + free(array); + array = NULL; + + return res; +} + +int main(int argc, char **argv) +{ + int i; + int res = 0; + + for (i = 0; i < argc; i++) { + char *arg = argv[i]; + unsigned int j; + + /* Test modifiers */ + if (strstr(arg, "count=") != NULL) { + char *p = strstr(arg, "="); + array_size = atol(p+1); + continue; + } else if (strcmp (arg, "wait") == 0) { + wait_if_ahead = 1; + continue; + } else if (strcmp(arg, "help") == 0) { + printf("Tests: "); + for (j = 0; j < ARRAY_SIZE(tests); j++) { + printf("%s ", tests[j].test_name); + } + printf("\n"); + } + + for (j = 0; j < ARRAY_SIZE(tests); j++) { + if (strcmp(arg, tests[j].test_name) == 0) { + res += setup_and_run_litmus(&tests[j]); + continue; + } + } + } + + return res; +}