From nobody Wed Jun 26 09:55:01 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=linaro.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1545244816582180.43103791346448; Wed, 19 Dec 2018 10:40:16 -0800 (PST) Received: from localhost ([::1]:33545 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gZglT-0004iG-41 for importer@patchew.org; Wed, 19 Dec 2018 13:40:15 -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) 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) 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-Google-Smtp-Source: AFSGD/X7PVXfmmddxiwGISi614ZxAThBtfh8bRcUvdt5OQqhiYn1uUlEP4YaKthPObWz5mqSKlIARQ== X-Received: by 2002:a1c:8c05:: with SMTP id o5mr7740155wmd.29.1545244747421; Wed, 19 Dec 2018 10:39:07 -0800 (PST) From: =?UTF-8?q?Alex=20Benn=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 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable 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=20Benn=C3=A9e?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) 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=C3=A9e --- 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 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 +=3D $(ARM_SRC) =20 ARM_TESTS=3Dhello-arm test-arm-iwmmxt =20 -TESTS +=3D $(ARM_TESTS) fcvt +TESTS +=3D $(ARM_TESTS) fcvt barrier =20 hello-arm: CFLAGS+=3D-marm -ffreestanding hello-arm: LDFLAGS+=3D-nostdlib @@ -30,3 +30,7 @@ endif =20 # On ARM Linux only supports 4k pages EXTRA_RUNS+=3Drun-test-mmap-4096 + +# Barrier tests need atomic definitions, steal QEMUs +barrier: CFLAGS+=3D-I$(SRC_PATH)/include/qemu +barrier: LDFLAGS+=3D-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_SE= Q_CST); }) +#define smp_mb_release() ({ barrier(); __atomic_thread_fence(__ATOMIC_RE= LEASE); }) +#define smp_mb_acquire() ({ barrier(); __atomic_thread_fence(__ATOMIC_AC= QUIRE); }) + +#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 =3D 100000; +static int wait_if_ahead =3D 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 =3D=3D 0 when y =3D=3D 1 is a failure. + */ + +static void * message_passing_write(void *arg) +{ + int i; + test_array *array =3D (test_array *) arg; + + wait_for_main_thread(); + + for (i =3D 0; i < array_size; i++) { + volatile test_array *entry =3D &array[i]; + entry->x =3D 1; + entry->y =3D 1; + } + + return NULL; +} + +static int message_passing_read(void *arg) +{ + int i; + int errors =3D 0, ready =3D 0; + test_array *array =3D (test_array *) arg; + + wake_up_secondary_thread(); + + for (i =3D 0; i < array_size; i++) { + volatile test_array *entry =3D &array[i]; + unsigned int x,y; + y =3D entry->y; + x =3D entry->x; + + if (y && !x) + errors++; + ready +=3D y; + } + + return errors; +} + +/* Simple Message Passing with barriers */ +static void * message_passing_write_barrier(void *arg) +{ + int i; + test_array *array =3D (test_array *) arg; + + wait_for_main_thread(); + + for (i =3D 0; i< array_size; i++) { + volatile test_array *entry =3D &array[i]; + entry->x =3D 1; + smp_wmb(); + entry->y =3D 1; + } + + return NULL; +} + +static int message_passing_read_barrier(void *arg) +{ + int i; + int errors =3D 0, ready =3D 0, not_ready =3D 0; + test_array *array =3D (test_array *) arg; + + wake_up_secondary_thread(); + + for (i =3D 0; i < array_size; i++) { + volatile test_array *entry =3D &array[i]; + unsigned int x, y; + y =3D entry->y; + smp_rmb(); + x =3D entry->x; + + if (y && !x) + errors++; + + if (y) { + ready++; + } else { + not_ready++; + + if (not_ready > 2) { + entry =3D &array[i+1]; + do { + not_ready =3D 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 =3D (test_array *) arg; + + for (i=3D0; i< array_size; i++) { + volatile test_array *entry =3D &array[i]; + entry->x =3D 1; + __atomic_store_n(&entry->y, 1, __ATOMIC_RELEASE); + } + + return NULL; +} + +static int message_passing_read_acquire(void *arg) +{ + int i; + int errors =3D 0, ready =3D 0, not_ready =3D 0; + test_array *array =3D (test_array *) arg; + + for (i =3D 0; i < array_size; i++) { + volatile test_array *entry =3D &array[i]; + unsigned int x, y; + __atomic_load(&entry->y, &y, __ATOMIC_ACQUIRE); + x =3D entry->x; + + if (y && !x) + errors++; + + if (y) { + ready++; + } else { + not_ready++; + + if (not_ready > 2) { + entry =3D &array[i+1]; + do { + not_ready =3D 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] =3D=3D 0 + * With memory fence both =3D=3D 0 should be impossible + */ + +static int check_store_and_load_results(const char *name, int thread, test= _array *array) +{ + int i; + int neither =3D 0; + int only_first =3D 0; + int only_second =3D 0; + int both =3D 0; + + for (i=3D0; i< array_size; i++) { + volatile test_array *entry =3D &array[i]; + if (entry->r[0] =3D=3D 0 && + entry->r[1] =3D=3D 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=3D%d only_t1=3D%d only_t2=3D%d both=3D%d\n", thre= ad, + 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 =3D (test_array *) arg; + + wake_up_secondary_thread(); + + for (i =3D 0; i < array_size; i++) { + volatile test_array *entry =3D &array[i]; + unsigned int r; + entry->x =3D 1; + r =3D entry->y; + entry->r[0] =3D r; + } + + return check_store_and_load_results("sal", 1, array); +} + +static void * store_and_load_2(void *arg) +{ + int i; + test_array *array =3D (test_array *) arg; + + wait_for_main_thread(); + + for (i =3D 0; i < array_size; i++) { + volatile test_array *entry =3D &array[i]; + unsigned int r; + entry->y =3D 1; + r =3D entry->x; + entry->r[1] =3D 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 =3D (test_array *) arg; + + wake_up_secondary_thread(); + + for (i =3D 0; i < array_size; i++) { + volatile test_array *entry =3D &array[i]; + unsigned int r; + entry->x =3D 1; + smp_mb(); + r =3D entry->y; + entry->r[0] =3D 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 =3D (test_array *) arg; + + wait_for_main_thread(); + + for (i =3D 0; i < array_size; i++) { + volatile test_array *entry =3D &array[i]; + unsigned int r; + entry->y =3D 1; + smp_mb(); + r =3D entry->x; + entry->r[1] =3D r; + } + + check_store_and_load_results("sal_barrier", 2, array); + + return NULL; +} + + +/* Test array */ +static test_descr_t tests[] =3D { + + { "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 =3D 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 =3D test->main_fn(array); + } else { + printf("%s: failed to allocate memory", test->test_name); + res =3D 1; + } + + /* ensure secondary thread has finished */ + pthread_join(tid1, NULL); + + free(array); + array =3D NULL; + + return res; +} + +int main(int argc, char **argv) +{ + int i; + int res =3D 0; + + for (i =3D 0; i < argc; i++) { + char *arg =3D argv[i]; + unsigned int j; + + /* Test modifiers */ + if (strstr(arg, "count=3D") !=3D NULL) { + char *p =3D strstr(arg, "=3D"); + array_size =3D atol(p+1); + continue; + } else if (strcmp (arg, "wait") =3D=3D 0) { + wait_if_ahead =3D 1; + continue; + } else if (strcmp(arg, "help") =3D=3D 0) { + printf("Tests: "); + for (j =3D 0; j < ARRAY_SIZE(tests); j++) { + printf("%s ", tests[j].test_name); + } + printf("\n"); + } + + for (j =3D 0; j < ARRAY_SIZE(tests); j++) { + if (strcmp(arg, tests[j].test_name) =3D=3D 0) { + res +=3D setup_and_run_litmus(&tests[j]); + continue; + } + } + } + + return res; +} --=20 2.17.1