diff mbox series

[kvm-unit-tests,v3,6/6] s390x: SMP test

Message ID 20190920080356.1948-7-frankja@linux.ibm.com
State New, archived
Headers show
Series s390x: Add multiboot and smp | expand

Commit Message

Janosch Frank Sept. 20, 2019, 8:03 a.m. UTC
Testing SIGP emulation for the following order codes:
* start
* stop
* restart
* set prefix
* store status
* stop and store status
* reset
* initial reset
* external call
* emegergency call

restart and set prefix are part of the library and needed to start
other cpus.

Signed-off-by: Janosch Frank <frankja@linux.ibm.com>
---
 s390x/Makefile      |   1 +
 s390x/smp.c         | 242 ++++++++++++++++++++++++++++++++++++++++++++
 s390x/unittests.cfg |   4 +
 3 files changed, 247 insertions(+)
 create mode 100644 s390x/smp.c

Comments

Thomas Huth Sept. 25, 2019, 8:49 a.m. UTC | #1
On 20/09/2019 10.03, Janosch Frank wrote:
> Testing SIGP emulation for the following order codes:
> * start
> * stop
> * restart
> * set prefix
> * store status
> * stop and store status
> * reset
> * initial reset
> * external call
> * emegergency call
> 
> restart and set prefix are part of the library and needed to start
> other cpus.
> 
> Signed-off-by: Janosch Frank <frankja@linux.ibm.com>
> ---
>  s390x/Makefile      |   1 +
>  s390x/smp.c         | 242 ++++++++++++++++++++++++++++++++++++++++++++
[...]
> +int main(void)
> +{
> +	report_prefix_push("smp");
> +
> +	if (smp_query_num_cpus() == 1) {
> +		report_abort("need at least 2 cpus for this test");

nit: report_abort() aborts immediately, so the "goto done" below is dead
code. Did you mean report_skip() instead?

> +		goto done;
> +	}
> +
> +	test_start();
> +	test_stop();
> +	test_stop_store_status();
> +	test_store_status();
> +	test_ecall();
> +	test_emcall();
> +	test_reset();
> +	test_reset_initial();
> +
> +done:
> +	report_prefix_pop();
> +	return report_summary();
> +}

Apart from the nit, the patch looks fine to me. Since this is IIRC the
only nit that is left in this series, I can also fix it up when picking
up the patch, if you like - just tell me whether you prefer report_skip
or deletion of the goto.

 Thomas
David Hildenbrand Sept. 25, 2019, 9:03 a.m. UTC | #2
On 20.09.19 10:03, Janosch Frank wrote:
> Testing SIGP emulation for the following order codes:
> * start
> * stop
> * restart
> * set prefix
> * store status
> * stop and store status
> * reset
> * initial reset
> * external call
> * emegergency call
> 
> restart and set prefix are part of the library and needed to start
> other cpus.
> 
> Signed-off-by: Janosch Frank <frankja@linux.ibm.com>
> ---
>  s390x/Makefile      |   1 +
>  s390x/smp.c         | 242 ++++++++++++++++++++++++++++++++++++++++++++
>  s390x/unittests.cfg |   4 +
>  3 files changed, 247 insertions(+)
>  create mode 100644 s390x/smp.c
> 
> diff --git a/s390x/Makefile b/s390x/Makefile
> index d83dd0b..3744372 100644
> --- a/s390x/Makefile
> +++ b/s390x/Makefile
> @@ -15,6 +15,7 @@ tests += $(TEST_DIR)/cpumodel.elf
>  tests += $(TEST_DIR)/diag288.elf
>  tests += $(TEST_DIR)/stsi.elf
>  tests += $(TEST_DIR)/skrf.elf
> +tests += $(TEST_DIR)/smp.elf
>  tests_binary = $(patsubst %.elf,%.bin,$(tests))
>  
>  all: directories test_cases test_cases_binary
> diff --git a/s390x/smp.c b/s390x/smp.c
> new file mode 100644
> index 0000000..7032494
> --- /dev/null
> +++ b/s390x/smp.c
> @@ -0,0 +1,242 @@
> +/*
> + * Tests sigp emulation
> + *
> + * Copyright 2019 IBM Corp.
> + *
> + * Authors:
> + *    Janosch Frank <frankja@linux.ibm.com>
> + *
> + * This code is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2.
> + */
> +#include <libcflat.h>
> +#include <asm/asm-offsets.h>
> +#include <asm/interrupt.h>
> +#include <asm/page.h>
> +#include <asm/facility.h>
> +#include <asm-generic/barrier.h>
> +#include <asm/sigp.h>
> +
> +#include <smp.h>
> +#include <alloc_page.h>
> +
> +static int testflag = 0;
> +
> +static void cpu_loop(void)
> +{
> +	for (;;) {}
> +}
> +
> +static void test_func(void)
> +{
> +	testflag = 1;
> +	mb();
> +	cpu_loop();
> +}
> +
> +static void test_start(void)
> +{
> +	struct psw psw;
> +	psw.mask =  extract_psw_mask();
> +	psw.addr = (unsigned long)test_func;
> +
> +	smp_cpu_setup(1, psw);
> +	while (!testflag) {
> +		mb();
> +	}
> +	report("start", 1);
> +}
> +
> +static void test_stop(void)
> +{
> +	smp_cpu_stop(1);
> +	/*
> +	 * The smp library waits for the CPU to shut down, but let's
> +	 * also do it here, so we don't rely on the library
> +	 * implementation
> +	 */
> +	while (!smp_cpu_stopped(1)) {}
> +	report("stop", 1);
> +}
> +
> +static void test_stop_store_status(void)
> +{
> +	struct cpu *cpu = smp_cpu_from_addr(1);
> +	struct lowcore *lc = (void *)0x0;
> +
> +	report_prefix_push("stop store status");
> +	lc->prefix_sa = 0;
> +	lc->grs_sa[15] = 0;
> +	smp_cpu_stop_store_status(1);
> +	mb();
> +	report("prefix", lc->prefix_sa == (uint32_t)(uintptr_t)cpu->lowcore);
> +	report("stack", lc->grs_sa[15]);
> +	report_prefix_pop();
> +}
> +
> +static void test_store_status(void)
> +{
> +	struct cpu_status *status = alloc_pages(1);
> +	uint32_t r;
> +
> +	report_prefix_push("store status at address");
> +	memset(status, 0, PAGE_SIZE * 2);
> +
> +	report_prefix_push("running");
> +	smp_cpu_restart(1);
> +	sigp(1, SIGP_STORE_STATUS_AT_ADDRESS, (uintptr_t)status, &r);
> +	report("incorrect state", r == SIGP_STATUS_INCORRECT_STATE);
> +	report("status not written", !memcmp(status, (void*)status + PAGE_SIZE, PAGE_SIZE));
> +	report_prefix_pop();
> +
> +	memset(status, 0, PAGE_SIZE);
> +	report_prefix_push("stopped");
> +	smp_cpu_stop(1);
> +	sigp(1, SIGP_STORE_STATUS_AT_ADDRESS, (uintptr_t)status, NULL);
> +	while (!status->prefix) { mb(); }
> +	report("status written", 1);
> +	free_pages(status, PAGE_SIZE * 2);
> +	report_prefix_pop();
> +
> +	report_prefix_pop();
> +}
> +
> +static void ecall(void)
> +{
> +	unsigned long mask;
> +	struct lowcore *lc = (void *)0x0;
> +
> +	expect_ext_int();
> +	ctl_set_bit(0, 13);
> +	mask = extract_psw_mask();
> +	mask |= PSW_MASK_EXT;
> +	load_psw_mask(mask);
> +	testflag = 1;
> +	while (lc->ext_int_code != 0x1202) { mb(); }
> +	report("ecall", 1);
> +	testflag= 1;
> +}
> +
> +static void test_ecall(void)
> +{
> +	struct psw psw;
> +	psw.mask =  extract_psw_mask();
> +	psw.addr = (unsigned long)ecall;
> +
> +	report_prefix_push("ecall");
> +	testflag= 0;
> +	smp_cpu_destroy(1);
> +
> +	smp_cpu_setup(1, psw);
> +	while (!testflag) { mb(); }
> +	testflag= 0;
> +	sigp(1, SIGP_EXTERNAL_CALL, 0, NULL);
> +	while(!testflag) {mb();}
> +	smp_cpu_stop(1);
> +	report_prefix_pop();
> +}
> +
> +static void emcall(void)
> +{
> +	unsigned long mask;
> +	struct lowcore *lc = (void *)0x0;
> +
> +	expect_ext_int();
> +	ctl_set_bit(0, 14);
> +	mask = extract_psw_mask();
> +	mask |= PSW_MASK_EXT;
> +	load_psw_mask(mask);
> +	testflag= 1;
> +	while (lc->ext_int_code != 0x1201) { mb(); }
> +	report("ecall", 1);
> +	testflag = 1;
> +}
> +
> +static void test_emcall(void)
> +{
> +	struct psw psw;
> +	psw.mask =  extract_psw_mask();
> +	psw.addr = (unsigned long)emcall;
> +
> +	report_prefix_push("emcall");
> +	testflag= 0;
> +	smp_cpu_destroy(1);
> +
> +	smp_cpu_setup(1, psw);
> +	while (!testflag) { mb(); }
> +	testflag= 0;
> +	sigp(1, SIGP_EMERGENCY_SIGNAL, 0, NULL);
> +	while(!testflag) { mb(); }
> +	smp_cpu_stop(1);
> +	report_prefix_pop();
> +}
> +
> +static void test_reset_initial(void)
> +{
> +	struct cpu_status *status = alloc_pages(0);
> +	struct psw psw;
> +
> +	psw.mask =  extract_psw_mask();
> +	psw.addr = (unsigned long)test_func;
> +
> +	report_prefix_push("reset initial");
> +	smp_cpu_setup(1, psw);
> +
> +	sigp_retry(1, SIGP_INITIAL_CPU_RESET, 0, NULL);
> +	sigp(1, SIGP_STORE_STATUS_AT_ADDRESS, (uintptr_t)status, NULL);
> +
> +	report_prefix_push("clear");
> +	report("psw", !status->psw.mask && !status->psw.addr);
> +	report("prefix", !status->prefix);
> +	report("fpc", !status->fpc);
> +	report("cpu timer", !status->cputm);
> +	report("todpr", !status->todpr);
> +	report_prefix_pop();
> +
> +	report_prefix_push("initialized");
> +	report("cr0 == 0xE0", status->crs[0] == 0xE0UL);
> +	report("cr14 == 0xC2000000", status->crs[14] == 0xC2000000UL);
> +	report_prefix_pop();
> +
> +	report("cpu stopped", smp_cpu_stopped(1));
> +	free_pages(status, PAGE_SIZE);
> +	report_prefix_pop();
> +}
> +
> +static void test_reset(void)
> +{
> +	struct psw psw;
> +
> +	psw.mask =  extract_psw_mask();
> +	psw.addr = (unsigned long)test_func;
> +
> +	report_prefix_push("cpu reset");
> +	smp_cpu_setup(1, psw);
> +
> +	sigp_retry(1, SIGP_CPU_RESET, 0, NULL);
> +	report("cpu stopped", smp_cpu_stopped(1));
> +	report_prefix_pop();
> +}
> +
> +int main(void)
> +{
> +	report_prefix_push("smp");
> +
> +	if (smp_query_num_cpus() == 1) {
> +		report_abort("need at least 2 cpus for this test");
> +		goto done;
> +	}

You should rather sense if CPU with the addr 1 is available. AFAIR, the
number of CPUs and the CPU addresses are two types of shoes.
Janosch Frank Sept. 25, 2019, 10:24 a.m. UTC | #3
On 9/25/19 11:03 AM, David Hildenbrand wrote:
> On 20.09.19 10:03, Janosch Frank wrote:

>> +
>> +int main(void)
>> +{
>> +	report_prefix_push("smp");
>> +
>> +	if (smp_query_num_cpus() == 1) {
>> +		report_abort("need at least 2 cpus for this test");
>> +		goto done;
>> +	}
> 
> You should rather sense if CPU with the addr 1 is available. AFAIR, the
> number of CPUs and the CPU addresses are two types of shoes.
> 

Yes and no, if we don't have more than one cpu we certainly don't have
cpu 1. And for what I've seen up to now QEMU assigns cpus linearly.
Janosch Frank Sept. 25, 2019, 10:26 a.m. UTC | #4
On 9/25/19 10:49 AM, Thomas Huth wrote:
> On 20/09/2019 10.03, Janosch Frank wrote:
>> Testing SIGP emulation for the following order codes:
>> * start
>> * stop
>> * restart
>> * set prefix
>> * store status
>> * stop and store status
>> * reset
>> * initial reset
>> * external call
>> * emegergency call
>>
>> restart and set prefix are part of the library and needed to start
>> other cpus.
>>
>> Signed-off-by: Janosch Frank <frankja@linux.ibm.com>
>> ---
>>  s390x/Makefile      |   1 +
>>  s390x/smp.c         | 242 ++++++++++++++++++++++++++++++++++++++++++++
> [...]
>> +int main(void)
>> +{
>> +	report_prefix_push("smp");
>> +
>> +	if (smp_query_num_cpus() == 1) {
>> +		report_abort("need at least 2 cpus for this test");
> 
> nit: report_abort() aborts immediately, so the "goto done" below is dead
> code. Did you mean report_skip() instead?

That would make more sense, yes

> 
>> +		goto done;
>> +	}
>> +
>> +	test_start();
>> +	test_stop();
>> +	test_stop_store_status();
>> +	test_store_status();
>> +	test_ecall();
>> +	test_emcall();
>> +	test_reset();
>> +	test_reset_initial();
>> +
>> +done:
>> +	report_prefix_pop();
>> +	return report_summary();
>> +}
> 
> Apart from the nit, the patch looks fine to me. Since this is IIRC the
> only nit that is left in this series, I can also fix it up when picking
> up the patch, if you like - just tell me whether you prefer report_skip
> or deletion of the goto.

Well, that might depend on how much weight David's latest comment has.
:-) If you want to, you can pick and fix.

> 
>  Thomas
>
David Hildenbrand Sept. 25, 2019, 1:27 p.m. UTC | #5
On 20.09.19 10:03, Janosch Frank wrote:
> Testing SIGP emulation for the following order codes:
> * start
> * stop
> * restart
> * set prefix
> * store status
> * stop and store status
> * reset
> * initial reset
> * external call
> * emegergency call
> 
> restart and set prefix are part of the library and needed to start
> other cpus.
> 
> Signed-off-by: Janosch Frank <frankja@linux.ibm.com>
> ---
>  s390x/Makefile      |   1 +
>  s390x/smp.c         | 242 ++++++++++++++++++++++++++++++++++++++++++++
>  s390x/unittests.cfg |   4 +
>  3 files changed, 247 insertions(+)
>  create mode 100644 s390x/smp.c
> 
> diff --git a/s390x/Makefile b/s390x/Makefile
> index d83dd0b..3744372 100644
> --- a/s390x/Makefile
> +++ b/s390x/Makefile
> @@ -15,6 +15,7 @@ tests += $(TEST_DIR)/cpumodel.elf
>  tests += $(TEST_DIR)/diag288.elf
>  tests += $(TEST_DIR)/stsi.elf
>  tests += $(TEST_DIR)/skrf.elf
> +tests += $(TEST_DIR)/smp.elf
>  tests_binary = $(patsubst %.elf,%.bin,$(tests))
>  
>  all: directories test_cases test_cases_binary
> diff --git a/s390x/smp.c b/s390x/smp.c
> new file mode 100644
> index 0000000..7032494
> --- /dev/null
> +++ b/s390x/smp.c
> @@ -0,0 +1,242 @@
> +/*
> + * Tests sigp emulation
> + *
> + * Copyright 2019 IBM Corp.
> + *
> + * Authors:
> + *    Janosch Frank <frankja@linux.ibm.com>
> + *
> + * This code is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2.
> + */
> +#include <libcflat.h>
> +#include <asm/asm-offsets.h>
> +#include <asm/interrupt.h>
> +#include <asm/page.h>
> +#include <asm/facility.h>
> +#include <asm-generic/barrier.h>
> +#include <asm/sigp.h>
> +
> +#include <smp.h>
> +#include <alloc_page.h>
> +
> +static int testflag = 0;
> +
> +static void cpu_loop(void)
> +{
> +	for (;;) {}

Won't that be optimized out completely?

> +}
> +
> +static void test_func(void)
> +{
> +	testflag = 1;
> +	mb();
> +	cpu_loop();
> +}
> +
> +static void test_start(void)
> +{
> +	struct psw psw;
> +	psw.mask =  extract_psw_mask();
> +	psw.addr = (unsigned long)test_func;
> +
> +	smp_cpu_setup(1, psw);
> +	while (!testflag) {
> +		mb();
> +	}
> +	report("start", 1);
> +}
> +
> +static void test_stop(void)
> +{
> +	smp_cpu_stop(1);
> +	/*
> +	 * The smp library waits for the CPU to shut down, but let's
> +	 * also do it here, so we don't rely on the library
> +	 * implementation
> +	 */
> +	while (!smp_cpu_stopped(1)) {}
> +	report("stop", 1);
> +}
> +
> +static void test_stop_store_status(void)
> +{
> +	struct cpu *cpu = smp_cpu_from_addr(1);
> +	struct lowcore *lc = (void *)0x0;
> +
> +	report_prefix_push("stop store status");
> +	lc->prefix_sa = 0;
> +	lc->grs_sa[15] = 0;
> +	smp_cpu_stop_store_status(1);
> +	mb();
> +	report("prefix", lc->prefix_sa == (uint32_t)(uintptr_t)cpu->lowcore);
> +	report("stack", lc->grs_sa[15]);
> +	report_prefix_pop();
> +}
> +
> +static void test_store_status(void)
> +{
> +	struct cpu_status *status = alloc_pages(1);
> +	uint32_t r;
> +
> +	report_prefix_push("store status at address");
> +	memset(status, 0, PAGE_SIZE * 2);
> +
> +	report_prefix_push("running");
> +	smp_cpu_restart(1);
> +	sigp(1, SIGP_STORE_STATUS_AT_ADDRESS, (uintptr_t)status, &r);
> +	report("incorrect state", r == SIGP_STATUS_INCORRECT_STATE);
> +	report("status not written", !memcmp(status, (void*)status + PAGE_SIZE, PAGE_SIZE));
> +	report_prefix_pop();
> +
> +	memset(status, 0, PAGE_SIZE);
> +	report_prefix_push("stopped");
> +	smp_cpu_stop(1);
> +	sigp(1, SIGP_STORE_STATUS_AT_ADDRESS, (uintptr_t)status, NULL);
> +	while (!status->prefix) { mb(); }
> +	report("status written", 1);
> +	free_pages(status, PAGE_SIZE * 2);
> +	report_prefix_pop();
> +
> +	report_prefix_pop();
> +}
> +
> +static void ecall(void)
> +{
> +	unsigned long mask;
> +	struct lowcore *lc = (void *)0x0;
> +
> +	expect_ext_int();
> +	ctl_set_bit(0, 13);
> +	mask = extract_psw_mask();
> +	mask |= PSW_MASK_EXT;
> +	load_psw_mask(mask);
> +	testflag = 1;
> +	while (lc->ext_int_code != 0x1202) { mb(); }
> +	report("ecall", 1);
> +	testflag= 1;
> +}
> +
> +static void test_ecall(void)
> +{
> +	struct psw psw;
> +	psw.mask =  extract_psw_mask();
> +	psw.addr = (unsigned long)ecall;
> +
> +	report_prefix_push("ecall");
> +	testflag= 0;
> +	smp_cpu_destroy(1);
> +
> +	smp_cpu_setup(1, psw);
> +	while (!testflag) { mb(); }
> +	testflag= 0;
> +	sigp(1, SIGP_EXTERNAL_CALL, 0, NULL);
> +	while(!testflag) {mb();}
> +	smp_cpu_stop(1);
> +	report_prefix_pop();
> +}
> +
> +static void emcall(void)
> +{
> +	unsigned long mask;
> +	struct lowcore *lc = (void *)0x0;
> +
> +	expect_ext_int();
> +	ctl_set_bit(0, 14);
> +	mask = extract_psw_mask();
> +	mask |= PSW_MASK_EXT;
> +	load_psw_mask(mask);
> +	testflag= 1;
> +	while (lc->ext_int_code != 0x1201) { mb(); }
> +	report("ecall", 1);
> +	testflag = 1;
> +}
> +
> +static void test_emcall(void)
> +{
> +	struct psw psw;
> +	psw.mask =  extract_psw_mask();
> +	psw.addr = (unsigned long)emcall;
> +
> +	report_prefix_push("emcall");
> +	testflag= 0;
> +	smp_cpu_destroy(1);
> +
> +	smp_cpu_setup(1, psw);
> +	while (!testflag) { mb(); }
> +	testflag= 0;
> +	sigp(1, SIGP_EMERGENCY_SIGNAL, 0, NULL);
> +	while(!testflag) { mb(); }
> +	smp_cpu_stop(1);
> +	report_prefix_pop();
> +}
> +
> +static void test_reset_initial(void)
> +{
> +	struct cpu_status *status = alloc_pages(0);
> +	struct psw psw;
> +
> +	psw.mask =  extract_psw_mask();
> +	psw.addr = (unsigned long)test_func;
> +
> +	report_prefix_push("reset initial");
> +	smp_cpu_setup(1, psw);
> +
> +	sigp_retry(1, SIGP_INITIAL_CPU_RESET, 0, NULL);
> +	sigp(1, SIGP_STORE_STATUS_AT_ADDRESS, (uintptr_t)status, NULL);
> +
> +	report_prefix_push("clear");
> +	report("psw", !status->psw.mask && !status->psw.addr);
> +	report("prefix", !status->prefix);
> +	report("fpc", !status->fpc);
> +	report("cpu timer", !status->cputm);
> +	report("todpr", !status->todpr);
> +	report_prefix_pop();
> +
> +	report_prefix_push("initialized");
> +	report("cr0 == 0xE0", status->crs[0] == 0xE0UL);
> +	report("cr14 == 0xC2000000", status->crs[14] == 0xC2000000UL);
> +	report_prefix_pop();
> +
> +	report("cpu stopped", smp_cpu_stopped(1));
> +	free_pages(status, PAGE_SIZE);
> +	report_prefix_pop();
> +}
> +
> +static void test_reset(void)
> +{
> +	struct psw psw;
> +
> +	psw.mask =  extract_psw_mask();
> +	psw.addr = (unsigned long)test_func;
> +
> +	report_prefix_push("cpu reset");
> +	smp_cpu_setup(1, psw);
> +
> +	sigp_retry(1, SIGP_CPU_RESET, 0, NULL);
> +	report("cpu stopped", smp_cpu_stopped(1));
> +	report_prefix_pop();
> +}
> +
> +int main(void)
> +{
> +	report_prefix_push("smp");
> +
> +	if (smp_query_num_cpus() == 1) {
> +		report_abort("need at least 2 cpus for this test");
> +		goto done;
> +	}
> +
> +	test_start();
> +	test_stop();
> +	test_stop_store_status();
> +	test_store_status();
> +	test_ecall();
> +	test_emcall();
> +	test_reset();
> +	test_reset_initial();
> +
> +done:
> +	report_prefix_pop();
> +	return report_summary();
> +}
> diff --git a/s390x/unittests.cfg b/s390x/unittests.cfg
> index cc79a4e..f1b07cd 100644
> --- a/s390x/unittests.cfg
> +++ b/s390x/unittests.cfg
> @@ -71,3 +71,7 @@ extra_params=-device diag288,id=watchdog0 --watchdog-action inject-nmi
>  
>  [stsi]
>  file = stsi.elf
> +
> +[smp]
> +file = smp.elf
> +extra_params =-smp 2
>
Thomas Huth Sept. 25, 2019, 1:30 p.m. UTC | #6
On 25/09/2019 15.27, David Hildenbrand wrote:
> On 20.09.19 10:03, Janosch Frank wrote:
>> Testing SIGP emulation for the following order codes:
>> * start
>> * stop
>> * restart
>> * set prefix
>> * store status
>> * stop and store status
>> * reset
>> * initial reset
>> * external call
>> * emegergency call
>>
>> restart and set prefix are part of the library and needed to start
>> other cpus.
>>
>> Signed-off-by: Janosch Frank <frankja@linux.ibm.com>
>> ---
>>  s390x/Makefile      |   1 +
>>  s390x/smp.c         | 242 ++++++++++++++++++++++++++++++++++++++++++++
>>  s390x/unittests.cfg |   4 +
>>  3 files changed, 247 insertions(+)
>>  create mode 100644 s390x/smp.c
>>
>> diff --git a/s390x/Makefile b/s390x/Makefile
>> index d83dd0b..3744372 100644
>> --- a/s390x/Makefile
>> +++ b/s390x/Makefile
>> @@ -15,6 +15,7 @@ tests += $(TEST_DIR)/cpumodel.elf
>>  tests += $(TEST_DIR)/diag288.elf
>>  tests += $(TEST_DIR)/stsi.elf
>>  tests += $(TEST_DIR)/skrf.elf
>> +tests += $(TEST_DIR)/smp.elf
>>  tests_binary = $(patsubst %.elf,%.bin,$(tests))
>>  
>>  all: directories test_cases test_cases_binary
>> diff --git a/s390x/smp.c b/s390x/smp.c
>> new file mode 100644
>> index 0000000..7032494
>> --- /dev/null
>> +++ b/s390x/smp.c
>> @@ -0,0 +1,242 @@
>> +/*
>> + * Tests sigp emulation
>> + *
>> + * Copyright 2019 IBM Corp.
>> + *
>> + * Authors:
>> + *    Janosch Frank <frankja@linux.ibm.com>
>> + *
>> + * This code is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License version 2.
>> + */
>> +#include <libcflat.h>
>> +#include <asm/asm-offsets.h>
>> +#include <asm/interrupt.h>
>> +#include <asm/page.h>
>> +#include <asm/facility.h>
>> +#include <asm-generic/barrier.h>
>> +#include <asm/sigp.h>
>> +
>> +#include <smp.h>
>> +#include <alloc_page.h>
>> +
>> +static int testflag = 0;
>> +
>> +static void cpu_loop(void)
>> +{
>> +	for (;;) {}
> 
> Won't that be optimized out completely?

Why? AFAIK this is the standard way to write and endless loop ... how
can a compiler optimize that away?

 Thomas
David Hildenbrand Sept. 25, 2019, 1:32 p.m. UTC | #7
On 25.09.19 15:30, Thomas Huth wrote:
> On 25/09/2019 15.27, David Hildenbrand wrote:
>> On 20.09.19 10:03, Janosch Frank wrote:
>>> Testing SIGP emulation for the following order codes:
>>> * start
>>> * stop
>>> * restart
>>> * set prefix
>>> * store status
>>> * stop and store status
>>> * reset
>>> * initial reset
>>> * external call
>>> * emegergency call
>>>
>>> restart and set prefix are part of the library and needed to start
>>> other cpus.
>>>
>>> Signed-off-by: Janosch Frank <frankja@linux.ibm.com>
>>> ---
>>>  s390x/Makefile      |   1 +
>>>  s390x/smp.c         | 242 ++++++++++++++++++++++++++++++++++++++++++++
>>>  s390x/unittests.cfg |   4 +
>>>  3 files changed, 247 insertions(+)
>>>  create mode 100644 s390x/smp.c
>>>
>>> diff --git a/s390x/Makefile b/s390x/Makefile
>>> index d83dd0b..3744372 100644
>>> --- a/s390x/Makefile
>>> +++ b/s390x/Makefile
>>> @@ -15,6 +15,7 @@ tests += $(TEST_DIR)/cpumodel.elf
>>>  tests += $(TEST_DIR)/diag288.elf
>>>  tests += $(TEST_DIR)/stsi.elf
>>>  tests += $(TEST_DIR)/skrf.elf
>>> +tests += $(TEST_DIR)/smp.elf
>>>  tests_binary = $(patsubst %.elf,%.bin,$(tests))
>>>  
>>>  all: directories test_cases test_cases_binary
>>> diff --git a/s390x/smp.c b/s390x/smp.c
>>> new file mode 100644
>>> index 0000000..7032494
>>> --- /dev/null
>>> +++ b/s390x/smp.c
>>> @@ -0,0 +1,242 @@
>>> +/*
>>> + * Tests sigp emulation
>>> + *
>>> + * Copyright 2019 IBM Corp.
>>> + *
>>> + * Authors:
>>> + *    Janosch Frank <frankja@linux.ibm.com>
>>> + *
>>> + * This code is free software; you can redistribute it and/or modify it
>>> + * under the terms of the GNU General Public License version 2.
>>> + */
>>> +#include <libcflat.h>
>>> +#include <asm/asm-offsets.h>
>>> +#include <asm/interrupt.h>
>>> +#include <asm/page.h>
>>> +#include <asm/facility.h>
>>> +#include <asm-generic/barrier.h>
>>> +#include <asm/sigp.h>
>>> +
>>> +#include <smp.h>
>>> +#include <alloc_page.h>
>>> +
>>> +static int testflag = 0;
>>> +
>>> +static void cpu_loop(void)
>>> +{
>>> +	for (;;) {}
>>
>> Won't that be optimized out completely?
> 
> Why? AFAIK this is the standard way to write and endless loop ... how
> can a compiler optimize that away?

Was messing it up with "just" an empty loop body, I think you're right.
David Hildenbrand Sept. 25, 2019, 1:35 p.m. UTC | #8
On 25.09.19 15:32, David Hildenbrand wrote:
> On 25.09.19 15:30, Thomas Huth wrote:
>> On 25/09/2019 15.27, David Hildenbrand wrote:
>>> On 20.09.19 10:03, Janosch Frank wrote:
>>>> Testing SIGP emulation for the following order codes:
>>>> * start
>>>> * stop
>>>> * restart
>>>> * set prefix
>>>> * store status
>>>> * stop and store status
>>>> * reset
>>>> * initial reset
>>>> * external call
>>>> * emegergency call
>>>>
>>>> restart and set prefix are part of the library and needed to start
>>>> other cpus.
>>>>
>>>> Signed-off-by: Janosch Frank <frankja@linux.ibm.com>
>>>> ---
>>>>  s390x/Makefile      |   1 +
>>>>  s390x/smp.c         | 242 ++++++++++++++++++++++++++++++++++++++++++++
>>>>  s390x/unittests.cfg |   4 +
>>>>  3 files changed, 247 insertions(+)
>>>>  create mode 100644 s390x/smp.c
>>>>
>>>> diff --git a/s390x/Makefile b/s390x/Makefile
>>>> index d83dd0b..3744372 100644
>>>> --- a/s390x/Makefile
>>>> +++ b/s390x/Makefile
>>>> @@ -15,6 +15,7 @@ tests += $(TEST_DIR)/cpumodel.elf
>>>>  tests += $(TEST_DIR)/diag288.elf
>>>>  tests += $(TEST_DIR)/stsi.elf
>>>>  tests += $(TEST_DIR)/skrf.elf
>>>> +tests += $(TEST_DIR)/smp.elf
>>>>  tests_binary = $(patsubst %.elf,%.bin,$(tests))
>>>>  
>>>>  all: directories test_cases test_cases_binary
>>>> diff --git a/s390x/smp.c b/s390x/smp.c
>>>> new file mode 100644
>>>> index 0000000..7032494
>>>> --- /dev/null
>>>> +++ b/s390x/smp.c
>>>> @@ -0,0 +1,242 @@
>>>> +/*
>>>> + * Tests sigp emulation
>>>> + *
>>>> + * Copyright 2019 IBM Corp.
>>>> + *
>>>> + * Authors:
>>>> + *    Janosch Frank <frankja@linux.ibm.com>
>>>> + *
>>>> + * This code is free software; you can redistribute it and/or modify it
>>>> + * under the terms of the GNU General Public License version 2.
>>>> + */
>>>> +#include <libcflat.h>
>>>> +#include <asm/asm-offsets.h>
>>>> +#include <asm/interrupt.h>
>>>> +#include <asm/page.h>
>>>> +#include <asm/facility.h>
>>>> +#include <asm-generic/barrier.h>
>>>> +#include <asm/sigp.h>
>>>> +
>>>> +#include <smp.h>
>>>> +#include <alloc_page.h>
>>>> +
>>>> +static int testflag = 0;
>>>> +
>>>> +static void cpu_loop(void)
>>>> +{
>>>> +	for (;;) {}
>>>
>>> Won't that be optimized out completely?
>>
>> Why? AFAIK this is the standard way to write and endless loop ... how
>> can a compiler optimize that away?
> 
> Was messing it up with "just" an empty loop body, I think you're right.
> 

However

https://stackoverflow.com/questions/2178115/are-compilers-allowed-to-eliminate-infinite-loops

"This is intended to allow compiler transformations such as removal of
empty loops even when termination cannot be proven."

I think this might get optimized out.
David Hildenbrand Sept. 25, 2019, 1:39 p.m. UTC | #9
On 25.09.19 15:35, David Hildenbrand wrote:
> On 25.09.19 15:32, David Hildenbrand wrote:
>> On 25.09.19 15:30, Thomas Huth wrote:
>>> On 25/09/2019 15.27, David Hildenbrand wrote:
>>>> On 20.09.19 10:03, Janosch Frank wrote:
>>>>> Testing SIGP emulation for the following order codes:
>>>>> * start
>>>>> * stop
>>>>> * restart
>>>>> * set prefix
>>>>> * store status
>>>>> * stop and store status
>>>>> * reset
>>>>> * initial reset
>>>>> * external call
>>>>> * emegergency call
>>>>>
>>>>> restart and set prefix are part of the library and needed to start
>>>>> other cpus.
>>>>>
>>>>> Signed-off-by: Janosch Frank <frankja@linux.ibm.com>
>>>>> ---
>>>>>  s390x/Makefile      |   1 +
>>>>>  s390x/smp.c         | 242 ++++++++++++++++++++++++++++++++++++++++++++
>>>>>  s390x/unittests.cfg |   4 +
>>>>>  3 files changed, 247 insertions(+)
>>>>>  create mode 100644 s390x/smp.c
>>>>>
>>>>> diff --git a/s390x/Makefile b/s390x/Makefile
>>>>> index d83dd0b..3744372 100644
>>>>> --- a/s390x/Makefile
>>>>> +++ b/s390x/Makefile
>>>>> @@ -15,6 +15,7 @@ tests += $(TEST_DIR)/cpumodel.elf
>>>>>  tests += $(TEST_DIR)/diag288.elf
>>>>>  tests += $(TEST_DIR)/stsi.elf
>>>>>  tests += $(TEST_DIR)/skrf.elf
>>>>> +tests += $(TEST_DIR)/smp.elf
>>>>>  tests_binary = $(patsubst %.elf,%.bin,$(tests))
>>>>>  
>>>>>  all: directories test_cases test_cases_binary
>>>>> diff --git a/s390x/smp.c b/s390x/smp.c
>>>>> new file mode 100644
>>>>> index 0000000..7032494
>>>>> --- /dev/null
>>>>> +++ b/s390x/smp.c
>>>>> @@ -0,0 +1,242 @@
>>>>> +/*
>>>>> + * Tests sigp emulation
>>>>> + *
>>>>> + * Copyright 2019 IBM Corp.
>>>>> + *
>>>>> + * Authors:
>>>>> + *    Janosch Frank <frankja@linux.ibm.com>
>>>>> + *
>>>>> + * This code is free software; you can redistribute it and/or modify it
>>>>> + * under the terms of the GNU General Public License version 2.
>>>>> + */
>>>>> +#include <libcflat.h>
>>>>> +#include <asm/asm-offsets.h>
>>>>> +#include <asm/interrupt.h>
>>>>> +#include <asm/page.h>
>>>>> +#include <asm/facility.h>
>>>>> +#include <asm-generic/barrier.h>
>>>>> +#include <asm/sigp.h>
>>>>> +
>>>>> +#include <smp.h>
>>>>> +#include <alloc_page.h>
>>>>> +
>>>>> +static int testflag = 0;
>>>>> +
>>>>> +static void cpu_loop(void)
>>>>> +{
>>>>> +	for (;;) {}
>>>>
>>>> Won't that be optimized out completely?
>>>
>>> Why? AFAIK this is the standard way to write and endless loop ... how
>>> can a compiler optimize that away?
>>
>> Was messing it up with "just" an empty loop body, I think you're right.
>>
> 
> However
> 
> https://stackoverflow.com/questions/2178115/are-compilers-allowed-to-eliminate-infinite-loops
> 
> "This is intended to allow compiler transformations such as removal of
> empty loops even when termination cannot be proven."
> 
> I think this might get optimized out.
> 

... but then, everybody does it, so it is most probably fine with GCC.
diff mbox series

Patch

diff --git a/s390x/Makefile b/s390x/Makefile
index d83dd0b..3744372 100644
--- a/s390x/Makefile
+++ b/s390x/Makefile
@@ -15,6 +15,7 @@  tests += $(TEST_DIR)/cpumodel.elf
 tests += $(TEST_DIR)/diag288.elf
 tests += $(TEST_DIR)/stsi.elf
 tests += $(TEST_DIR)/skrf.elf
+tests += $(TEST_DIR)/smp.elf
 tests_binary = $(patsubst %.elf,%.bin,$(tests))
 
 all: directories test_cases test_cases_binary
diff --git a/s390x/smp.c b/s390x/smp.c
new file mode 100644
index 0000000..7032494
--- /dev/null
+++ b/s390x/smp.c
@@ -0,0 +1,242 @@ 
+/*
+ * Tests sigp emulation
+ *
+ * Copyright 2019 IBM Corp.
+ *
+ * Authors:
+ *    Janosch Frank <frankja@linux.ibm.com>
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2.
+ */
+#include <libcflat.h>
+#include <asm/asm-offsets.h>
+#include <asm/interrupt.h>
+#include <asm/page.h>
+#include <asm/facility.h>
+#include <asm-generic/barrier.h>
+#include <asm/sigp.h>
+
+#include <smp.h>
+#include <alloc_page.h>
+
+static int testflag = 0;
+
+static void cpu_loop(void)
+{
+	for (;;) {}
+}
+
+static void test_func(void)
+{
+	testflag = 1;
+	mb();
+	cpu_loop();
+}
+
+static void test_start(void)
+{
+	struct psw psw;
+	psw.mask =  extract_psw_mask();
+	psw.addr = (unsigned long)test_func;
+
+	smp_cpu_setup(1, psw);
+	while (!testflag) {
+		mb();
+	}
+	report("start", 1);
+}
+
+static void test_stop(void)
+{
+	smp_cpu_stop(1);
+	/*
+	 * The smp library waits for the CPU to shut down, but let's
+	 * also do it here, so we don't rely on the library
+	 * implementation
+	 */
+	while (!smp_cpu_stopped(1)) {}
+	report("stop", 1);
+}
+
+static void test_stop_store_status(void)
+{
+	struct cpu *cpu = smp_cpu_from_addr(1);
+	struct lowcore *lc = (void *)0x0;
+
+	report_prefix_push("stop store status");
+	lc->prefix_sa = 0;
+	lc->grs_sa[15] = 0;
+	smp_cpu_stop_store_status(1);
+	mb();
+	report("prefix", lc->prefix_sa == (uint32_t)(uintptr_t)cpu->lowcore);
+	report("stack", lc->grs_sa[15]);
+	report_prefix_pop();
+}
+
+static void test_store_status(void)
+{
+	struct cpu_status *status = alloc_pages(1);
+	uint32_t r;
+
+	report_prefix_push("store status at address");
+	memset(status, 0, PAGE_SIZE * 2);
+
+	report_prefix_push("running");
+	smp_cpu_restart(1);
+	sigp(1, SIGP_STORE_STATUS_AT_ADDRESS, (uintptr_t)status, &r);
+	report("incorrect state", r == SIGP_STATUS_INCORRECT_STATE);
+	report("status not written", !memcmp(status, (void*)status + PAGE_SIZE, PAGE_SIZE));
+	report_prefix_pop();
+
+	memset(status, 0, PAGE_SIZE);
+	report_prefix_push("stopped");
+	smp_cpu_stop(1);
+	sigp(1, SIGP_STORE_STATUS_AT_ADDRESS, (uintptr_t)status, NULL);
+	while (!status->prefix) { mb(); }
+	report("status written", 1);
+	free_pages(status, PAGE_SIZE * 2);
+	report_prefix_pop();
+
+	report_prefix_pop();
+}
+
+static void ecall(void)
+{
+	unsigned long mask;
+	struct lowcore *lc = (void *)0x0;
+
+	expect_ext_int();
+	ctl_set_bit(0, 13);
+	mask = extract_psw_mask();
+	mask |= PSW_MASK_EXT;
+	load_psw_mask(mask);
+	testflag = 1;
+	while (lc->ext_int_code != 0x1202) { mb(); }
+	report("ecall", 1);
+	testflag= 1;
+}
+
+static void test_ecall(void)
+{
+	struct psw psw;
+	psw.mask =  extract_psw_mask();
+	psw.addr = (unsigned long)ecall;
+
+	report_prefix_push("ecall");
+	testflag= 0;
+	smp_cpu_destroy(1);
+
+	smp_cpu_setup(1, psw);
+	while (!testflag) { mb(); }
+	testflag= 0;
+	sigp(1, SIGP_EXTERNAL_CALL, 0, NULL);
+	while(!testflag) {mb();}
+	smp_cpu_stop(1);
+	report_prefix_pop();
+}
+
+static void emcall(void)
+{
+	unsigned long mask;
+	struct lowcore *lc = (void *)0x0;
+
+	expect_ext_int();
+	ctl_set_bit(0, 14);
+	mask = extract_psw_mask();
+	mask |= PSW_MASK_EXT;
+	load_psw_mask(mask);
+	testflag= 1;
+	while (lc->ext_int_code != 0x1201) { mb(); }
+	report("ecall", 1);
+	testflag = 1;
+}
+
+static void test_emcall(void)
+{
+	struct psw psw;
+	psw.mask =  extract_psw_mask();
+	psw.addr = (unsigned long)emcall;
+
+	report_prefix_push("emcall");
+	testflag= 0;
+	smp_cpu_destroy(1);
+
+	smp_cpu_setup(1, psw);
+	while (!testflag) { mb(); }
+	testflag= 0;
+	sigp(1, SIGP_EMERGENCY_SIGNAL, 0, NULL);
+	while(!testflag) { mb(); }
+	smp_cpu_stop(1);
+	report_prefix_pop();
+}
+
+static void test_reset_initial(void)
+{
+	struct cpu_status *status = alloc_pages(0);
+	struct psw psw;
+
+	psw.mask =  extract_psw_mask();
+	psw.addr = (unsigned long)test_func;
+
+	report_prefix_push("reset initial");
+	smp_cpu_setup(1, psw);
+
+	sigp_retry(1, SIGP_INITIAL_CPU_RESET, 0, NULL);
+	sigp(1, SIGP_STORE_STATUS_AT_ADDRESS, (uintptr_t)status, NULL);
+
+	report_prefix_push("clear");
+	report("psw", !status->psw.mask && !status->psw.addr);
+	report("prefix", !status->prefix);
+	report("fpc", !status->fpc);
+	report("cpu timer", !status->cputm);
+	report("todpr", !status->todpr);
+	report_prefix_pop();
+
+	report_prefix_push("initialized");
+	report("cr0 == 0xE0", status->crs[0] == 0xE0UL);
+	report("cr14 == 0xC2000000", status->crs[14] == 0xC2000000UL);
+	report_prefix_pop();
+
+	report("cpu stopped", smp_cpu_stopped(1));
+	free_pages(status, PAGE_SIZE);
+	report_prefix_pop();
+}
+
+static void test_reset(void)
+{
+	struct psw psw;
+
+	psw.mask =  extract_psw_mask();
+	psw.addr = (unsigned long)test_func;
+
+	report_prefix_push("cpu reset");
+	smp_cpu_setup(1, psw);
+
+	sigp_retry(1, SIGP_CPU_RESET, 0, NULL);
+	report("cpu stopped", smp_cpu_stopped(1));
+	report_prefix_pop();
+}
+
+int main(void)
+{
+	report_prefix_push("smp");
+
+	if (smp_query_num_cpus() == 1) {
+		report_abort("need at least 2 cpus for this test");
+		goto done;
+	}
+
+	test_start();
+	test_stop();
+	test_stop_store_status();
+	test_store_status();
+	test_ecall();
+	test_emcall();
+	test_reset();
+	test_reset_initial();
+
+done:
+	report_prefix_pop();
+	return report_summary();
+}
diff --git a/s390x/unittests.cfg b/s390x/unittests.cfg
index cc79a4e..f1b07cd 100644
--- a/s390x/unittests.cfg
+++ b/s390x/unittests.cfg
@@ -71,3 +71,7 @@  extra_params=-device diag288,id=watchdog0 --watchdog-action inject-nmi
 
 [stsi]
 file = stsi.elf
+
+[smp]
+file = smp.elf
+extra_params =-smp 2