From patchwork Fri Feb 26 13:15:32 2016 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: 63049 Delivered-To: patch@linaro.org Received: by 10.112.199.169 with SMTP id jl9csp713393lbc; Fri, 26 Feb 2016 05:19:17 -0800 (PST) X-Received: by 10.140.30.4 with SMTP id c4mr1876359qgc.14.1456492754616; Fri, 26 Feb 2016 05:19:14 -0800 (PST) Return-Path: Received: from lists.gnu.org (lists.gnu.org. [2001:4830:134:3::11]) by mx.google.com with ESMTPS id 53si13107769qgt.99.2016.02.26.05.19.14 for (version=TLS1 cipher=AES128-SHA bits=128/128); Fri, 26 Feb 2016 05:19:14 -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; 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; dkim=fail header.i=@linaro.org Received: from localhost ([::1]:49712 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aZIIb-0004mV-Sr for patch@linaro.org; Fri, 26 Feb 2016 08:19:13 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:33416) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aZIFO-0008OQ-TK for qemu-devel@nongnu.org; Fri, 26 Feb 2016 08:15:59 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1aZIFN-0004Fy-8L for qemu-devel@nongnu.org; Fri, 26 Feb 2016 08:15:54 -0500 Received: from mail-wm0-x231.google.com ([2a00:1450:400c:c09::231]:36250) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1aZIFM-0004Fu-Ua for qemu-devel@nongnu.org; Fri, 26 Feb 2016 08:15:53 -0500 Received: by mail-wm0-x231.google.com with SMTP id g62so72422241wme.1 for ; Fri, 26 Feb 2016 05:15:52 -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=DnOBttCC0E4TR4wOCKGpexaDK6QSJ22d65Zv4kS3sNc=; b=jrOdhw9qJpJOo5xto5hb+wn7Ry9y+w1N12XlcNOk/6R1QBAbtmN0yk3CeDb2Yc+ebn 8875PaWvKCv1rQ2KJvscU9CI6VXkB2sJ34zf72XGklq/V8S10TUDAYwaq86qELTSxlZm XERRchZ2bEJ2VsXcVLbVSkZPfCiuJeIq7j5TU= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=DnOBttCC0E4TR4wOCKGpexaDK6QSJ22d65Zv4kS3sNc=; b=UlKqYkyHEhBgPO0oSMruW2BYVDo66UUEIDSv/NhstIflMGwXJvwtF63qE5RLPkEz2R lSY2QNnH1smWJ9CDHK/W42ACWWZgD6SjXsM9C9hVbOEtG3xnCa/ZAGEnXgOQXpPP63Jh Doqb4tMjtJnPmVOSngIFsispNPM9HKWx/UGXLmIttW4v/XOh84yctAATyy4gRKgZcOd3 BAQIahL8Yyytvtfm4mVwux1UnknRaqvTUF4e02wSGEO+EtSSnRiEgUe803JCJl9cAT1P 6EN/4/AsMSXKiMh2TIbujql9WLs3YRcb07NFHP9lyja0TMbt4hltCmE9wLVDLRULVWcm v2HA== X-Gm-Message-State: AD7BkJJBIZZ3ZzDAoBz80SdqNi8OyivCefFW9MUBMGlS9bkUZKzoV8DPghH2lyofY/wCVvpL X-Received: by 10.28.14.4 with SMTP id 4mr3284767wmo.100.1456492552403; Fri, 26 Feb 2016 05:15:52 -0800 (PST) Received: from zen.linaro.local ([81.128.185.34]) by smtp.gmail.com with ESMTPSA id ks5sm12457480wjb.13.2016.02.26.05.15.45 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 26 Feb 2016 05:15:46 -0800 (PST) Received: from zen.linaroharston (localhost [127.0.0.1]) by zen.linaro.local (Postfix) with ESMTP id 929643E0329; Fri, 26 Feb 2016 13:15:41 +0000 (GMT) From: =?UTF-8?q?Alex=20Benn=C3=A9e?= To: mttcg@listserver.greensocs.com, mark.burton@greensocs.com, fred.konrad@greensocs.com, a.rigo@virtualopensystems.com Date: Fri, 26 Feb 2016 13:15:32 +0000 Message-Id: <1456492533-17171-11-git-send-email-alex.bennee@linaro.org> X-Mailer: git-send-email 2.7.1 In-Reply-To: <1456492533-17171-1-git-send-email-alex.bennee@linaro.org> References: <1456492533-17171-1-git-send-email-alex.bennee@linaro.org> MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2a00:1450:400c:c09::231 Cc: peter.maydell@linaro.org, drjones@redhat.com, a.spyridakis@virtualopensystems.com, claudio.fontana@huawei.com, qemu-devel@nongnu.org, will.deacon@arm.com, crosthwaitepeter@gmail.com, pbonzini@redhat.com, =?UTF-8?q?Alex=20Benn=C3=A9e?= , aurelien@aurel32.net, rth@twiddle.net Subject: [Qemu-devel] [RFC 10/11] arm/barrier-litmus-tests: add some litmus tests X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+patch=linaro.org@nongnu.org Sender: qemu-devel-bounces+patch=linaro.org@nongnu.org This adds a framework for adding simple barrier litmus tests against ARM. The litmus tests aren't as comprehensive as the academic exercises which will attempt to do all sorts of things to keep racing CPUs synced up. These tests do honour the "sync" parameter to do a poor-mans equivilent. I've imported a few more of the barrier primatives from the Linux source tree so we consistently use macros. Signed-off-by: Alex Bennée --- - add a unittest.cfg --- arm/barrier-litmus-test.c | 258 +++++++++++++++++++++++++++++++++++++++++++ arm/unittests.cfg | 21 ++++ config/config-arm-common.mak | 2 + lib/arm/asm/barrier.h | 63 ++++++++++- lib/arm64/asm/barrier.h | 50 +++++++++ 5 files changed, 393 insertions(+), 1 deletion(-) create mode 100644 arm/barrier-litmus-test.c -- 2.7.1 diff --git a/arm/barrier-litmus-test.c b/arm/barrier-litmus-test.c new file mode 100644 index 0000000..45cdcdc --- /dev/null +++ b/arm/barrier-litmus-test.c @@ -0,0 +1,258 @@ +/* + * 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 + +#define MAX_CPUS 8 + +/* 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 int z; +} test_array; + +volatile test_array *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 void (*test_fn)(void); + +typedef struct { + const char *test_name; + bool should_pass; + test_fn main_fn; + test_fn secondary_fns[MAX_CPUS-1]; +} test_descr_t; + +/* 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. + */ + +void message_passing_write(void) +{ + int i; + for (i=0; i< array_size; i++) { + volatile test_array *entry = &array[i]; + entry->x = 1; + entry->y = 1; + } + + halt(); +} + +void message_passing_read(void) +{ + int i; + int errors = 0, ready = 0; + + 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; + } + + report_xfail("mp: %d errors, %d ready", true, errors == 0, errors, ready); +} + +/* Simple Message Passing with barriers */ +void message_passing_write_barrier(void) +{ + int i; + for (i=0; i< array_size; i++) { + volatile test_array *entry = &array[i]; + entry->x = 1; + smp_wmb(); + entry->y = 1; + } + + halt(); +} + +void message_passing_read_barrier(void) +{ + int i; + int errors = 0, ready = 0, not_ready = 0; + + 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); + } + } + } + + report("mp barrier: %d errors, %d ready", errors == 0, errors, ready); +} + +/* Simple Message Passing with Acquire/Release */ +void message_passing_write_release(void) +{ + int i; + for (i=0; i< array_size; i++) { + volatile test_array *entry = &array[i]; + entry->x = 1; + smp_store_release(&entry->y, 1); + } + + halt(); +} + +void message_passing_read_acquire(void) +{ + int i; + int errors = 0, ready = 0, not_ready = 0; + + for (i=0; i< array_size; i++) { + volatile test_array *entry = &array[i]; + unsigned int x, y; + y = smp_load_acquire(&entry->y); + 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); + } + } + } + + report("mp acqrel: %d errors, %d ready", errors == 0, errors, ready); +} + + +/* 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 } + } +}; + + +void setup_and_run_litmus(test_descr_t *test) +{ + array = calloc(array_size, sizeof(test_array)); + + if (array) { + int i = 0; + printf("Allocated test array @ %p\n", array); + + while (test->secondary_fns[i]) { + smp_boot_secondary(i+1, test->secondary_fns[i]); + i++; + } + + test->main_fn(); + } else { + report("%s: failed to allocate memory",false, test->test_name); + } +} + +int main(int argc, char **argv) +{ + int i; + unsigned int j; + test_descr_t *test = NULL; + + for (i=0; i + #define sev() asm volatile("sev" : : : "memory") #define wfe() asm volatile("wfe" : : : "memory") #define wfi() asm volatile("wfi" : : : "memory") @@ -20,4 +22,63 @@ #define smp_rmb() smp_mb() #define smp_wmb() dmb(ishst) +extern void abort(void); + +static inline void __write_once_size(volatile void *p, void *res, int size) +{ + switch (size) { + case 1: *(volatile uint8_t *)p = *(uint8_t *)res; break; + case 2: *(volatile uint16_t *)p = *(uint16_t *)res; break; + case 4: *(volatile uint32_t *)p = *(uint32_t *)res; break; + case 8: *(volatile uint64_t *)p = *(uint64_t *)res; break; + default: + /* unhandled case */ + abort(); + } +} + +#define WRITE_ONCE(x, val) \ +({ \ + union { typeof(x) __val; char __c[1]; } __u = \ + { .__val = (typeof(x)) (val) }; \ + __write_once_size(&(x), __u.__c, sizeof(x)); \ + __u.__val; \ +}) + +#define smp_store_release(p, v) \ +do { \ + smp_mb(); \ + WRITE_ONCE(*p, v); \ +} while (0) + + +static inline +void __read_once_size(const volatile void *p, void *res, int size) +{ + switch (size) { + case 1: *(uint8_t *)res = *(volatile uint8_t *)p; break; + case 2: *(uint16_t *)res = *(volatile uint16_t *)p; break; + case 4: *(uint32_t *)res = *(volatile uint32_t *)p; break; + case 8: *(uint64_t *)res = *(volatile uint64_t *)p; break; + default: + /* unhandled case */ + abort(); + } +} + +#define READ_ONCE(x) \ +({ \ + union { typeof(x) __val; char __c[1]; } __u; \ + __read_once_size(&(x), __u.__c, sizeof(x)); \ + __u.__val; \ +}) + + +#define smp_load_acquire(p) \ +({ \ + typeof(*p) ___p1 = READ_ONCE(*p); \ + smp_mb(); \ + ___p1; \ +}) + #endif /* _ASMARM_BARRIER_H_ */ diff --git a/lib/arm64/asm/barrier.h b/lib/arm64/asm/barrier.h index dbdac9d..aafabdc 100644 --- a/lib/arm64/asm/barrier.h +++ b/lib/arm64/asm/barrier.h @@ -19,4 +19,54 @@ #define smp_rmb() dmb(ishld) #define smp_wmb() dmb(ishst) +#define smp_store_release(p, v) \ +do { \ + switch (sizeof(*p)) { \ + case 1: \ + asm volatile ("stlrb %w1, %0" \ + : "=Q" (*p) : "r" (v) : "memory"); \ + break; \ + case 2: \ + asm volatile ("stlrh %w1, %0" \ + : "=Q" (*p) : "r" (v) : "memory"); \ + break; \ + case 4: \ + asm volatile ("stlr %w1, %0" \ + : "=Q" (*p) : "r" (v) : "memory"); \ + break; \ + case 8: \ + asm volatile ("stlr %1, %0" \ + : "=Q" (*p) : "r" (v) : "memory"); \ + break; \ + } \ +} while (0) + +#define smp_load_acquire(p) \ +({ \ + union { typeof(*p) __val; char __c[1]; } __u; \ + switch (sizeof(*p)) { \ + case 1: \ + asm volatile ("ldarb %w0, %1" \ + : "=r" (*(u8 *)__u.__c) \ + : "Q" (*p) : "memory"); \ + break; \ + case 2: \ + asm volatile ("ldarh %w0, %1" \ + : "=r" (*(u16 *)__u.__c) \ + : "Q" (*p) : "memory"); \ + break; \ + case 4: \ + asm volatile ("ldar %w0, %1" \ + : "=r" (*(u32 *)__u.__c) \ + : "Q" (*p) : "memory"); \ + break; \ + case 8: \ + asm volatile ("ldar %0, %1" \ + : "=r" (*(u64 *)__u.__c) \ + : "Q" (*p) : "memory"); \ + break; \ + } \ + __u.__val; \ +}) + #endif /* _ASMARM64_BARRIER_H_ */