Re: [kvm-unit-tests RFC 4/4] spe: Test Profiling Buffer Events

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

 



Hi,

On 8/31/20 9:34 PM, Eric Auger wrote:
> Setup the infrastructure to check the occurence of events.
> The test checks the Buffer Full event occurs when no space
> is available. The PPI is handled and we check the syndrome register
> against the expected event.
> 
> Signed-off-by: Eric Auger <eric.auger@xxxxxxxxxx>
> ---
>  arm/spe.c         | 141 +++++++++++++++++++++++++++++++++++++++++++++-
>  arm/unittests.cfg |   8 +++
>  2 files changed, 148 insertions(+), 1 deletion(-)
> 
> diff --git a/arm/spe.c b/arm/spe.c
> index 7996f79..2f5ee35 100644
> --- a/arm/spe.c
> +++ b/arm/spe.c
> @@ -19,6 +19,7 @@
>  #include "alloc_page.h"
>  #include <bitops.h>
>  #include "alloc.h"
> +#include <asm/gic.h>
>  
>  struct spe {
>  	int min_interval;
> @@ -36,13 +37,37 @@ struct spe {
>  	bool unique_record_size;
>  };
>  
> +enum spe_event_exception_class {
> +	EC_STAGE1_DATA_ABORT =  0x24,
> +	EC_STAGE2_DATA_ABORT = 0x25,
> +	EC_OTHER = 0,
> +};
> +
> +struct spe_event {
> +	enum spe_event_exception_class ec;
> +	bool dl;	/* data lost */
> +	bool ea;	/* external abort */
> +	bool s;		/* service */
> +	bool coll;	/* collision */
> +	union {
> +		bool buffer_filled; /* ec = other */
> +	} mss;
> +};
> +
>  static struct spe spe;
>  
> +struct spe_stats {
> +	struct spe_event observed;
> +	bool unexpected;
> +};
> +static struct spe_stats spe_stats;
> +
>  #ifdef __arm__
>  
>  static bool spe_probe(void) { return false; }
>  static void test_spe_introspection(void) { }
>  static void test_spe_buffer(void) { }
> +static void test_spe_events(void) { }
>  
>  #else
>  
> @@ -95,6 +120,16 @@ static void test_spe_buffer(void) { }
>  #define PMSCR_EL1_TS			0x20
>  #define PMSCR_EL1_PCT			0x40
>  
> +#define PMBSR_EL1_COLL			0x10000
> +#define PMBSR_EL1_S			0x20000
> +#define PMBSR_EL1_EA			0x40000
> +#define PMBSR_EL1_DL			0x80000
> +#define PMBSR_EL1_EC_SHIFT		26
> +#define PMBSR_EL1_EC_MASK		0x3F
> +#define PMBSR_EL1_MISS_MASK		0xFFFF
> +
> +#define SPE_PPI 21
> +
>  static int min_interval(uint8_t idr_bits)
>  {
>  	switch (idr_bits) {
> @@ -119,6 +154,44 @@ static int min_interval(uint8_t idr_bits)
>  	}
>  }
>  
> +static int decode_syndrome_register(uint64_t sr, struct spe_event *event, bool verbose)
> +{
> +	if (!sr)
> +		return 0;
> +
> +	if (sr & PMBSR_EL1_S)
> +		event->s = true;
> +	if (sr & PMBSR_EL1_COLL)
> +		event->coll = true;
> +	if (sr & PMBSR_EL1_EA)
> +		event->ea = true;
> +	if (sr & PMBSR_EL1_DL)
> +		event->dl = true;
> +	if (verbose)
> +		report_info("PMBSR_EL1: Service=%d Collision=%d External Fault=%d DataLost=%d",
> +			    event->s, event->coll, event->ea, event->dl);
> +
> +	switch ((sr >> PMBSR_EL1_EC_SHIFT) & PMBSR_EL1_EC_MASK) {
> +	case EC_OTHER:
> +		event->ec = EC_OTHER;
> +		event->mss.buffer_filled = sr & 0x1;
> +		if (verbose)
> +			report_info("PMBSR_EL1: EC = OTHER buffer filled=%d", event->mss.buffer_filled);
> +		break;
> +	case EC_STAGE1_DATA_ABORT:
> +		event->ec = EC_STAGE1_DATA_ABORT;
> +		report_info("PMBSR_EL1: EC = stage 1 data abort");
> +		break;
> +	case EC_STAGE2_DATA_ABORT:
> +		event->ec = EC_STAGE2_DATA_ABORT;
> +		report_info("PMBSR_EL1: EC = stage 2 data abort");
> +		break;
> +	default:
> +		return -1;
> +	}
> +	return 0;
> +}
> +
>  static bool spe_probe(void)
>  {
>  	uint64_t pmbidr_el1, pmsidr_el1;
> @@ -224,6 +297,13 @@ static void reset(void)
>  
>  	/* Make sure the syndrome register is void */
>  	write_sysreg_s(0, PMBSR_EL1);
> +
> +	memset(&spe_stats, 0, sizeof(spe_stats));
> +}
> +
> +inline bool event_match(struct spe_event *observed, struct spe_event *expected)
> +{
> +	return !memcmp(observed, expected, sizeof(struct spe_event));
>  }
>  
>  static inline void drain(void)
> @@ -235,6 +315,7 @@ static inline void drain(void)
>  
>  static void test_spe_buffer(void)
>  {
> +	struct spe_event observed = {}, expected = {};
>  	uint64_t pmbsr_el1, val1, val2;
>  	void *addr = malloc(10 * PAGE_SIZE);
>  
> @@ -290,7 +371,61 @@ static void test_spe_buffer(void)
>  		report_info("This corresponds to %ld record(s) of %d bytes",
>  			    val2 / spe.maxsize, spe.maxsize);
>  	pmbsr_el1 = read_sysreg_s(PMBSR_EL1);
> -	report(!pmbsr_el1, "PMBSR_EL1: no event");
> +	report(!(decode_syndrome_register(pmbsr_el1, &observed, true)) &&
> +	       event_match(&observed, &expected), "PMBSR_EL1: no event");
> +
> +	free(addr);
> +}
> +
> +static void irq_handler(struct pt_regs *regs)
> +{
> +	uint32_t irqstat, irqnr;
> +
> +	irqstat = gic_read_iar();
> +	irqnr = gic_iar_irqnr(irqstat);
> +
> +	if (irqnr == SPE_PPI) {
> +		uint64_t pmbsr_el1 = read_sysreg_s(PMBSR_EL1);
> +
> +		if (decode_syndrome_register(pmbsr_el1, &spe_stats.observed, true))
> +			spe_stats.unexpected = true;
> +		report_info("SPE IRQ! SR=0x%lx", pmbsr_el1);
> +		write_sysreg_s(0, PMBSR_EL1);
> +	} else {
> +		spe_stats.unexpected = true;
> +	}
> +	gic_write_eoir(irqstat);
> +}
> +
> +static inline bool has_event_occurred(struct spe_event *expected)
> +{
> +	return (!spe_stats.unexpected && event_match(&spe_stats.observed, expected));
> +}
> +
> +static void test_spe_events(void)
> +{
> +	struct spe_event expected = {.ec = EC_OTHER, .mss.buffer_filled = true, .s = true};
> +	void *addr = malloc(10 * PAGE_SIZE);
> +
> +	gic_enable_defaults();
> +	install_irq_handler(EL1H_IRQ, irq_handler);
> +	local_irq_enable();
> +	gic_enable_irq(SPE_PPI);
> +
> +	reset();
> +
> +	/* Willingly set pmblimitr tp pmdptr */
> +	spe.pmblimitr_el1 = spe.pmbptr_el1;
> +
> +	mem_access_loop(addr, 100000, spe.pmblimitr_el1 | PMBLIMITR_EL1_E);
> +	drain();
> +	report(has_event_occurred(&expected), "PMBSR_EL1: buffer full event");
> +
> +	/* redo it once */
I noticed I must reset the stats here. I will fix that in next version.

Thanks

Eric
> +
> +	mem_access_loop(addr, 100000, spe.pmblimitr_el1 | PMBLIMITR_EL1_E);
> +	drain();
> +	report(has_event_occurred(&expected), "PMBSR_EL1: buffer full event");
>  
>  	free(addr);
>  }
> @@ -317,6 +452,10 @@ int main(int argc, char *argv[])
>  		report_prefix_push(argv[1]);
>  		test_spe_buffer();
>  		report_prefix_pop();
> +	} else if (strcmp(argv[1], "spe-events") == 0) {
> +		report_prefix_push(argv[1]);
> +		test_spe_events();
> +		report_prefix_pop();
>  	} else {
>  		report_abort("Unknown sub-test '%s'", argv[1]);
>  	}
> diff --git a/arm/unittests.cfg b/arm/unittests.cfg
> index bb0e84c..b2b07be 100644
> --- a/arm/unittests.cfg
> +++ b/arm/unittests.cfg
> @@ -150,6 +150,14 @@ extra_params = -append 'spe-buffer'
>  accel = kvm
>  arch = arm64
>  
> +[spe-events]
> +file = spe.flat
> +groups = spe
> +arch = arm64
> +extra_params = -append 'spe-events'
> +accel = kvm
> +arch = arm64
> +
>  # Test GIC emulation
>  [gicv2-ipi]
>  file = gic.flat
> 




[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