Re: [kvm-unit-tests PATCH v4 5/5] riscv: sbi: Add SSE extension tests

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




On 14/01/2025 12:59, Andrew Jones wrote:
> 
> Continuing review...
> 
> On Mon, Nov 25, 2024 at 05:21:54PM +0100, Clément Léger wrote:
> ...
>> +static void sse_test_inject_global(unsigned long event_id)
>> +{
>> +	unsigned long ret;
>> +	unsigned int cpu;
>> +	struct sse_handler_arg args;
>> +	volatile struct sse_foreign_cpu_test_arg test_arg = {.event_id = event_id};
>> +	enum sbi_sse_state state;
>> +
>> +	args.handler = sse_foreign_cpu_handler;
>> +	args.handler_data = (void *)&test_arg;
>> +	args.stack = sse_alloc_stack();
>> +
>> +	report_prefix_push("global_dispatch");
>> +
>> +	ret = sse_event_register(event_id, &args);
>> +	if (ret)
> 
> print/report something here?>
>> +		goto done;
>> +
>> +	for_each_online_cpu(cpu) {
>> +		test_arg.expected_cpu = cpu;
>> +		/* For test_arg content to be visible for other CPUs */
>> +		smp_wmb();
>> +		ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_PREFERRED_HART, cpu);
>> +		if (ret) {
>> +			report_fail("Failed to set preferred hart");
>> +			goto done;
>> +		}
>> +
>> +		ret = sse_event_enable(event_id);
>> +		if (ret) {
>> +			report_fail("Failed to enable SSE event");
>> +			goto done;
>> +		}
>> +
>> +		ret = sse_event_inject(event_id, cpu);
>> +		if (ret) {
>> +			report_fail("Failed to inject event");
>> +			goto done;
>> +		}
>> +
>> +		while (!test_arg.done) {
>> +			/* For shared test_arg structure */
>> +			smp_rmb();
>> +		}
>> +
>> +		test_arg.done = false;
>> +
>> +		/* Wait for event to be in ENABLED state */
>> +		do {
>> +			ret = sse_get_state(event_id, &state);
>> +			if (ret) {
>> +				report_fail("Failed to get event state");
>> +				goto done;
>> +			}
>> +		} while (state != SBI_SSE_STATE_ENABLED);
>> +
>> +		ret = sse_event_disable(event_id);
>> +		if (ret) {
>> +			report_fail("Failed to disable SSE event");
>> +			goto done;
>> +		}
>> +
>> +		report_pass("Global event on CPU %d", cpu);
>> +	}
> 
> Breaking this into two parts
> 
>  for_each_online_cpu() {
>     ... inject event ...
>  }
>  ... wait a bit ...
>  for_each_online_cpu() {
>     ... check results ...
>  }

Hey Andrew,

That not possible since these are global event so they can only be
injected on one CPU at a time. I did this modification for local event
as you suggested though.

> 
> would allow testing that the SBI implementation can handle
> simultaneous requests.
> 
>> +
>> +done:
>> +	ret = sse_event_unregister(event_id);
>> +	if (ret)
>> +		report_fail("Failed to unregister event");
> 
> If we came here from the 'goto done' for sse_event_register(), then I'd
> expect unregister to fail since we failed to register.

Indeed, I'll fix the error sequence

> 
>> +
>> +	sse_free_stack(args.stack);
>> +	report_prefix_pop();
>> +}
>> +
>> +struct priority_test_arg {
>> +	unsigned long evt;
>> +	bool called;
>> +	u32 prio;
>> +	struct priority_test_arg *next_evt_arg;
>> +	void (*check_func)(struct priority_test_arg *arg);
>> +};
>> +
>> +static void sse_hi_priority_test_handler(void *arg, struct pt_regs *regs,
>> +					 unsigned int hartid)
>> +{
>> +	struct priority_test_arg *targ = arg;
>> +	struct priority_test_arg *next = targ->next_evt_arg;
>> +
>> +	targ->called = 1;
> 
> nit: = true
> 
>> +	if (next) {
>> +		sse_event_inject(next->evt, current_thread_info()->hartid);
>> +		if (sse_event_pending(next->evt))
>> +			report_fail("Higher priority event is pending");
>> +		if (!next->called)
>> +			report_fail("Higher priority event was not handled");
> 
> Should change these if-report-fails to report()'s
> 
>> +	}
>> +}
>> +
>> +static void sse_low_priority_test_handler(void *arg, struct pt_regs *regs,
>> +					  unsigned int hartid)
>> +{
>> +	struct priority_test_arg *targ = arg;
>> +	struct priority_test_arg *next = targ->next_evt_arg;
>> +
>> +	targ->called = 1;
>   
> nit: = true
> 
>> +
>> +	if (next) {
>> +		sse_event_inject(next->evt, current_thread_info()->hartid);
>> +
>> +		if (!sse_event_pending(next->evt))
>> +			report_fail("Lower priority event is pending");
>> +
>> +		if (next->called)
>> +			report_fail("Lower priority event %s was handle before %s",
>> +			      sse_evt_name(next->evt), sse_evt_name(targ->evt));
> 
> Should change these if-report-fails to report()'s
> 
>> +	}
>> +}
>> +
>> +static void sse_test_injection_priority_arg(struct priority_test_arg *in_args,
>> +					    unsigned int in_args_size,
>> +					    sse_handler_fn handler,
>> +					    const char *test_name)
>> +{
>> +	unsigned int i;
>> +	int ret;
>> +	unsigned long event_id;
>> +	struct priority_test_arg *arg;
>> +	unsigned int args_size = 0;
>> +	struct sse_handler_arg event_args[in_args_size];
>> +	struct priority_test_arg *args[in_args_size];
>> +	void *stack;
>> +	struct sse_handler_arg *event_arg;
>> +
>> +	report_prefix_push(test_name);
>> +
>> +	for (i = 0; i < in_args_size; i++) {
>> +		arg = &in_args[i];
>> +		event_id = arg->evt;
>> +		if (!sse_evt_can_inject(event_id))
>> +			continue;
>> +
>> +		args[args_size] = arg;
>> +		args_size++;
>> +	}
>> +
>> +	if (!args_size) {
>> +		report_skip("No event injectable");
>> +		report_prefix_pop();
>> +		goto skip;
>> +	}
>> +
>> +	for (i = 0; i < args_size; i++) {
>> +		arg = args[i];
>> +		event_id = arg->evt;
>> +		stack = sse_alloc_stack();
>> +
>> +		event_arg = &event_args[i];
>> +		event_arg->handler = handler;
>> +		event_arg->handler_data = (void *)arg;
>> +		event_arg->stack = stack;
>> +
>> +		if (i < (args_size - 1))
>> +			arg->next_evt_arg = args[i + 1];
>> +		else
>> +			arg->next_evt_arg = NULL;
>> +
>> +		/* Be sure global events are targeting the current hart */
>> +		sse_global_event_set_current_hart(event_id);
>> +
>> +		sse_event_register(event_id, event_arg);
>> +		sse_event_set_attr(event_id, SBI_SSE_ATTR_PRIORITY, arg->prio);
>> +		sse_event_enable(event_id);
>> +	}
>> +
>> +	/* Inject first event */
>> +	ret = sse_event_inject(args[0]->evt, current_thread_info()->hartid);
>> +	report(ret == SBI_SUCCESS, "SSE injection no error");
>> +
>> +	for (i = 0; i < args_size; i++) {
>> +		arg = args[i];
>> +		event_id = arg->evt;
>> +
>> +		if (!arg->called)
>> +			report_fail("Event %s handler called", sse_evt_name(arg->evt));
> 
> report()
> 
>> +
>> +		sse_event_disable(event_id);
>> +		sse_event_unregister(event_id);
>> +
>> +		event_arg = &event_args[i];
>> +		sse_free_stack(event_arg->stack);
>> +	}
>> +
>> +skip:
>> +	report_prefix_pop();
>> +}
>> +
>> +static struct priority_test_arg hi_prio_args[] = {
>> +	{.evt = SBI_SSE_EVENT_GLOBAL_SOFTWARE},
>> +	{.evt = SBI_SSE_EVENT_LOCAL_SOFTWARE},
>> +	{.evt = SBI_SSE_EVENT_LOCAL_PMU},
>> +	{.evt = SBI_SSE_EVENT_GLOBAL_RAS},
>> +	{.evt = SBI_SSE_EVENT_LOCAL_RAS},
>> +};
>> +
>> +static struct priority_test_arg low_prio_args[] = {
>> +	{.evt = SBI_SSE_EVENT_LOCAL_RAS},
>> +	{.evt = SBI_SSE_EVENT_GLOBAL_RAS},
>> +	{.evt = SBI_SSE_EVENT_LOCAL_PMU},
>> +	{.evt = SBI_SSE_EVENT_LOCAL_SOFTWARE},
>> +	{.evt = SBI_SSE_EVENT_GLOBAL_SOFTWARE},
>> +};
>> +
>> +static struct priority_test_arg prio_args[] = {
>> +	{.evt = SBI_SSE_EVENT_GLOBAL_SOFTWARE, .prio = 5},
>> +	{.evt = SBI_SSE_EVENT_LOCAL_SOFTWARE, .prio = 10},
>> +	{.evt = SBI_SSE_EVENT_LOCAL_PMU, .prio = 15},
>> +	{.evt = SBI_SSE_EVENT_GLOBAL_RAS, .prio = 20},
>> +	{.evt = SBI_SSE_EVENT_LOCAL_RAS, .prio = 25},
>> +};
>> +
>> +static struct priority_test_arg same_prio_args[] = {
>> +	{.evt = SBI_SSE_EVENT_LOCAL_PMU, .prio = 0},
>> +	{.evt = SBI_SSE_EVENT_LOCAL_RAS, .prio = 10},
>> +	{.evt = SBI_SSE_EVENT_LOCAL_SOFTWARE, .prio = 10},
>> +	{.evt = SBI_SSE_EVENT_GLOBAL_SOFTWARE, .prio = 10},
>> +	{.evt = SBI_SSE_EVENT_GLOBAL_RAS, .prio = 20},
>> +};
>> +
>> +static void sse_test_injection_priority(void)
>> +{
>> +	report_prefix_push("prio");
>> +
>> +	sse_test_injection_priority_arg(hi_prio_args, ARRAY_SIZE(hi_prio_args),
>> +					sse_hi_priority_test_handler, "high");
>> +
>> +	sse_test_injection_priority_arg(low_prio_args, ARRAY_SIZE(low_prio_args),
>> +					sse_low_priority_test_handler, "low");
>> +
>> +	sse_test_injection_priority_arg(prio_args, ARRAY_SIZE(prio_args),
>> +					sse_low_priority_test_handler, "changed");
>> +
>> +	sse_test_injection_priority_arg(same_prio_args, ARRAY_SIZE(same_prio_args),
>> +					sse_low_priority_test_handler, "same_prio_args");
>> +
>> +	report_prefix_pop();
>> +}
>> +
>> +static bool sse_can_inject(unsigned long event_id)
>> +{
>> +	int ret;
>> +	unsigned long status;
>> +
>> +	ret = sse_event_get_attr(event_id, SBI_SSE_ATTR_STATUS, &status);
>> +	report(ret == 0, "SSE get attr status no error");
> 
> I'm not sure we need this report()
> 
>> +	if (ret)
>> +		return 0;
> 
> nit: return false
> 
>> +
>> +	return !!(status & BIT(SBI_SSE_ATTR_STATUS_INJECT_OFFSET));
>> +}
>> +
>> +static void boot_secondary(void *data)
>> +{
>> +	sse_hart_unmask();
>> +}
>> +
>> +static void sse_check_mask(void)
>> +{
>> +	int ret;
>> +
>> +	/* Upon boot, event are masked, check that */
>> +	ret = sse_hart_mask();
>> +	report(ret == SBI_ERR_ALREADY_STARTED, "SSE hart mask at boot time ok");
> 
> The spec says that trying to mask twice should return
> SBI_ERR_ALREADY_STOPPED. If this test is passing then we probably have it
> backwards in opensbi too.

I shouldn't have looked at OpenSBI code to implement that test /o\.
Thanks for catching it.

> 
>> +
>> +	ret = sse_hart_unmask();
>> +	report(ret == SBI_SUCCESS, "SSE hart no error ok");
>> +	ret = sse_hart_unmask();
>> +	report(ret == SBI_ERR_ALREADY_STOPPED, "SSE hart unmask twice error ok");
> 
> Should be SBI_ERR_ALREADY_STARTED
> 
>> +
>> +	ret = sse_hart_mask();
>> +	report(ret == SBI_SUCCESS, "SSE hart mask no error");
>> +	ret = sse_hart_mask();
>> +	report(ret == SBI_ERR_ALREADY_STARTED, "SSE hart mask twice ok");
> 
> SBI_ERR_ALREADY_STOPPED
> 
>> +}
>> +
>> +void check_sse(void)
>> +{
>> +	unsigned long i, event;
>> +
>> +	report_prefix_push("sse");
>> +	sse_check_mask();
> 
> I guess the mask check should come after the sbi_probe()

Indeed.

> 
>> +
>> +	/*
>> +	 * Dummy wakeup of all processors since some of them will be targeted
>> +	 * by global events without going through the wakeup call as well as
>> +	 * unmasking all 
>> +	 */
>> +	on_cpus(boot_secondary, NULL);
>> +
>> +	if (!sbi_probe(SBI_EXT_SSE)) {
>> +		report_skip("SSE extension not available");
>> +		report_prefix_pop();
>> +		return;
>> +	}
>> +
>> +	for (i = 0; i < ARRAY_SIZE(sse_event_infos); i++) {
>> +		event = sse_event_infos[i].event_id;
>> +		report_prefix_push(sse_event_infos[i].name);
>> +		if (!sse_can_inject(event)) {
>> +			report_skip("Event does not support injection");
> 
> Let's output the event ID too.

Sure.

> 
>> +			report_prefix_pop();
>> +			continue;
>> +		} else {
>> +			sse_event_infos[i].can_inject = true;
>> +		}
>> +		sse_test_attr(event);
>> +		sse_test_register_error(event);
>> +		sse_test_inject_simple(event);
>> +		if (sse_event_is_global(event))
>> +			sse_test_inject_global(event);
>> +		else
>> +			sse_test_inject_local(event);
>> +
>> +		report_prefix_pop();
>> +	}
>> +
>> +	sse_test_injection_priority();
>> +
>> +	report_prefix_pop();
>> +}
>> diff --git a/riscv/sbi.c b/riscv/sbi.c
>> index 6f4ddaf1..33d5e40d 100644
>> --- a/riscv/sbi.c
>> +++ b/riscv/sbi.c
>> @@ -32,6 +32,8 @@
>>  
>>  #define	HIGH_ADDR_BOUNDARY	((phys_addr_t)1 << 32)
>>  
>> +void check_sse(void);
>> +
>>  static long __labs(long a)
>>  {
>>  	return __builtin_labs(a);
>> @@ -1451,6 +1453,7 @@ int main(int argc, char **argv)
>>  	check_hsm();
>>  	check_dbcn();
>>  	check_susp();
>> +	check_sse();
>>  
>>  	return report_summary();
>>  }
>> -- 
>> 2.45.2
>>
> 
> Thanks,
> drew

Thanks for the review.

Clément





[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux