From: Nico Boehr <nrb@linux.ibm.com>
To: kvm@vger.kernel.org
Cc: frankja@linux.ibm.com, imbrenda@linux.ibm.com, thuth@redhat.com,
nsg@linux.ibm.com
Subject: [kvm-unit-tests PATCH v5 1/1] s390x: add parallel skey migration test
Date: Tue, 20 Dec 2022 09:30:30 +0100 [thread overview]
Message-ID: <20221220083030.30153-2-nrb@linux.ibm.com> (raw)
In-Reply-To: <20221220083030.30153-1-nrb@linux.ibm.com>
Right now, we have a test which sets storage keys, then migrates the VM
and - after migration finished - verifies the skeys are still there.
Add a new version of the test which changes storage keys while the
migration is in progress. This is achieved by adding a command line
argument to the existing migration-skey test.
Signed-off-by: Nico Boehr <nrb@linux.ibm.com>
Reviewed-by: Nina Schoetterl-Glausch <nsg@linux.ibm.com>
Reviewed-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
---
s390x/migration-skey.c | 218 +++++++++++++++++++++++++++++++++++++----
s390x/unittests.cfg | 15 ++-
2 files changed, 210 insertions(+), 23 deletions(-)
diff --git a/s390x/migration-skey.c b/s390x/migration-skey.c
index a91eb6b5a63e..8d6d8ecfe61c 100644
--- a/s390x/migration-skey.c
+++ b/s390x/migration-skey.c
@@ -2,6 +2,12 @@
/*
* Storage Key migration tests
*
+ * There are two variants of this test:
+ * - sequential: set storage keys on some pages, migrates the VM and then
+ * verifies that the storage keys are still as we expect.
+ * - parallel: start migration and set and check storage keys on some
+ * pages while migration is in process.
+ *
* Copyright IBM Corp. 2022
*
* Authors:
@@ -13,16 +19,45 @@
#include <asm/facility.h>
#include <asm/page.h>
#include <asm/mem.h>
-#include <asm/interrupt.h>
+#include <asm/barrier.h>
#include <hardware.h>
+#include <smp.h>
+
+struct verify_result {
+ bool verify_failed;
+ union skey expected_key;
+ union skey actual_key;
+ unsigned long page_mismatch_idx;
+ unsigned long page_mismatch_addr;
+};
#define NUM_PAGES 128
-static uint8_t pagebuf[NUM_PAGES][PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
+static uint8_t pagebuf[NUM_PAGES * PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
+
+static struct verify_result result;
+
+static unsigned int thread_iters;
+static bool thread_should_exit;
+static bool thread_exited;
+
+static enum {
+ TEST_INVALID,
+ TEST_SEQUENTIAL,
+ TEST_PARALLEL
+} arg_test_to_run;
-static void test_migration(void)
+/*
+ * Set storage key test pattern on pagebuf with a seed for the storage keys.
+ *
+ * Each page's storage key is generated by taking the page's index in pagebuf,
+ * XOR-ing that with the given seed and then multipling the result with two.
+ *
+ * Only the lower seven bits of the seed are considered.
+ */
+static void set_test_pattern(unsigned char seed)
{
- union skey expected_key, actual_key;
- int i, key_to_set, key_mismatches = 0;
+ unsigned char key_to_set;
+ unsigned long i;
for (i = 0; i < NUM_PAGES; i++) {
/*
@@ -32,15 +67,34 @@ static void test_migration(void)
* protection as well as reference and change indication for
* some keys.
*/
- key_to_set = i * 2;
- set_storage_key(pagebuf[i], key_to_set, 1);
+ key_to_set = (i ^ seed) * 2;
+ set_storage_key(pagebuf + i * PAGE_SIZE, key_to_set, 1);
}
+}
- migrate_once();
+/*
+ * Verify storage keys on pagebuf.
+ * Storage keys must have been set by set_test_pattern on pagebuf before.
+ * set_test_pattern must have been called with the same seed value.
+ *
+ * If storage keys match the expected result, will return a verify_result
+ * with verify_failed false. All other fields are then invalid.
+ * If there is a mismatch, returned struct will have verify_failed true and will
+ * be filled with the details on the first mismatch encountered.
+ */
+static struct verify_result verify_test_pattern(unsigned char seed)
+{
+ union skey expected_key, actual_key;
+ struct verify_result result = {
+ .verify_failed = true
+ };
+ uint8_t *cur_page;
+ unsigned long i;
for (i = 0; i < NUM_PAGES; i++) {
- actual_key.val = get_storage_key(pagebuf[i]);
- expected_key.val = i * 2;
+ cur_page = pagebuf + i * PAGE_SIZE;
+ actual_key.val = get_storage_key(cur_page);
+ expected_key.val = (i ^ seed) * 2;
/*
* The PoP neither gives a guarantee that the reference bit is
@@ -51,27 +105,153 @@ static void test_migration(void)
actual_key.str.rf = 0;
expected_key.str.rf = 0;
- /* don't log anything when key matches to avoid spamming the log */
if (actual_key.val != expected_key.val) {
- key_mismatches++;
- report_fail("page %d expected_key=0x%x actual_key=0x%x", i, expected_key.val, actual_key.val);
+ result.expected_key.val = expected_key.val;
+ result.actual_key.val = actual_key.val;
+ result.page_mismatch_idx = i;
+ result.page_mismatch_addr = (unsigned long)cur_page;
+ return result;
}
}
- report(!key_mismatches, "skeys after migration match");
+ result.verify_failed = false;
+ return result;
+}
+
+static void report_verify_result(const struct verify_result * result)
+{
+ if (result->verify_failed)
+ report_fail("page skey mismatch: first page idx = %lu, addr = 0x%lx, "
+ "expected_key = 0x%02x, actual_key = 0x%02x",
+ result->page_mismatch_idx, result->page_mismatch_addr,
+ result->expected_key.val, result->actual_key.val);
+ else
+ report_pass("skeys match");
+}
+
+static void test_skey_migration_sequential(void)
+{
+ report_prefix_push("sequential");
+
+ set_test_pattern(0);
+
+ migrate_once();
+
+ result = verify_test_pattern(0);
+ report_verify_result(&result);
+
+ report_prefix_pop();
+}
+
+static void set_skeys_thread(void)
+{
+ while (!READ_ONCE(thread_should_exit)) {
+ set_test_pattern(thread_iters);
+
+ result = verify_test_pattern(thread_iters);
+
+ /*
+ * Always increment even if the verify fails. This ensures primary CPU knows where
+ * we left off and can do an additional verify round after migration finished.
+ */
+ thread_iters++;
+
+ if (result.verify_failed)
+ break;
+ }
+
+ WRITE_ONCE(thread_exited, 1);
+}
+
+static void test_skey_migration_parallel(void)
+{
+ report_prefix_push("parallel");
+
+ if (smp_query_num_cpus() == 1) {
+ report_skip("need at least 2 cpus for this test");
+ goto error;
+ }
+
+ smp_cpu_setup(1, PSW_WITH_CUR_MASK(set_skeys_thread));
+
+ migrate_once();
+
+ WRITE_ONCE(thread_should_exit, 1);
+
+ while (!READ_ONCE(thread_exited))
+ ;
+
+ /* Ensure we read result and thread_iters below from memory after thread exited */
+ mb();
+ report_info("thread completed %u iterations", thread_iters);
+
+ report_prefix_push("during migration");
+ report_verify_result(&result);
+ report_prefix_pop();
+
+ /*
+ * Verification of skeys occurs on the thread. We don't know if we
+ * were still migrating during the verification.
+ * To be sure, make another verification round after the migration
+ * finished to catch skeys which might not have been migrated
+ * correctly.
+ */
+ report_prefix_push("after migration");
+ assert(thread_iters > 0);
+ result = verify_test_pattern(thread_iters - 1);
+ report_verify_result(&result);
+ report_prefix_pop();
+
+error:
+ report_prefix_pop();
+}
+
+static void print_usage(void)
+{
+ report_info("Usage: migration-skey [--parallel|--sequential]");
+}
+
+static void parse_args(int argc, char **argv)
+{
+ if (argc < 2) {
+ /* default to sequential since it only needs one cpu */
+ arg_test_to_run = TEST_SEQUENTIAL;
+ return;
+ }
+
+ if (!strcmp("--parallel", argv[1]))
+ arg_test_to_run = TEST_PARALLEL;
+ else if (!strcmp("--sequential", argv[1]))
+ arg_test_to_run = TEST_SEQUENTIAL;
+ else
+ arg_test_to_run = TEST_INVALID;
}
-int main(void)
+int main(int argc, char **argv)
{
report_prefix_push("migration-skey");
- if (test_facility(169))
+ if (test_facility(169)) {
report_skip("storage key removal facility is active");
- else
- test_migration();
+ goto error;
+ }
- migrate_once();
+ parse_args(argc, argv);
+
+ switch (arg_test_to_run) {
+ case TEST_SEQUENTIAL:
+ test_skey_migration_sequential();
+ break;
+ case TEST_PARALLEL:
+ test_skey_migration_parallel();
+ break;
+ default:
+ print_usage();
+ break;
+ }
+error:
+ migrate_once();
report_prefix_pop();
return report_summary();
}
diff --git a/s390x/unittests.cfg b/s390x/unittests.cfg
index 3caf81eda396..d97eb5e943c8 100644
--- a/s390x/unittests.cfg
+++ b/s390x/unittests.cfg
@@ -185,10 +185,6 @@ smp = 2
file = migration-cmm.elf
groups = migration
-[migration-skey]
-file = migration-skey.elf
-groups = migration
-
[panic-loop-extint]
file = panic-loop-extint.elf
groups = panic
@@ -208,3 +204,14 @@ groups = migration
[exittime]
file = exittime.elf
smp = 2
+
+[migration-skey-sequential]
+file = migration-skey.elf
+groups = migration
+extra_params = -append '--sequential'
+
+[migration-skey-parallel]
+file = migration-skey.elf
+smp = 2
+groups = migration
+extra_params = -append '--parallel'
--
2.36.1
prev parent reply other threads:[~2022-12-20 8:30 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-12-20 8:30 [kvm-unit-tests PATCH v5 0/1] s390x: test storage keys during migration Nico Boehr
2022-12-20 8:30 ` Nico Boehr [this message]
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=20221220083030.30153-2-nrb@linux.ibm.com \
--to=nrb@linux.ibm.com \
--cc=frankja@linux.ibm.com \
--cc=imbrenda@linux.ibm.com \
--cc=kvm@vger.kernel.org \
--cc=nsg@linux.ibm.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.