From: Nicholas Piggin <npiggin@gmail.com>
To: Thomas Huth <thuth@redhat.com>
Cc: Nicholas Piggin <npiggin@gmail.com>,
Laurent Vivier <lvivier@redhat.com>,
Andrew Jones <andrew.jones@linux.dev>,
linuxppc-dev@lists.ozlabs.org, kvm@vger.kernel.org
Subject: [kvm-unit-tests PATCH v9 20/31] powerpc: Add atomics tests
Date: Sat, 4 May 2024 22:28:26 +1000 [thread overview]
Message-ID: <20240504122841.1177683-21-npiggin@gmail.com> (raw)
In-Reply-To: <20240504122841.1177683-1-npiggin@gmail.com>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
powerpc/Makefile.common | 1 +
powerpc/atomics.c | 375 ++++++++++++++++++++++++++++++++++++++++
powerpc/unittests.cfg | 9 +
3 files changed, 385 insertions(+)
create mode 100644 powerpc/atomics.c
diff --git a/powerpc/Makefile.common b/powerpc/Makefile.common
index 1ee9c25d6..420103c87 100644
--- a/powerpc/Makefile.common
+++ b/powerpc/Makefile.common
@@ -11,6 +11,7 @@ tests-common = \
$(TEST_DIR)/spapr_hcall.elf \
$(TEST_DIR)/rtas.elf \
$(TEST_DIR)/emulator.elf \
+ $(TEST_DIR)/atomics.elf \
$(TEST_DIR)/tm.elf \
$(TEST_DIR)/smp.elf \
$(TEST_DIR)/sprs.elf \
diff --git a/powerpc/atomics.c b/powerpc/atomics.c
new file mode 100644
index 000000000..975711fc8
--- /dev/null
+++ b/powerpc/atomics.c
@@ -0,0 +1,375 @@
+/* SPDX-License-Identifier: LGPL-2.0-only */
+/*
+ * Test some powerpc instructions
+ *
+ * Copyright 2024 Nicholas Piggin, IBM Corp.
+ */
+#include <stdint.h>
+#include <libcflat.h>
+#include <migrate.h>
+#include <asm/processor.h>
+#include <asm/time.h>
+#include <asm/atomic.h>
+#include <asm/setup.h>
+#include <asm/barrier.h>
+#include <asm/smp.h>
+
+static bool do_migrate;
+static bool do_record;
+
+#define RSV_SIZE 128
+
+static uint8_t granule[RSV_SIZE] __attribute((__aligned__(RSV_SIZE)));
+
+static void spin_lock(unsigned int *lock)
+{
+ unsigned int old;
+
+ asm volatile ("1:"
+ "lwarx %0,0,%2;"
+ "cmpwi %0,0;"
+ "bne 1b;"
+ "stwcx. %1,0,%2;"
+ "bne- 1b;"
+ "lwsync;"
+ : "=&r"(old) : "r"(1), "r"(lock) : "cr0", "memory");
+}
+
+static void spin_unlock(unsigned int *lock)
+{
+ asm volatile("lwsync;"
+ "stw %1,%0;"
+ : "+m"(*lock) : "r"(0) : "memory");
+}
+
+static volatile bool got_interrupt;
+static volatile struct pt_regs recorded_regs;
+
+static void interrupt_handler(struct pt_regs *regs, void *opaque)
+{
+ assert(!got_interrupt);
+ got_interrupt = true;
+ memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
+ regs_advance_insn(regs);
+}
+
+static void test_lwarx_stwcx(int argc, char *argv[])
+{
+ unsigned int *var = (unsigned int *)granule;
+ unsigned int old;
+ unsigned int result;
+
+ *var = 0;
+ asm volatile ("1:"
+ "lwarx %0,0,%2;"
+ "stwcx. %1,0,%2;"
+ "bne- 1b;"
+ : "=&r"(old) : "r"(1), "r"(var) : "cr0", "memory");
+ report(old == 0 && *var == 1, "simple update");
+
+ *var = 0;
+ asm volatile ("li %0,0;"
+ "stwcx. %1,0,%2;"
+ "stwcx. %1,0,%2;"
+ "bne- 1f;"
+ "li %0,1;"
+ "1:"
+ : "=&r"(result)
+ : "r"(1), "r"(var) : "cr0", "memory");
+ report(result == 0 && *var == 0, "failed stwcx. (no reservation)");
+
+ *var = 0;
+ asm volatile ("li %0,0;"
+ "lwarx %1,0,%4;"
+ "stw %3,0(%4);"
+ "stwcx. %2,0,%4;"
+ "bne- 1f;"
+ "li %0,1;"
+ "1:"
+ : "=&r"(result), "=&r"(old)
+ : "r"(1), "r"(2), "r"(var) : "cr0", "memory");
+ /* This is implementation specific, so don't fail */
+ if (result == 0 && *var == 2)
+ report(true, "failed stwcx. (intervening store)");
+ else
+ report(true, "succeeded stwcx. (intervening store)");
+
+ handle_exception(0x600, interrupt_handler, NULL);
+ handle_exception(0x700, interrupt_handler, NULL);
+
+ /* Implementations may not necessarily invoke the alignment interrupt */
+ old = 10;
+ *var = 0;
+ asm volatile (
+ "lwarx %0,0,%1;"
+ : "+&r"(old) : "r"((char *)var + 1));
+ report(old == 10 && got_interrupt && recorded_regs.trap == 0x600, "unaligned lwarx causes fault");
+ got_interrupt = false;
+
+ /*
+ * Unaligned stwcx. is more difficult to test, at least under QEMU,
+ * the store does not proceed if there is no matching reservation, so
+ * the alignment handler does not get invoked. This is okay according
+ * to the Power ISA (unalignment does not necessarily invoke the
+ * alignment interrupt). But POWER CPUs do cause alignment interrupt.
+ */
+ *var = 0;
+ asm volatile (
+ "lwarx %0,0,%2;"
+ "stwcx. %1,0,%3;"
+ : "=&r"(old) : "r"(1), "r"(var), "r"((char *)var+1) : "cr0", "memory");
+ /* unaligned larx/stcx. is not required by the ISA to cause an exception, in TCG the stcx does not. */
+ report_kfail(true, old == 0 && *var == 0 && got_interrupt && recorded_regs.trap == 0x600, "unaligned stwcx. causes fault");
+ got_interrupt = false;
+
+ handle_exception(0x600, NULL, NULL);
+
+}
+
+static void test_lqarx_stqcx(int argc, char *argv[])
+{
+ union {
+ __int128_t var;
+ struct {
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ unsigned long var1;
+ unsigned long var2;
+#else
+ unsigned long var2;
+ unsigned long var1;
+#endif
+ };
+ } var __attribute__((aligned(16)));
+ register unsigned long new1 asm("r8");
+ register unsigned long new2 asm("r9");
+ register unsigned long old1 asm("r10");
+ register unsigned long old2 asm("r11");
+ unsigned int result;
+
+ var.var1 = 1;
+ var.var2 = 2;
+
+ (void)new2;
+ (void)old2;
+
+ old1 = 0;
+ old2 = 0;
+ new1 = 3;
+ new2 = 4;
+ asm volatile ("1:"
+ "lqarx %0,0,%4;"
+ "stqcx. %2,0,%4;"
+ "bne- 1b;"
+ : "=&r"(old1), "=&r"(old2)
+ : "r"(new1), "r"(new2), "r"(&var)
+ : "cr0", "memory");
+
+ report(old1 == 2 && old2 == 1 && var.var1 == 4 && var.var2 == 3,
+ "simple update");
+
+ var.var1 = 1;
+ var.var2 = 2;
+ new1 = 3;
+ new2 = 4;
+ asm volatile ("li %0,0;"
+ "stqcx. %1,0,%3;"
+ "stqcx. %1,0,%3;"
+ "bne- 1f;"
+ "li %0,1;"
+ "1:"
+ : "=&r"(result)
+ : "r"(new1), "r"(new2), "r"(&var)
+ : "cr0", "memory");
+ report(result == 0 && var.var1 == 1 && var.var2 == 2,
+ "failed stqcx. (no reservation)");
+
+ var.var1 = 1;
+ var.var2 = 2;
+ new1 = 3;
+ new2 = 4;
+ asm volatile ("li %0,0;"
+ "lqarx %1,0,%6;"
+ "std %5,0(%6);"
+ "stqcx. %3,0,%6;"
+ "bne- 1f;"
+ "li %0,1;"
+ "1:"
+ : "=&r"(result), "=&r"(old1), "=&r"(old2)
+ : "r"(new1), "r"(new2), "r"(0), "r"(&var)
+ : "cr0", "memory");
+ /* This is implementation specific, so don't fail */
+ if (result == 0 && (var.var1 == 0 || var.var2 == 0))
+ report(true, "failed stqcx. (intervening store)");
+ else
+ report(true, "succeeded stqcx. (intervening store)");
+}
+
+static void test_migrate_reserve(int argc, char *argv[])
+{
+ unsigned int *var = (unsigned int *)granule;
+ unsigned int old;
+ int i;
+ int succeed = 0;
+
+ if (!do_migrate)
+ return;
+
+ for (i = 0; i < 10; i++) {
+ *var = 0x12345;
+ asm volatile ("lwarx %0,0,%1" : "=&r"(old) : "r"(var) : "memory");
+ migrate_quiet();
+ asm volatile ("stwcx. %0,0,%1" : : "r"(0xf00d), "r"(var) : "cr0", "memory");
+ if (*var == 0xf00d)
+ succeed++;
+ }
+
+ if (do_record) {
+ /*
+ * Running under TCG record-replay, reservations must not
+ * be lost by migration
+ */
+ report(succeed > 0, "migrated reservation is not lost");
+ } else {
+ report(succeed == 0, "migrated reservation is lost");
+ }
+
+ report_prefix_pop();
+}
+
+#define ITERS 10000000
+static int test_counter = 0;
+static void test_inc_perf(int argc, char *argv[])
+{
+ int i;
+ uint64_t tb1, tb2;
+
+ tb1 = get_tb();
+ for (i = 0; i < ITERS; i++)
+ __atomic_fetch_add(&test_counter, 1, __ATOMIC_RELAXED);
+ tb2 = get_tb();
+ report(true, "atomic add takes %ldns", (tb2 - tb1) * 1000000000 / ITERS / tb_hz);
+
+ tb1 = get_tb();
+ for (i = 0; i < ITERS; i++)
+ __atomic_fetch_add(&test_counter, 1, __ATOMIC_SEQ_CST);
+ tb2 = get_tb();
+ report(true, "sequentially conssistent atomic add takes %ldns", (tb2 - tb1) * 1000000000 / ITERS / tb_hz);
+}
+
+static long smp_inc_counter = 0;
+static int smp_inc_started;
+
+static void smp_inc_fn(int cpu_id)
+{
+ long i;
+
+ atomic_fetch_inc(&smp_inc_started);
+ while (smp_inc_started < nr_cpus_present)
+ cpu_relax();
+
+ for (i = 0; i < ITERS; i++)
+ atomic_fetch_inc(&smp_inc_counter);
+ atomic_fetch_dec(&smp_inc_started);
+}
+
+static void test_smp_inc(int argc, char **argv)
+{
+ if (nr_cpus_present < 2)
+ return;
+
+ if (!start_all_cpus(smp_inc_fn))
+ report_abort("Failed to start secondary cpus");
+
+ while (smp_inc_started < nr_cpus_present - 1)
+ cpu_relax();
+ smp_inc_fn(smp_processor_id());
+ while (smp_inc_started > 0)
+ cpu_relax();
+
+ stop_all_cpus();
+
+ report(smp_inc_counter == nr_cpus_present * ITERS, "counter lost no increments");
+}
+
+static long smp_lock_counter __attribute__((aligned(128))) = 0;
+static unsigned int smp_lock __attribute__((aligned(128)));
+static int smp_lock_started;
+
+static void smp_lock_fn(int cpu_id)
+{
+ long i;
+
+ atomic_fetch_inc(&smp_lock_started);
+ while (smp_lock_started < nr_cpus_present)
+ cpu_relax();
+
+ for (i = 0; i < ITERS; i++) {
+ spin_lock(&smp_lock);
+ smp_lock_counter++;
+ spin_unlock(&smp_lock);
+ }
+ atomic_fetch_dec(&smp_lock_started);
+}
+
+static void test_smp_lock(int argc, char **argv)
+{
+ if (nr_cpus_present < 2)
+ return;
+
+ if (!start_all_cpus(smp_lock_fn))
+ report_abort("Failed to start secondary cpus");
+
+ while (smp_lock_started < nr_cpus_present - 1)
+ cpu_relax();
+ smp_lock_fn(smp_processor_id());
+ while (smp_lock_started > 0)
+ cpu_relax();
+
+ stop_all_cpus();
+
+ report(smp_lock_counter == nr_cpus_present * ITERS, "counter lost no increments");
+}
+
+struct {
+ const char *name;
+ void (*func)(int argc, char **argv);
+} hctests[] = {
+ { "lwarx/stwcx", test_lwarx_stwcx },
+ { "lqarx/stqcx", test_lqarx_stqcx },
+ { "migration", test_migrate_reserve },
+ { "performance", test_inc_perf },
+ { "SMP-atomic", test_smp_inc },
+ { "SMP-lock", test_smp_lock },
+ { NULL, NULL }
+};
+
+int main(int argc, char **argv)
+{
+ int i;
+ int all;
+
+ all = argc == 1 || !strcmp(argv[1], "all");
+
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-r") == 0) {
+ do_record = true;
+ }
+ if (strcmp(argv[i], "-m") == 0) {
+ do_migrate = true;
+ }
+ }
+
+ report_prefix_push("atomics");
+
+ for (i = 0; hctests[i].name != NULL; i++) {
+ if (all || strcmp(argv[1], hctests[i].name) == 0) {
+ report_prefix_push(hctests[i].name);
+ hctests[i].func(argc, argv);
+ report_prefix_pop();
+ }
+ }
+
+ report_prefix_pop();
+
+ return report_summary();
+}
diff --git a/powerpc/unittests.cfg b/powerpc/unittests.cfg
index 5c458996b..a4210ab2a 100644
--- a/powerpc/unittests.cfg
+++ b/powerpc/unittests.cfg
@@ -83,6 +83,15 @@ file = smp.elf
smp = 8,threads=4
accel = tcg,thread=single
+[atomics]
+file = atomics.elf
+
+[atomics-migration]
+file = atomics.elf
+machine = pseries
+extra_params = -append "migration -m"
+groups = migration
+
[h_cede_tm]
file = tm.elf
machine = pseries
--
2.43.0
next prev parent reply other threads:[~2024-05-04 12:30 UTC|newest]
Thread overview: 49+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-05-04 12:28 [kvm-unit-tests PATCH v9 00/31] powerpc improvements Nicholas Piggin
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 01/31] doc: update unittests doc Nicholas Piggin
2024-05-06 7:03 ` Thomas Huth
2024-05-07 3:57 ` Nicholas Piggin
2024-05-06 8:02 ` Andrew Jones
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 02/31] report: Add known failure reporting option Nicholas Piggin
2024-05-06 7:25 ` Thomas Huth
2024-05-06 8:01 ` Andrew Jones
2024-05-06 10:19 ` Thomas Huth
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 03/31] powerpc: Mark known failing tests as kfail Nicholas Piggin
2024-05-06 7:37 ` Thomas Huth
2024-05-07 4:07 ` Nicholas Piggin
2024-05-07 11:44 ` Thomas Huth
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 04/31] powerpc: Update unittests for latest QEMU version Nicholas Piggin
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 05/31] powerpc/sprs: Specify SPRs with data rather than code Nicholas Piggin
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 06/31] powerpc/sprs: Avoid taking PMU interrupts caused by register fuzzing Nicholas Piggin
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 07/31] scripts: allow machine option to be specified in unittests.cfg Nicholas Piggin
2024-05-07 15:08 ` Thomas Huth
2024-05-08 12:27 ` Nicholas Piggin
2024-05-08 12:55 ` Thomas Huth
2024-05-08 12:58 ` Thomas Huth
2024-05-08 13:36 ` Thomas Huth
2024-05-09 5:44 ` Nicholas Piggin
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 08/31] scripts: Accommodate powerpc powernv machine differences Nicholas Piggin
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 09/31] powerpc: Support powernv machine with QEMU TCG Nicholas Piggin
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 10/31] powerpc: Fix emulator illegal instruction test for powernv Nicholas Piggin
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 11/31] powerpc/sprs: Test hypervisor registers on powernv machine Nicholas Piggin
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 12/31] powerpc: general interrupt tests Nicholas Piggin
2024-05-07 12:12 ` Thomas Huth
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 13/31] powerpc: Add rtas stop-self support Nicholas Piggin
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 14/31] powerpc: Remove broken SMP exception stack setup Nicholas Piggin
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 15/31] powerpc: Enable page alloc operations Nicholas Piggin
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 16/31] powerpc: add SMP and IPI support Nicholas Piggin
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 17/31] powerpc: Add cpu_relax Nicholas Piggin
2024-05-07 13:44 ` Thomas Huth
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 18/31] powerpc: Permit ACCEL=tcg,thread=single Nicholas Piggin
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 19/31] powerpc: Avoid using larx/stcx. in spinlocks when only one CPU is running Nicholas Piggin
2024-05-04 12:28 ` Nicholas Piggin [this message]
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 21/31] powerpc: Add timebase tests Nicholas Piggin
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 22/31] powerpc: Add MMU support Nicholas Piggin
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 23/31] common/sieve: Use vmalloc.h for setup_mmu definition Nicholas Piggin
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 24/31] common/sieve: Support machines without MMU Nicholas Piggin
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 25/31] powerpc: Add sieve.c common test Nicholas Piggin
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 26/31] powerpc: add usermode support Nicholas Piggin
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 27/31] powerpc: add pmu tests Nicholas Piggin
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 28/31] configure: Make arch_libdir a first-class entity Nicholas Piggin
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 29/31] powerpc: Remove remnants of ppc64 directory and build structure Nicholas Piggin
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 30/31] powerpc: Add facility to query TCG or KVM host Nicholas Piggin
2024-05-04 12:28 ` [kvm-unit-tests PATCH v9 31/31] powerpc: gitlab CI update Nicholas Piggin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240504122841.1177683-21-npiggin@gmail.com \
--to=npiggin@gmail.com \
--cc=andrew.jones@linux.dev \
--cc=kvm@vger.kernel.org \
--cc=linuxppc-dev@lists.ozlabs.org \
--cc=lvivier@redhat.com \
--cc=thuth@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).