From patchwork Thu Nov 24 16:10:31 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: 9446025 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 9F6E260779 for ; Thu, 24 Nov 2016 16:18:53 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 91120280D0 for ; Thu, 24 Nov 2016 16:18:53 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 85190280F4; Thu, 24 Nov 2016 16:18:53 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.1 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_MED,T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 64CB1280D0 for ; Thu, 24 Nov 2016 16:18:52 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.85_2 #1 (Red Hat Linux)) id 1c9whl-0000yN-5V; Thu, 24 Nov 2016 16:16:57 +0000 Received: from mail-wm0-x22e.google.com ([2a00:1450:400c:c09::22e]) by bombadil.infradead.org with esmtps (Exim 4.85_2 #1 (Red Hat Linux)) id 1c9wcV-0003UP-9r for linux-arm-kernel@lists.infradead.org; Thu, 24 Nov 2016 16:11:39 +0000 Received: by mail-wm0-x22e.google.com with SMTP id a197so119262599wmd.0 for ; Thu, 24 Nov 2016 08:11:10 -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=evlQj66uVdgqnajVw/hzLgJks8QAMAYvhGBQjw50oHg=; b=VOPvjmkDoInermBMONw4XJeGBpJKblyQZSWLtulWXj1PXNBo/ya1a4FMr/XAwsX4yP ZXc8dCDSsxOAYYILRfxr4iBOqzyBfrKbfWrCkq5GDQXzzEeEJ9uAJJvqSwbrSwx2z6l9 hg3O7vdQ/DFv4fQ34sNA8/CmTda608QLRj+TQ= 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=evlQj66uVdgqnajVw/hzLgJks8QAMAYvhGBQjw50oHg=; b=cCvCbM/6Gwc/DtPgLgJ62VWP2UxrFrcwgf0bSDeIBt7/Uk2g5SYtkyhj02QhzMnCFL 4F0pKPqnxY0a7b4We9nFwQo+1OAvZO5v3raUZRIhz8nDpsd7azsaXfDm88pdQKimKBCI f8VqiUqUBuTAJTJEJMCAZJiU22OGfgqr4shz3+6myeEzaMpt52WLxAiN4Wf4EEMbrOrp u5DYleAuJVguam9L/taoovmntlbYDKjZNMo7OFk4hQJ3fdidnZP65h7V+u7sZDDjbrOX AXUGwCN7mI19LN0DgLw6mtm0TIHmUlQUR0kzAmz9OEa6e3uAV1gMl0luPn/SnBHt6HFT iKkA== X-Gm-Message-State: AKaTC02vz5ze5FOe1ZlmklDl8L8eD4ooqz3Pg5CRYRXNyD1zKKrOOe72ORI0CS1ucr1oU4m7 X-Received: by 10.28.169.74 with SMTP id s71mr3104000wme.1.1480003869192; Thu, 24 Nov 2016 08:11:09 -0800 (PST) Received: from zen.linaro.local ([81.128.185.34]) by smtp.gmail.com with ESMTPSA id t82sm8934916wmd.17.2016.11.24.08.11.03 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 24 Nov 2016 08:11:06 -0800 (PST) Received: from zen.linaroharston (localhost [127.0.0.1]) by zen.linaro.local (Postfix) with ESMTP id 3A5623E057B; Thu, 24 Nov 2016 16:11:00 +0000 (GMT) From: =?UTF-8?q?Alex=20Benn=C3=A9e?= To: kvm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, kvmarm@lists.cs.columbia.edu, christoffer.dall@linaro.org, marc.zyngier@arm.com Subject: [kvm-unit-tests PATCH v7 09/11] arm/locking-tests: add comprehensive locking test Date: Thu, 24 Nov 2016 16:10:31 +0000 Message-Id: <20161124161033.11456-10-alex.bennee@linaro.org> X-Mailer: git-send-email 2.10.1 In-Reply-To: <20161124161033.11456-1-alex.bennee@linaro.org> References: <20161124161033.11456-1-alex.bennee@linaro.org> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20161124_081131_938259_B4B02465 X-CRM114-Status: GOOD ( 24.69 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: mttcg@listserver.greensocs.com, peter.maydell@linaro.org, claudio.fontana@huawei.com, nikunj@linux.vnet.ibm.com, jan.kiszka@siemens.com, mark.burton@greensocs.com, a.rigo@virtualopensystems.com, qemu-devel@nongnu.org, cota@braap.org, serge.fdrv@gmail.com, pbonzini@redhat.com, bobby.prani@gmail.com, rth@twiddle.net, =?UTF-8?q?Alex=20Benn=C3=A9e?= , fred.konrad@greensocs.com Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP This test has been written mainly to stress multi-threaded TCG behaviour but will demonstrate failure by default on real hardware. The test takes the following parameters: - "lock" use GCC's locking semantics - "atomic" use GCC's __atomic primitives - "wfelock" use WaitForEvent sleep - "excl" use load/store exclusive semantics Also two more options allow the test to be tweaked - "noshuffle" disables the memory shuffling - "count=%ld" set your own per-CPU increment count Signed-off-by: Alex Bennée --- v2 - Don't use thumb style strexeq stuff - Add atomic and wfelock tests - Add count/noshuffle test controls - Move barrier tests to separate test file v4 - fix up unitests.cfg to use correct test name - move into "locking" group, remove barrier tests - use a table to add tests, mark which are expected to work - correctly report XFAIL v5 - max out at -smp 4 in unittest.cfg v7 - make test control flags bools - default the count to 100000 (so it doesn't timeout) --- arm/Makefile.common | 2 + arm/locking-test.c | 302 ++++++++++++++++++++++++++++++++++++++++++++++++++++ arm/unittests.cfg | 34 ++++++ 3 files changed, 338 insertions(+) create mode 100644 arm/locking-test.c diff --git a/arm/Makefile.common b/arm/Makefile.common index 528166d..eb4cfdf 100644 --- a/arm/Makefile.common +++ b/arm/Makefile.common @@ -15,6 +15,7 @@ tests-common += $(TEST_DIR)/pci-test.flat tests-common += $(TEST_DIR)/gic.flat tests-common += $(TEST_DIR)/tlbflush-code.flat tests-common += $(TEST_DIR)/tlbflush-data.flat +tests-common += $(TEST_DIR)/locking-test.flat all: test_cases @@ -85,3 +86,4 @@ test_cases: $(generated_files) $(tests-common) $(tests) $(TEST_DIR)/selftest.o $(cstart.o): $(asm-offsets) $(TEST_DIR)/tlbflush-code.elf: $(cstart.o) $(TEST_DIR)/tlbflush-code.o $(TEST_DIR)/tlbflush-data.elf: $(cstart.o) $(TEST_DIR)/tlbflush-data.o +$(TEST_DIR)/locking-test.elf: $(cstart.o) $(TEST_DIR)/locking-test.o diff --git a/arm/locking-test.c b/arm/locking-test.c new file mode 100644 index 0000000..f10c61b --- /dev/null +++ b/arm/locking-test.c @@ -0,0 +1,302 @@ +#include +#include +#include +#include +#include + +#include + +#define MAX_CPUS 8 + +/* Test definition structure + * + * A simple structure that describes the test name, expected pass and + * increment function. + */ + +/* Function pointers for test */ +typedef void (*inc_fn)(int cpu); + +typedef struct { + const char *test_name; + bool should_pass; + inc_fn main_fn; +} test_descr_t; + +/* How many increments to do */ +static int increment_count = 1000000; +static bool do_shuffle = true; + +/* Shared value all the tests attempt to safely increment using + * various forms of atomic locking and exclusive behaviour. + */ +static unsigned int shared_value; + +/* PAGE_SIZE * uint32_t means we span several pages */ +__attribute__((aligned(PAGE_SIZE))) static uint32_t memory_array[PAGE_SIZE]; + +/* We use the alignment of the following to ensure accesses to locking + * and synchronisation primatives don't interfere with the page of the + * shared value + */ +__attribute__((aligned(PAGE_SIZE))) static unsigned int per_cpu_value[MAX_CPUS]; +__attribute__((aligned(PAGE_SIZE))) static cpumask_t smp_test_complete; +__attribute__((aligned(PAGE_SIZE))) struct isaac_ctx prng_context[MAX_CPUS]; + +/* Some of the approaches use a global lock to prevent contention. */ +static int global_lock; + +/* In any SMP setting this *should* fail due to cores stepping on + * each other updating the shared variable + */ +static void increment_shared(int cpu) +{ + (void)cpu; + + shared_value++; +} + +/* GCC __sync primitives are deprecated in favour of __atomic */ +static void increment_shared_with_lock(int cpu) +{ + (void)cpu; + + while (__sync_lock_test_and_set(&global_lock, 1)); + shared_value++; + __sync_lock_release(&global_lock); +} + +/* In practice even __ATOMIC_RELAXED uses ARM's ldxr/stex exclusive + * semantics */ +static void increment_shared_with_atomic(int cpu) +{ + (void)cpu; + + __atomic_add_fetch(&shared_value, 1, __ATOMIC_SEQ_CST); +} + + +/* + * Load/store exclusive with WFE (wait-for-event) + * + * See ARMv8 ARM examples: + * Use of Wait For Event (WFE) and Send Event (SEV) with locks + */ + +static void increment_shared_with_wfelock(int cpu) +{ + (void)cpu; + +#if defined(__aarch64__) + asm volatile( + " mov w1, #1\n" + " sevl\n" + " prfm PSTL1KEEP, [%[lock]]\n" + "1: wfe\n" + " ldaxr w0, [%[lock]]\n" + " cbnz w0, 1b\n" + " stxr w0, w1, [%[lock]]\n" + " cbnz w0, 1b\n" + /* lock held */ + " ldr w0, [%[sptr]]\n" + " add w0, w0, #0x1\n" + " str w0, [%[sptr]]\n" + /* now release */ + " stlr wzr, [%[lock]]\n" + : /* out */ + : [lock] "r" (&global_lock), [sptr] "r" (&shared_value) /* in */ + : "w0", "w1", "cc"); +#else + asm volatile( + " mov r1, #1\n" + "1: ldrex r0, [%[lock]]\n" + " cmp r0, #0\n" + " wfene\n" + " strexeq r0, r1, [%[lock]]\n" + " cmpeq r0, #0\n" + " bne 1b\n" + " dmb\n" + /* lock held */ + " ldr r0, [%[sptr]]\n" + " add r0, r0, #0x1\n" + " str r0, [%[sptr]]\n" + /* now release */ + " mov r0, #0\n" + " dmb\n" + " str r0, [%[lock]]\n" + " dsb\n" + " sev\n" + : /* out */ + : [lock] "r" (&global_lock), [sptr] "r" (&shared_value) /* in */ + : "r0", "r1", "cc"); +#endif +} + + +/* + * Hand-written version of the load/store exclusive + */ +static void increment_shared_with_excl(int cpu) +{ + (void)cpu; + +#if defined(__aarch64__) + asm volatile( + "1: ldxr w0, [%[sptr]]\n" + " add w0, w0, #0x1\n" + " stxr w1, w0, [%[sptr]]\n" + " cbnz w1, 1b\n" + : /* out */ + : [sptr] "r" (&shared_value) /* in */ + : "w0", "w1", "cc"); +#else + asm volatile( + "1: ldrex r0, [%[sptr]]\n" + " add r0, r0, #0x1\n" + " strex r1, r0, [%[sptr]]\n" + " cmp r1, #0\n" + " bne 1b\n" + : /* out */ + : [sptr] "r" (&shared_value) /* in */ + : "r0", "r1", "cc"); +#endif +} + +/* Test array */ +static test_descr_t tests[] = { + { "none", false, increment_shared }, + { "lock", true, increment_shared_with_lock }, + { "atomic", true, increment_shared_with_atomic }, + { "wfelock", true, increment_shared_with_wfelock }, + { "excl", true, increment_shared_with_excl } +}; + +/* The idea of this is just to generate some random load/store + * activity which may or may not race with an un-barried incremented + * of the shared counter + */ +static void shuffle_memory(int cpu) +{ + int i; + uint32_t lspat = isaac_next_uint32(&prng_context[cpu]); + uint32_t seq = isaac_next_uint32(&prng_context[cpu]); + int count = seq & 0x1f; + uint32_t val=0; + + seq >>= 5; + + for (i=0; i>= PAGE_SHIFT; + seq ^= lspat; + lspat >>= 1; + } + +} + +static inc_fn increment_function; + +static void do_increment(void) +{ + int i; + int cpu = smp_processor_id(); + + printf("CPU%d: online and ++ing\n", cpu); + + for (i=0; i < increment_count; i++) { + per_cpu_value[cpu]++; + increment_function(cpu); + + if (do_shuffle) + shuffle_memory(cpu); + } + + printf("CPU%d: Done, %d incs\n", cpu, per_cpu_value[cpu]); + + cpumask_set_cpu(cpu, &smp_test_complete); + if (cpu != 0) + halt(); +} + +static void setup_and_run_test(test_descr_t *test) +{ + unsigned int i, sum = 0; + int cpu, cpu_cnt = 0; + + increment_function = test->main_fn; + + /* fill our random page */ + for (i=0; ishould_pass) { + report("total incs %d", sum == shared_value, shared_value); + } else { + report_xfail("total incs %d", true, sum == shared_value, shared_value); + } +} + +int main(int argc, char **argv) +{ + static const unsigned char seed[] = "myseed"; + test_descr_t *test = &tests[0]; + int i; + unsigned int j; + + isaac_init(&prng_context[0], &seed[0], sizeof(seed)); + + for (i=0; i4?4:$MAX_SMP)) extra_params = -append "page" groups = tlbflush +# Locking tests +[locking::none] +file = locking-test.flat +smp = $(($MAX_SMP>4?4:$MAX_SMP)) +groups = locking +accel = tcg + +[locking::lock] +file = locking-test.flat +smp = $(($MAX_SMP>4?4:$MAX_SMP)) +extra_params = -append 'lock' +groups = locking +accel = tcg + +[locking::atomic] +file = locking-test.flat +smp = $(($MAX_SMP>4?4:$MAX_SMP)) +extra_params = -append 'atomic' +groups = locking +accel = tcg + +[locking::wfelock] +file = locking-test.flat +smp = $(($MAX_SMP>4?4:$MAX_SMP)) +extra_params = -append 'wfelock' +groups = locking +accel = tcg + +[locking::excl] +file = locking-test.flat +smp = $(($MAX_SMP>4?4:$MAX_SMP)) +extra_params = -append 'excl' +groups = locking +accel = tcg