mirror of
				https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
				synced 2025-10-31 08:08:51 +01:00 
			
		
		
		
	Fixed MTP to work with TWRP
This commit is contained in:
		
						commit
						f6dfaef42e
					
				
					 50820 changed files with 20846062 additions and 0 deletions
				
			
		
							
								
								
									
										33
									
								
								tools/testing/selftests/powerpc/pmu/ebb/Makefile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								tools/testing/selftests/powerpc/pmu/ebb/Makefile
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | |||
| noarg: | ||||
| 	$(MAKE) -C ../../ | ||||
| 
 | ||||
| # The EBB handler is 64-bit code and everything links against it
 | ||||
| CFLAGS += -m64 | ||||
| 
 | ||||
| PROGS := reg_access_test event_attributes_test cycles_test	\
 | ||||
| 	 cycles_with_freeze_test pmc56_overflow_test		\
 | ||||
| 	 ebb_vs_cpu_event_test cpu_event_vs_ebb_test		\
 | ||||
| 	 cpu_event_pinned_vs_ebb_test task_event_vs_ebb_test	\
 | ||||
| 	 task_event_pinned_vs_ebb_test multi_ebb_procs_test	\
 | ||||
| 	 multi_counter_test pmae_handling_test			\
 | ||||
| 	 close_clears_pmcc_test instruction_count_test		\
 | ||||
| 	 fork_cleanup_test ebb_on_child_test			\
 | ||||
| 	 ebb_on_willing_child_test back_to_back_ebbs_test	\
 | ||||
| 	 lost_exception_test no_handler_test			\
 | ||||
| 	 cycles_with_mmcr2_test | ||||
| 
 | ||||
| all: $(PROGS) | ||||
| 
 | ||||
| $(PROGS): ../../harness.c ../event.c ../lib.c ebb.c ebb_handler.S trace.c busy_loop.S | ||||
| 
 | ||||
| instruction_count_test: ../loop.S | ||||
| 
 | ||||
| lost_exception_test: ../lib.c | ||||
| 
 | ||||
| run_tests: all | ||||
| 	@-for PROG in $(PROGS); do \
 | ||||
| 		./$$PROG; \
 | ||||
| 	done; | ||||
| 
 | ||||
| clean: | ||||
| 	rm -f $(PROGS) | ||||
							
								
								
									
										106
									
								
								tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,106 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdbool.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #include "ebb.h" | ||||
| 
 | ||||
| 
 | ||||
| #define NUMBER_OF_EBBS	50 | ||||
| 
 | ||||
| /*
 | ||||
|  * Test that if we overflow the counter while in the EBB handler, we take | ||||
|  * another EBB on exiting from the handler. | ||||
|  * | ||||
|  * We do this by counting with a stupidly low sample period, causing us to | ||||
|  * overflow the PMU while we're still in the EBB handler, leading to another | ||||
|  * EBB. | ||||
|  * | ||||
|  * We get out of what would otherwise be an infinite loop by leaving the | ||||
|  * counter frozen once we've taken enough EBBs. | ||||
|  */ | ||||
| 
 | ||||
| static void ebb_callee(void) | ||||
| { | ||||
| 	uint64_t siar, val; | ||||
| 
 | ||||
| 	val = mfspr(SPRN_BESCR); | ||||
| 	if (!(val & BESCR_PMEO)) { | ||||
| 		ebb_state.stats.spurious++; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	ebb_state.stats.ebb_count++; | ||||
| 	trace_log_counter(ebb_state.trace, ebb_state.stats.ebb_count); | ||||
| 
 | ||||
| 	/* Resets the PMC */ | ||||
| 	count_pmc(1, sample_period); | ||||
| 
 | ||||
| out: | ||||
| 	if (ebb_state.stats.ebb_count == NUMBER_OF_EBBS) | ||||
| 		/* Reset but leave counters frozen */ | ||||
| 		reset_ebb_with_clear_mask(MMCR0_PMAO); | ||||
| 	else | ||||
| 		/* Unfreezes */ | ||||
| 		reset_ebb(); | ||||
| 
 | ||||
| 	/* Do some stuff to chew some cycles and pop the counter */ | ||||
| 	siar = mfspr(SPRN_SIAR); | ||||
| 	trace_log_reg(ebb_state.trace, SPRN_SIAR, siar); | ||||
| 
 | ||||
| 	val = mfspr(SPRN_PMC1); | ||||
| 	trace_log_reg(ebb_state.trace, SPRN_PMC1, val); | ||||
| 
 | ||||
| 	val = mfspr(SPRN_MMCR0); | ||||
| 	trace_log_reg(ebb_state.trace, SPRN_MMCR0, val); | ||||
| } | ||||
| 
 | ||||
| int back_to_back_ebbs(void) | ||||
| { | ||||
| 	struct event event; | ||||
| 
 | ||||
| 	event_init_named(&event, 0x1001e, "cycles"); | ||||
| 	event_leader_ebb_init(&event); | ||||
| 
 | ||||
| 	event.attr.exclude_kernel = 1; | ||||
| 	event.attr.exclude_hv = 1; | ||||
| 	event.attr.exclude_idle = 1; | ||||
| 
 | ||||
| 	FAIL_IF(event_open(&event)); | ||||
| 
 | ||||
| 	setup_ebb_handler(ebb_callee); | ||||
| 
 | ||||
| 	FAIL_IF(ebb_event_enable(&event)); | ||||
| 
 | ||||
| 	sample_period = 5; | ||||
| 
 | ||||
| 	ebb_freeze_pmcs(); | ||||
| 	mtspr(SPRN_PMC1, pmc_sample_period(sample_period)); | ||||
| 	ebb_global_enable(); | ||||
| 	ebb_unfreeze_pmcs(); | ||||
| 
 | ||||
| 	while (ebb_state.stats.ebb_count < NUMBER_OF_EBBS) | ||||
| 		FAIL_IF(core_busy_loop()); | ||||
| 
 | ||||
| 	ebb_global_disable(); | ||||
| 	ebb_freeze_pmcs(); | ||||
| 
 | ||||
| 	count_pmc(1, sample_period); | ||||
| 
 | ||||
| 	dump_ebb_state(); | ||||
| 
 | ||||
| 	event_close(&event); | ||||
| 
 | ||||
| 	FAIL_IF(ebb_state.stats.ebb_count != NUMBER_OF_EBBS); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int main(void) | ||||
| { | ||||
| 	return test_harness(back_to_back_ebbs, "back_to_back_ebbs"); | ||||
| } | ||||
							
								
								
									
										271
									
								
								tools/testing/selftests/powerpc/pmu/ebb/busy_loop.S
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										271
									
								
								tools/testing/selftests/powerpc/pmu/ebb/busy_loop.S
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,271 @@ | |||
| /* | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #include <ppc-asm.h> | ||||
| 
 | ||||
| 	.text | ||||
| 
 | ||||
| FUNC_START(core_busy_loop) | ||||
| 	stdu	%r1, -168(%r1) | ||||
| 	std	r14, 160(%r1) | ||||
| 	std	r15, 152(%r1) | ||||
| 	std	r16, 144(%r1) | ||||
| 	std	r17, 136(%r1) | ||||
| 	std	r18, 128(%r1) | ||||
| 	std	r19, 120(%r1) | ||||
| 	std	r20, 112(%r1) | ||||
| 	std	r21, 104(%r1) | ||||
| 	std	r22, 96(%r1) | ||||
| 	std	r23, 88(%r1) | ||||
| 	std	r24, 80(%r1) | ||||
| 	std	r25, 72(%r1) | ||||
| 	std	r26, 64(%r1) | ||||
| 	std	r27, 56(%r1) | ||||
| 	std	r28, 48(%r1) | ||||
| 	std	r29, 40(%r1) | ||||
| 	std	r30, 32(%r1) | ||||
| 	std	r31, 24(%r1) | ||||
| 
 | ||||
| 	li	 r3, 0x3030 | ||||
| 	std	 r3, -96(%r1) | ||||
| 	li	 r4, 0x4040 | ||||
| 	std	 r4, -104(%r1) | ||||
| 	li	 r5, 0x5050 | ||||
| 	std	 r5, -112(%r1) | ||||
| 	li	 r6, 0x6060 | ||||
| 	std	 r6, -120(%r1) | ||||
| 	li	 r7, 0x7070 | ||||
| 	std	 r7, -128(%r1) | ||||
| 	li	 r8, 0x0808 | ||||
| 	std	 r8, -136(%r1) | ||||
| 	li	 r9, 0x0909 | ||||
| 	std	 r9, -144(%r1) | ||||
| 	li	r10, 0x1010 | ||||
| 	std	r10, -152(%r1) | ||||
| 	li	r11, 0x1111 | ||||
| 	std	r11, -160(%r1) | ||||
| 	li	r14, 0x1414 | ||||
| 	std	r14, -168(%r1) | ||||
| 	li	r15, 0x1515 | ||||
| 	std	r15, -176(%r1) | ||||
| 	li	r16, 0x1616 | ||||
| 	std	r16, -184(%r1) | ||||
| 	li	r17, 0x1717 | ||||
| 	std	r17, -192(%r1) | ||||
| 	li	r18, 0x1818 | ||||
| 	std	r18, -200(%r1) | ||||
| 	li	r19, 0x1919 | ||||
| 	std	r19, -208(%r1) | ||||
| 	li	r20, 0x2020 | ||||
| 	std	r20, -216(%r1) | ||||
| 	li	r21, 0x2121 | ||||
| 	std	r21, -224(%r1) | ||||
| 	li	r22, 0x2222 | ||||
| 	std	r22, -232(%r1) | ||||
| 	li	r23, 0x2323 | ||||
| 	std	r23, -240(%r1) | ||||
| 	li	r24, 0x2424 | ||||
| 	std	r24, -248(%r1) | ||||
| 	li	r25, 0x2525 | ||||
| 	std	r25, -256(%r1) | ||||
| 	li	r26, 0x2626 | ||||
| 	std	r26, -264(%r1) | ||||
| 	li	r27, 0x2727 | ||||
| 	std	r27, -272(%r1) | ||||
| 	li	r28, 0x2828 | ||||
| 	std	r28, -280(%r1) | ||||
| 	li	r29, 0x2929 | ||||
| 	std	r29, -288(%r1) | ||||
| 	li	r30, 0x3030 | ||||
| 	li	r31, 0x3131 | ||||
| 
 | ||||
| 	li	r3, 0 | ||||
| 0:	addi	r3, r3, 1 | ||||
| 	cmpwi	r3, 100 | ||||
| 	blt	0b | ||||
| 
 | ||||
| 	/* Return 1 (fail) unless we get through all the checks */ | ||||
| 	li	r3, 1 | ||||
| 
 | ||||
| 	/* Check none of our registers have been corrupted */ | ||||
| 	cmpwi	r4,  0x4040 | ||||
| 	bne	1f | ||||
| 	cmpwi	r5,  0x5050 | ||||
| 	bne	1f | ||||
| 	cmpwi	r6,  0x6060 | ||||
| 	bne	1f | ||||
| 	cmpwi	r7,  0x7070 | ||||
| 	bne	1f | ||||
| 	cmpwi	r8,  0x0808 | ||||
| 	bne	1f | ||||
| 	cmpwi	r9,  0x0909 | ||||
| 	bne	1f | ||||
| 	cmpwi	r10, 0x1010 | ||||
| 	bne	1f | ||||
| 	cmpwi	r11, 0x1111 | ||||
| 	bne	1f | ||||
| 	cmpwi	r14, 0x1414 | ||||
| 	bne	1f | ||||
| 	cmpwi	r15, 0x1515 | ||||
| 	bne	1f | ||||
| 	cmpwi	r16, 0x1616 | ||||
| 	bne	1f | ||||
| 	cmpwi	r17, 0x1717 | ||||
| 	bne	1f | ||||
| 	cmpwi	r18, 0x1818 | ||||
| 	bne	1f | ||||
| 	cmpwi	r19, 0x1919 | ||||
| 	bne	1f | ||||
| 	cmpwi	r20, 0x2020 | ||||
| 	bne	1f | ||||
| 	cmpwi	r21, 0x2121 | ||||
| 	bne	1f | ||||
| 	cmpwi	r22, 0x2222 | ||||
| 	bne	1f | ||||
| 	cmpwi	r23, 0x2323 | ||||
| 	bne	1f | ||||
| 	cmpwi	r24, 0x2424 | ||||
| 	bne	1f | ||||
| 	cmpwi	r25, 0x2525 | ||||
| 	bne	1f | ||||
| 	cmpwi	r26, 0x2626 | ||||
| 	bne	1f | ||||
| 	cmpwi	r27, 0x2727 | ||||
| 	bne	1f | ||||
| 	cmpwi	r28, 0x2828 | ||||
| 	bne	1f | ||||
| 	cmpwi	r29, 0x2929 | ||||
| 	bne	1f | ||||
| 	cmpwi	r30, 0x3030 | ||||
| 	bne	1f | ||||
| 	cmpwi	r31, 0x3131 | ||||
| 	bne	1f | ||||
| 
 | ||||
| 	/* Load junk into all our registers before we reload them from the stack. */ | ||||
| 	li	r3,  0xde | ||||
| 	li	r4,  0xad | ||||
| 	li	r5,  0xbe | ||||
| 	li	r6,  0xef | ||||
| 	li	r7,  0xde | ||||
| 	li	r8,  0xad | ||||
| 	li	r9,  0xbe | ||||
| 	li	r10, 0xef | ||||
| 	li	r11, 0xde | ||||
| 	li	r14, 0xad | ||||
| 	li	r15, 0xbe | ||||
| 	li	r16, 0xef | ||||
| 	li	r17, 0xde | ||||
| 	li	r18, 0xad | ||||
| 	li	r19, 0xbe | ||||
| 	li	r20, 0xef | ||||
| 	li	r21, 0xde | ||||
| 	li	r22, 0xad | ||||
| 	li	r23, 0xbe | ||||
| 	li	r24, 0xef | ||||
| 	li	r25, 0xde | ||||
| 	li	r26, 0xad | ||||
| 	li	r27, 0xbe | ||||
| 	li	r28, 0xef | ||||
| 	li	r29, 0xdd | ||||
| 
 | ||||
| 	ld	r3,	-96(%r1) | ||||
| 	cmpwi	r3,  0x3030 | ||||
| 	bne	1f | ||||
| 	ld	r4,	-104(%r1) | ||||
| 	cmpwi	r4,  0x4040 | ||||
| 	bne	1f | ||||
| 	ld	r5,	-112(%r1) | ||||
| 	cmpwi	r5,  0x5050 | ||||
| 	bne	1f | ||||
| 	ld	r6,	-120(%r1) | ||||
| 	cmpwi	r6,  0x6060 | ||||
| 	bne	1f | ||||
| 	ld	r7,	-128(%r1) | ||||
| 	cmpwi	r7,  0x7070 | ||||
| 	bne	1f | ||||
| 	ld	r8,	-136(%r1) | ||||
| 	cmpwi	r8,  0x0808 | ||||
| 	bne	1f | ||||
| 	ld	r9,	-144(%r1) | ||||
| 	cmpwi	r9,  0x0909 | ||||
| 	bne	1f | ||||
| 	ld	r10, -152(%r1) | ||||
| 	cmpwi	r10, 0x1010 | ||||
| 	bne	1f | ||||
| 	ld	r11, -160(%r1) | ||||
| 	cmpwi	r11, 0x1111 | ||||
| 	bne	1f | ||||
| 	ld	r14, -168(%r1) | ||||
| 	cmpwi	r14, 0x1414 | ||||
| 	bne	1f | ||||
| 	ld	r15, -176(%r1) | ||||
| 	cmpwi	r15, 0x1515 | ||||
| 	bne	1f | ||||
| 	ld	r16, -184(%r1) | ||||
| 	cmpwi	r16, 0x1616 | ||||
| 	bne	1f | ||||
| 	ld	r17, -192(%r1) | ||||
| 	cmpwi	r17, 0x1717 | ||||
| 	bne	1f | ||||
| 	ld	r18, -200(%r1) | ||||
| 	cmpwi	r18, 0x1818 | ||||
| 	bne	1f | ||||
| 	ld	r19, -208(%r1) | ||||
| 	cmpwi	r19, 0x1919 | ||||
| 	bne	1f | ||||
| 	ld	r20, -216(%r1) | ||||
| 	cmpwi	r20, 0x2020 | ||||
| 	bne	1f | ||||
| 	ld	r21, -224(%r1) | ||||
| 	cmpwi	r21, 0x2121 | ||||
| 	bne	1f | ||||
| 	ld	r22, -232(%r1) | ||||
| 	cmpwi	r22, 0x2222 | ||||
| 	bne	1f | ||||
| 	ld	r23, -240(%r1) | ||||
| 	cmpwi	r23, 0x2323 | ||||
| 	bne	1f | ||||
| 	ld	r24, -248(%r1) | ||||
| 	cmpwi	r24, 0x2424 | ||||
| 	bne	1f | ||||
| 	ld	r25, -256(%r1) | ||||
| 	cmpwi	r25, 0x2525 | ||||
| 	bne	1f | ||||
| 	ld	r26, -264(%r1) | ||||
| 	cmpwi	r26, 0x2626 | ||||
| 	bne	1f | ||||
| 	ld	r27, -272(%r1) | ||||
| 	cmpwi	r27, 0x2727 | ||||
| 	bne	1f | ||||
| 	ld	r28, -280(%r1) | ||||
| 	cmpwi	r28, 0x2828 | ||||
| 	bne	1f | ||||
| 	ld	r29, -288(%r1) | ||||
| 	cmpwi	r29, 0x2929 | ||||
| 	bne	1f | ||||
| 
 | ||||
| 	/* Load 0 (success) to return */ | ||||
| 	li	r3, 0 | ||||
| 
 | ||||
| 1:	ld	r14, 160(%r1) | ||||
| 	ld	r15, 152(%r1) | ||||
| 	ld	r16, 144(%r1) | ||||
| 	ld	r17, 136(%r1) | ||||
| 	ld	r18, 128(%r1) | ||||
| 	ld	r19, 120(%r1) | ||||
| 	ld	r20, 112(%r1) | ||||
| 	ld	r21, 104(%r1) | ||||
| 	ld	r22, 96(%r1) | ||||
| 	ld	r23, 88(%r1) | ||||
| 	ld	r24, 80(%r1) | ||||
| 	ld	r25, 72(%r1) | ||||
| 	ld	r26, 64(%r1) | ||||
| 	ld	r27, 56(%r1) | ||||
| 	ld	r28, 48(%r1) | ||||
| 	ld	r29, 40(%r1) | ||||
| 	ld	r30, 32(%r1) | ||||
| 	ld	r31, 24(%r1) | ||||
| 	addi	%r1, %r1, 168 | ||||
| 	blr | ||||
|  | @ -0,0 +1,59 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <setjmp.h> | ||||
| #include <signal.h> | ||||
| 
 | ||||
| #include "ebb.h" | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Test that closing the EBB event clears MMCR0_PMCC, preventing further access | ||||
|  * by userspace to the PMU hardware. | ||||
|  */ | ||||
| 
 | ||||
| int close_clears_pmcc(void) | ||||
| { | ||||
| 	struct event event; | ||||
| 
 | ||||
| 	event_init_named(&event, 0x1001e, "cycles"); | ||||
| 	event_leader_ebb_init(&event); | ||||
| 
 | ||||
| 	FAIL_IF(event_open(&event)); | ||||
| 
 | ||||
| 	ebb_enable_pmc_counting(1); | ||||
| 	setup_ebb_handler(standard_ebb_callee); | ||||
| 	ebb_global_enable(); | ||||
| 	FAIL_IF(ebb_event_enable(&event)); | ||||
| 
 | ||||
| 	mtspr(SPRN_PMC1, pmc_sample_period(sample_period)); | ||||
| 
 | ||||
| 	while (ebb_state.stats.ebb_count < 1) | ||||
| 		FAIL_IF(core_busy_loop()); | ||||
| 
 | ||||
| 	ebb_global_disable(); | ||||
| 	event_close(&event); | ||||
| 
 | ||||
| 	FAIL_IF(ebb_state.stats.ebb_count == 0); | ||||
| 
 | ||||
| 	/* The real test is here, do we take a SIGILL when writing PMU regs now
 | ||||
| 	 * that we have closed the event. We expect that we will. */ | ||||
| 
 | ||||
| 	FAIL_IF(catch_sigill(write_pmc1)); | ||||
| 
 | ||||
| 	/* We should still be able to read EBB regs though */ | ||||
| 	mfspr(SPRN_EBBHR); | ||||
| 	mfspr(SPRN_EBBRR); | ||||
| 	mfspr(SPRN_BESCR); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int main(void) | ||||
| { | ||||
| 	return test_harness(close_clears_pmcc, "close_clears_pmcc"); | ||||
| } | ||||
|  | @ -0,0 +1,93 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #include <signal.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdbool.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/wait.h> | ||||
| #include <unistd.h> | ||||
| 
 | ||||
| #include "ebb.h" | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Tests a pinned cpu event vs an EBB - in that order. The pinned cpu event | ||||
|  * should remain and the EBB event should fail to enable. | ||||
|  */ | ||||
| 
 | ||||
| static int setup_cpu_event(struct event *event, int cpu) | ||||
| { | ||||
| 	event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL"); | ||||
| 
 | ||||
| 	event->attr.pinned = 1; | ||||
| 
 | ||||
| 	event->attr.exclude_kernel = 1; | ||||
| 	event->attr.exclude_hv = 1; | ||||
| 	event->attr.exclude_idle = 1; | ||||
| 
 | ||||
| 	SKIP_IF(require_paranoia_below(1)); | ||||
| 	FAIL_IF(event_open_with_cpu(event, cpu)); | ||||
| 	FAIL_IF(event_enable(event)); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int cpu_event_pinned_vs_ebb(void) | ||||
| { | ||||
| 	union pipe read_pipe, write_pipe; | ||||
| 	struct event event; | ||||
| 	int cpu, rc; | ||||
| 	pid_t pid; | ||||
| 
 | ||||
| 	cpu = pick_online_cpu(); | ||||
| 	FAIL_IF(cpu < 0); | ||||
| 	FAIL_IF(bind_to_cpu(cpu)); | ||||
| 
 | ||||
| 	FAIL_IF(pipe(read_pipe.fds) == -1); | ||||
| 	FAIL_IF(pipe(write_pipe.fds) == -1); | ||||
| 
 | ||||
| 	pid = fork(); | ||||
| 	if (pid == 0) { | ||||
| 		/* NB order of pipes looks reversed */ | ||||
| 		exit(ebb_child(write_pipe, read_pipe)); | ||||
| 	} | ||||
| 
 | ||||
| 	/* We setup the cpu event first */ | ||||
| 	rc = setup_cpu_event(&event, cpu); | ||||
| 	if (rc) { | ||||
| 		kill_child_and_wait(pid); | ||||
| 		return rc; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Signal the child to install its EBB event and wait */ | ||||
| 	if (sync_with_child(read_pipe, write_pipe)) | ||||
| 		/* If it fails, wait for it to exit */ | ||||
| 		goto wait; | ||||
| 
 | ||||
| 	/* Signal the child to run */ | ||||
| 	FAIL_IF(sync_with_child(read_pipe, write_pipe)); | ||||
| 
 | ||||
| wait: | ||||
| 	/* We expect it to fail to read the event */ | ||||
| 	FAIL_IF(wait_for_child(pid) != 2); | ||||
| 
 | ||||
| 	FAIL_IF(event_disable(&event)); | ||||
| 	FAIL_IF(event_read(&event)); | ||||
| 
 | ||||
| 	event_report(&event); | ||||
| 
 | ||||
| 	/* The cpu event should have run */ | ||||
| 	FAIL_IF(event.result.value == 0); | ||||
| 	FAIL_IF(event.result.enabled != event.result.running); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int main(void) | ||||
| { | ||||
| 	return test_harness(cpu_event_pinned_vs_ebb, "cpu_event_pinned_vs_ebb"); | ||||
| } | ||||
|  | @ -0,0 +1,89 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #include <signal.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdbool.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/wait.h> | ||||
| #include <unistd.h> | ||||
| 
 | ||||
| #include "ebb.h" | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Tests a cpu event vs an EBB - in that order. The EBB should force the cpu | ||||
|  * event off the PMU. | ||||
|  */ | ||||
| 
 | ||||
| static int setup_cpu_event(struct event *event, int cpu) | ||||
| { | ||||
| 	event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL"); | ||||
| 
 | ||||
| 	event->attr.exclude_kernel = 1; | ||||
| 	event->attr.exclude_hv = 1; | ||||
| 	event->attr.exclude_idle = 1; | ||||
| 
 | ||||
| 	SKIP_IF(require_paranoia_below(1)); | ||||
| 	FAIL_IF(event_open_with_cpu(event, cpu)); | ||||
| 	FAIL_IF(event_enable(event)); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int cpu_event_vs_ebb(void) | ||||
| { | ||||
| 	union pipe read_pipe, write_pipe; | ||||
| 	struct event event; | ||||
| 	int cpu, rc; | ||||
| 	pid_t pid; | ||||
| 
 | ||||
| 	cpu = pick_online_cpu(); | ||||
| 	FAIL_IF(cpu < 0); | ||||
| 	FAIL_IF(bind_to_cpu(cpu)); | ||||
| 
 | ||||
| 	FAIL_IF(pipe(read_pipe.fds) == -1); | ||||
| 	FAIL_IF(pipe(write_pipe.fds) == -1); | ||||
| 
 | ||||
| 	pid = fork(); | ||||
| 	if (pid == 0) { | ||||
| 		/* NB order of pipes looks reversed */ | ||||
| 		exit(ebb_child(write_pipe, read_pipe)); | ||||
| 	} | ||||
| 
 | ||||
| 	/* We setup the cpu event first */ | ||||
| 	rc = setup_cpu_event(&event, cpu); | ||||
| 	if (rc) { | ||||
| 		kill_child_and_wait(pid); | ||||
| 		return rc; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Signal the child to install its EBB event and wait */ | ||||
| 	if (sync_with_child(read_pipe, write_pipe)) | ||||
| 		/* If it fails, wait for it to exit */ | ||||
| 		goto wait; | ||||
| 
 | ||||
| 	/* Signal the child to run */ | ||||
| 	FAIL_IF(sync_with_child(read_pipe, write_pipe)); | ||||
| 
 | ||||
| wait: | ||||
| 	/* We expect the child to succeed */ | ||||
| 	FAIL_IF(wait_for_child(pid)); | ||||
| 
 | ||||
| 	FAIL_IF(event_disable(&event)); | ||||
| 	FAIL_IF(event_read(&event)); | ||||
| 
 | ||||
| 	event_report(&event); | ||||
| 
 | ||||
| 	/* The cpu event may have run */ | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int main(void) | ||||
| { | ||||
| 	return test_harness(cpu_event_vs_ebb, "cpu_event_vs_ebb"); | ||||
| } | ||||
							
								
								
									
										58
									
								
								tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,58 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #include "ebb.h" | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Basic test that counts user cycles and takes EBBs. | ||||
|  */ | ||||
| int cycles(void) | ||||
| { | ||||
| 	struct event event; | ||||
| 
 | ||||
| 	event_init_named(&event, 0x1001e, "cycles"); | ||||
| 	event_leader_ebb_init(&event); | ||||
| 
 | ||||
| 	event.attr.exclude_kernel = 1; | ||||
| 	event.attr.exclude_hv = 1; | ||||
| 	event.attr.exclude_idle = 1; | ||||
| 
 | ||||
| 	FAIL_IF(event_open(&event)); | ||||
| 
 | ||||
| 	ebb_enable_pmc_counting(1); | ||||
| 	setup_ebb_handler(standard_ebb_callee); | ||||
| 	ebb_global_enable(); | ||||
| 	FAIL_IF(ebb_event_enable(&event)); | ||||
| 
 | ||||
| 	mtspr(SPRN_PMC1, pmc_sample_period(sample_period)); | ||||
| 
 | ||||
| 	while (ebb_state.stats.ebb_count < 10) { | ||||
| 		FAIL_IF(core_busy_loop()); | ||||
| 		FAIL_IF(ebb_check_mmcr0()); | ||||
| 	} | ||||
| 
 | ||||
| 	ebb_global_disable(); | ||||
| 	ebb_freeze_pmcs(); | ||||
| 
 | ||||
| 	count_pmc(1, sample_period); | ||||
| 
 | ||||
| 	dump_ebb_state(); | ||||
| 
 | ||||
| 	event_close(&event); | ||||
| 
 | ||||
| 	FAIL_IF(ebb_state.stats.ebb_count == 0); | ||||
| 	FAIL_IF(!ebb_check_count(1, sample_period, 100)); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int main(void) | ||||
| { | ||||
| 	return test_harness(cycles, "cycles"); | ||||
| } | ||||
|  | @ -0,0 +1,117 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdbool.h> | ||||
| 
 | ||||
| #include "ebb.h" | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Test of counting cycles while using MMCR0_FC (freeze counters) to only count | ||||
|  * parts of the code. This is complicated by the fact that FC is set by the | ||||
|  * hardware when the event overflows. We may take the EBB after we have set FC, | ||||
|  * so we have to be careful about whether we clear FC at the end of the EBB | ||||
|  * handler or not. | ||||
|  */ | ||||
| 
 | ||||
| static bool counters_frozen = false; | ||||
| static int ebbs_while_frozen = 0; | ||||
| 
 | ||||
| static void ebb_callee(void) | ||||
| { | ||||
| 	uint64_t mask, val; | ||||
| 
 | ||||
| 	mask = MMCR0_PMAO | MMCR0_FC; | ||||
| 
 | ||||
| 	val = mfspr(SPRN_BESCR); | ||||
| 	if (!(val & BESCR_PMEO)) { | ||||
| 		ebb_state.stats.spurious++; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	ebb_state.stats.ebb_count++; | ||||
| 	trace_log_counter(ebb_state.trace, ebb_state.stats.ebb_count); | ||||
| 
 | ||||
| 	val = mfspr(SPRN_MMCR0); | ||||
| 	trace_log_reg(ebb_state.trace, SPRN_MMCR0, val); | ||||
| 
 | ||||
| 	if (counters_frozen) { | ||||
| 		trace_log_string(ebb_state.trace, "frozen"); | ||||
| 		ebbs_while_frozen++; | ||||
| 		mask &= ~MMCR0_FC; | ||||
| 	} | ||||
| 
 | ||||
| 	count_pmc(1, sample_period); | ||||
| out: | ||||
| 	reset_ebb_with_clear_mask(mask); | ||||
| } | ||||
| 
 | ||||
| int cycles_with_freeze(void) | ||||
| { | ||||
| 	struct event event; | ||||
| 	uint64_t val; | ||||
| 	bool fc_cleared; | ||||
| 
 | ||||
| 	event_init_named(&event, 0x1001e, "cycles"); | ||||
| 	event_leader_ebb_init(&event); | ||||
| 
 | ||||
| 	event.attr.exclude_kernel = 1; | ||||
| 	event.attr.exclude_hv = 1; | ||||
| 	event.attr.exclude_idle = 1; | ||||
| 
 | ||||
| 	FAIL_IF(event_open(&event)); | ||||
| 
 | ||||
| 	setup_ebb_handler(ebb_callee); | ||||
| 	ebb_global_enable(); | ||||
| 	FAIL_IF(ebb_event_enable(&event)); | ||||
| 
 | ||||
| 	mtspr(SPRN_PMC1, pmc_sample_period(sample_period)); | ||||
| 
 | ||||
| 	fc_cleared = false; | ||||
| 
 | ||||
| 	/* Make sure we loop until we take at least one EBB */ | ||||
| 	while ((ebb_state.stats.ebb_count < 20 && !fc_cleared) || | ||||
| 		ebb_state.stats.ebb_count < 1) | ||||
| 	{ | ||||
| 		counters_frozen = false; | ||||
| 		mb(); | ||||
| 		mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC); | ||||
| 
 | ||||
| 		FAIL_IF(core_busy_loop()); | ||||
| 
 | ||||
| 		counters_frozen = true; | ||||
| 		mb(); | ||||
| 		mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) |  MMCR0_FC); | ||||
| 
 | ||||
| 		val = mfspr(SPRN_MMCR0); | ||||
| 		if (! (val & MMCR0_FC)) { | ||||
| 			printf("Outside of loop, FC NOT set MMCR0 0x%lx\n", val); | ||||
| 			fc_cleared = true; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	ebb_global_disable(); | ||||
| 	ebb_freeze_pmcs(); | ||||
| 
 | ||||
| 	count_pmc(1, sample_period); | ||||
| 
 | ||||
| 	dump_ebb_state(); | ||||
| 
 | ||||
| 	printf("EBBs while frozen %d\n", ebbs_while_frozen); | ||||
| 
 | ||||
| 	event_close(&event); | ||||
| 
 | ||||
| 	FAIL_IF(ebb_state.stats.ebb_count == 0); | ||||
| 	FAIL_IF(fc_cleared); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int main(void) | ||||
| { | ||||
| 	return test_harness(cycles_with_freeze, "cycles_with_freeze"); | ||||
| } | ||||
|  | @ -0,0 +1,91 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdbool.h> | ||||
| 
 | ||||
| #include "ebb.h" | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Test of counting cycles while manipulating the user accessible bits in MMCR2. | ||||
|  */ | ||||
| 
 | ||||
| /* We use two values because the first freezes PMC1 and so we would get no EBBs */ | ||||
| #define MMCR2_EXPECTED_1 0x4020100804020000UL /* (FC1P|FC2P|FC3P|FC4P|FC5P|FC6P) */ | ||||
| #define MMCR2_EXPECTED_2 0x0020100804020000UL /* (     FC2P|FC3P|FC4P|FC5P|FC6P) */ | ||||
| 
 | ||||
| 
 | ||||
| int cycles_with_mmcr2(void) | ||||
| { | ||||
| 	struct event event; | ||||
| 	uint64_t val, expected[2], actual; | ||||
| 	int i; | ||||
| 	bool bad_mmcr2; | ||||
| 
 | ||||
| 	event_init_named(&event, 0x1001e, "cycles"); | ||||
| 	event_leader_ebb_init(&event); | ||||
| 
 | ||||
| 	event.attr.exclude_kernel = 1; | ||||
| 	event.attr.exclude_hv = 1; | ||||
| 	event.attr.exclude_idle = 1; | ||||
| 
 | ||||
| 	FAIL_IF(event_open(&event)); | ||||
| 
 | ||||
| 	ebb_enable_pmc_counting(1); | ||||
| 	setup_ebb_handler(standard_ebb_callee); | ||||
| 	ebb_global_enable(); | ||||
| 
 | ||||
| 	FAIL_IF(ebb_event_enable(&event)); | ||||
| 
 | ||||
| 	mtspr(SPRN_PMC1, pmc_sample_period(sample_period)); | ||||
| 
 | ||||
| 	/* XXX Set of MMCR2 must be after enable */ | ||||
| 	expected[0] = MMCR2_EXPECTED_1; | ||||
| 	expected[1] = MMCR2_EXPECTED_2; | ||||
| 	i = 0; | ||||
| 	bad_mmcr2 = false; | ||||
| 
 | ||||
| 	/* Make sure we loop until we take at least one EBB */ | ||||
| 	while ((ebb_state.stats.ebb_count < 20 && !bad_mmcr2) || | ||||
| 		ebb_state.stats.ebb_count < 1) | ||||
| 	{ | ||||
| 		mtspr(SPRN_MMCR2, expected[i % 2]); | ||||
| 
 | ||||
| 		FAIL_IF(core_busy_loop()); | ||||
| 
 | ||||
| 		val = mfspr(SPRN_MMCR2); | ||||
| 		if (val != expected[i % 2]) { | ||||
| 			bad_mmcr2 = true; | ||||
| 			actual = val; | ||||
| 		} | ||||
| 
 | ||||
| 		i++; | ||||
| 	} | ||||
| 
 | ||||
| 	ebb_global_disable(); | ||||
| 	ebb_freeze_pmcs(); | ||||
| 
 | ||||
| 	count_pmc(1, sample_period); | ||||
| 
 | ||||
| 	dump_ebb_state(); | ||||
| 
 | ||||
| 	event_close(&event); | ||||
| 
 | ||||
| 	FAIL_IF(ebb_state.stats.ebb_count == 0); | ||||
| 
 | ||||
| 	if (bad_mmcr2) | ||||
| 		printf("Bad MMCR2 value seen is 0x%lx\n", actual); | ||||
| 
 | ||||
| 	FAIL_IF(bad_mmcr2); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int main(void) | ||||
| { | ||||
| 	return test_harness(cycles_with_mmcr2, "cycles_with_mmcr2"); | ||||
| } | ||||
							
								
								
									
										478
									
								
								tools/testing/selftests/powerpc/pmu/ebb/ebb.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										478
									
								
								tools/testing/selftests/powerpc/pmu/ebb/ebb.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,478 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #define _GNU_SOURCE	/* For CPU_ZERO etc. */ | ||||
| 
 | ||||
| #include <sched.h> | ||||
| #include <sys/wait.h> | ||||
| #include <setjmp.h> | ||||
| #include <signal.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <sys/ioctl.h> | ||||
| 
 | ||||
| #include "trace.h" | ||||
| #include "reg.h" | ||||
| #include "ebb.h" | ||||
| 
 | ||||
| 
 | ||||
| void (*ebb_user_func)(void); | ||||
| 
 | ||||
| void ebb_hook(void) | ||||
| { | ||||
| 	if (ebb_user_func) | ||||
| 		ebb_user_func(); | ||||
| } | ||||
| 
 | ||||
| struct ebb_state ebb_state; | ||||
| 
 | ||||
| u64 sample_period = 0x40000000ull; | ||||
| 
 | ||||
| void reset_ebb_with_clear_mask(unsigned long mmcr0_clear_mask) | ||||
| { | ||||
| 	u64 val; | ||||
| 
 | ||||
| 	/* 2) clear MMCR0[PMAO] - docs say BESCR[PMEO] should do this */ | ||||
| 	/* 3) set MMCR0[PMAE]	- docs say BESCR[PME] should do this */ | ||||
| 	val = mfspr(SPRN_MMCR0); | ||||
| 	mtspr(SPRN_MMCR0, (val & ~mmcr0_clear_mask) | MMCR0_PMAE); | ||||
| 
 | ||||
| 	/* 4) clear BESCR[PMEO] */ | ||||
| 	mtspr(SPRN_BESCRR, BESCR_PMEO); | ||||
| 
 | ||||
| 	/* 5) set BESCR[PME] */ | ||||
| 	mtspr(SPRN_BESCRS, BESCR_PME); | ||||
| 
 | ||||
| 	/* 6) rfebb 1 - done in our caller */ | ||||
| } | ||||
| 
 | ||||
| void reset_ebb(void) | ||||
| { | ||||
| 	reset_ebb_with_clear_mask(MMCR0_PMAO | MMCR0_FC); | ||||
| } | ||||
| 
 | ||||
| /* Called outside of the EBB handler to check MMCR0 is sane */ | ||||
| int ebb_check_mmcr0(void) | ||||
| { | ||||
| 	u64 val; | ||||
| 
 | ||||
| 	val = mfspr(SPRN_MMCR0); | ||||
| 	if ((val & (MMCR0_FC | MMCR0_PMAO)) == MMCR0_FC) { | ||||
| 		/* It's OK if we see FC & PMAO, but not FC by itself */ | ||||
| 		printf("Outside of loop, only FC set 0x%llx\n", val); | ||||
| 		return 1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| bool ebb_check_count(int pmc, u64 sample_period, int fudge) | ||||
| { | ||||
| 	u64 count, upper, lower; | ||||
| 
 | ||||
| 	count = ebb_state.stats.pmc_count[PMC_INDEX(pmc)]; | ||||
| 
 | ||||
| 	lower = ebb_state.stats.ebb_count * (sample_period - fudge); | ||||
| 
 | ||||
| 	if (count < lower) { | ||||
| 		printf("PMC%d count (0x%llx) below lower limit 0x%llx (-0x%llx)\n", | ||||
| 			pmc, count, lower, lower - count); | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	upper = ebb_state.stats.ebb_count * (sample_period + fudge); | ||||
| 
 | ||||
| 	if (count > upper) { | ||||
| 		printf("PMC%d count (0x%llx) above upper limit 0x%llx (+0x%llx)\n", | ||||
| 			pmc, count, upper, count - upper); | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	printf("PMC%d count (0x%llx) is between 0x%llx and 0x%llx delta +0x%llx/-0x%llx\n", | ||||
| 		pmc, count, lower, upper, count - lower, upper - count); | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| void standard_ebb_callee(void) | ||||
| { | ||||
| 	int found, i; | ||||
| 	u64 val; | ||||
| 
 | ||||
| 	val = mfspr(SPRN_BESCR); | ||||
| 	if (!(val & BESCR_PMEO)) { | ||||
| 		ebb_state.stats.spurious++; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	ebb_state.stats.ebb_count++; | ||||
| 	trace_log_counter(ebb_state.trace, ebb_state.stats.ebb_count); | ||||
| 
 | ||||
| 	val = mfspr(SPRN_MMCR0); | ||||
| 	trace_log_reg(ebb_state.trace, SPRN_MMCR0, val); | ||||
| 
 | ||||
| 	found = 0; | ||||
| 	for (i = 1; i <= 6; i++) { | ||||
| 		if (ebb_state.pmc_enable[PMC_INDEX(i)]) | ||||
| 			found += count_pmc(i, sample_period); | ||||
| 	} | ||||
| 
 | ||||
| 	if (!found) | ||||
| 		ebb_state.stats.no_overflow++; | ||||
| 
 | ||||
| out: | ||||
| 	reset_ebb(); | ||||
| } | ||||
| 
 | ||||
| extern void ebb_handler(void); | ||||
| 
 | ||||
| void setup_ebb_handler(void (*callee)(void)) | ||||
| { | ||||
| 	u64 entry; | ||||
| 
 | ||||
| #if defined(_CALL_ELF) && _CALL_ELF == 2 | ||||
| 	entry = (u64)ebb_handler; | ||||
| #else | ||||
| 	struct opd | ||||
| 	{ | ||||
| 	    u64 entry; | ||||
| 	    u64 toc; | ||||
| 	} *opd; | ||||
| 
 | ||||
| 	opd = (struct opd *)ebb_handler; | ||||
| 	entry = opd->entry; | ||||
| #endif | ||||
| 	printf("EBB Handler is at %#llx\n", entry); | ||||
| 
 | ||||
| 	ebb_user_func = callee; | ||||
| 
 | ||||
| 	/* Ensure ebb_user_func is set before we set the handler */ | ||||
| 	mb(); | ||||
| 	mtspr(SPRN_EBBHR, entry); | ||||
| 
 | ||||
| 	/* Make sure the handler is set before we return */ | ||||
| 	mb(); | ||||
| } | ||||
| 
 | ||||
| void clear_ebb_stats(void) | ||||
| { | ||||
| 	memset(&ebb_state.stats, 0, sizeof(ebb_state.stats)); | ||||
| } | ||||
| 
 | ||||
| void dump_summary_ebb_state(void) | ||||
| { | ||||
| 	printf("ebb_state:\n"			\ | ||||
| 	       "  ebb_count    = %d\n"		\ | ||||
| 	       "  spurious     = %d\n"		\ | ||||
| 	       "  negative     = %d\n"		\ | ||||
| 	       "  no_overflow  = %d\n"		\ | ||||
| 	       "  pmc[1] count = 0x%llx\n"	\ | ||||
| 	       "  pmc[2] count = 0x%llx\n"	\ | ||||
| 	       "  pmc[3] count = 0x%llx\n"	\ | ||||
| 	       "  pmc[4] count = 0x%llx\n"	\ | ||||
| 	       "  pmc[5] count = 0x%llx\n"	\ | ||||
| 	       "  pmc[6] count = 0x%llx\n", | ||||
| 		ebb_state.stats.ebb_count, ebb_state.stats.spurious, | ||||
| 		ebb_state.stats.negative, ebb_state.stats.no_overflow, | ||||
| 		ebb_state.stats.pmc_count[0], ebb_state.stats.pmc_count[1], | ||||
| 		ebb_state.stats.pmc_count[2], ebb_state.stats.pmc_count[3], | ||||
| 		ebb_state.stats.pmc_count[4], ebb_state.stats.pmc_count[5]); | ||||
| } | ||||
| 
 | ||||
| static char *decode_mmcr0(u32 value) | ||||
| { | ||||
| 	static char buf[16]; | ||||
| 
 | ||||
| 	buf[0] = '\0'; | ||||
| 
 | ||||
| 	if (value & (1 << 31)) | ||||
| 		strcat(buf, "FC "); | ||||
| 	if (value & (1 << 26)) | ||||
| 		strcat(buf, "PMAE "); | ||||
| 	if (value & (1 << 7)) | ||||
| 		strcat(buf, "PMAO "); | ||||
| 
 | ||||
| 	return buf; | ||||
| } | ||||
| 
 | ||||
| static char *decode_bescr(u64 value) | ||||
| { | ||||
| 	static char buf[16]; | ||||
| 
 | ||||
| 	buf[0] = '\0'; | ||||
| 
 | ||||
| 	if (value & (1ull << 63)) | ||||
| 		strcat(buf, "GE "); | ||||
| 	if (value & (1ull << 32)) | ||||
| 		strcat(buf, "PMAE "); | ||||
| 	if (value & 1) | ||||
| 		strcat(buf, "PMAO "); | ||||
| 
 | ||||
| 	return buf; | ||||
| } | ||||
| 
 | ||||
| void dump_ebb_hw_state(void) | ||||
| { | ||||
| 	u64 bescr; | ||||
| 	u32 mmcr0; | ||||
| 
 | ||||
| 	mmcr0 = mfspr(SPRN_MMCR0); | ||||
| 	bescr = mfspr(SPRN_BESCR); | ||||
| 
 | ||||
| 	printf("HW state:\n"		\ | ||||
| 	       "MMCR0 0x%016x %s\n"	\ | ||||
| 	       "MMCR2 0x%016lx\n"	\ | ||||
| 	       "EBBHR 0x%016lx\n"	\ | ||||
| 	       "BESCR 0x%016llx %s\n"	\ | ||||
| 	       "PMC1  0x%016lx\n"	\ | ||||
| 	       "PMC2  0x%016lx\n"	\ | ||||
| 	       "PMC3  0x%016lx\n"	\ | ||||
| 	       "PMC4  0x%016lx\n"	\ | ||||
| 	       "PMC5  0x%016lx\n"	\ | ||||
| 	       "PMC6  0x%016lx\n"	\ | ||||
| 	       "SIAR  0x%016lx\n", | ||||
| 	       mmcr0, decode_mmcr0(mmcr0), mfspr(SPRN_MMCR2), | ||||
| 	       mfspr(SPRN_EBBHR), bescr, decode_bescr(bescr), | ||||
| 	       mfspr(SPRN_PMC1), mfspr(SPRN_PMC2), mfspr(SPRN_PMC3), | ||||
| 	       mfspr(SPRN_PMC4), mfspr(SPRN_PMC5), mfspr(SPRN_PMC6), | ||||
| 	       mfspr(SPRN_SIAR)); | ||||
| } | ||||
| 
 | ||||
| void dump_ebb_state(void) | ||||
| { | ||||
| 	dump_summary_ebb_state(); | ||||
| 
 | ||||
| 	dump_ebb_hw_state(); | ||||
| 
 | ||||
| 	trace_buffer_print(ebb_state.trace); | ||||
| } | ||||
| 
 | ||||
| int count_pmc(int pmc, uint32_t sample_period) | ||||
| { | ||||
| 	uint32_t start_value; | ||||
| 	u64 val; | ||||
| 
 | ||||
| 	/* 0) Read PMC */ | ||||
| 	start_value = pmc_sample_period(sample_period); | ||||
| 
 | ||||
| 	val = read_pmc(pmc); | ||||
| 	if (val < start_value) | ||||
| 		ebb_state.stats.negative++; | ||||
| 	else | ||||
| 		ebb_state.stats.pmc_count[PMC_INDEX(pmc)] += val - start_value; | ||||
| 
 | ||||
| 	trace_log_reg(ebb_state.trace, SPRN_PMC1 + pmc - 1, val); | ||||
| 
 | ||||
| 	/* 1) Reset PMC */ | ||||
| 	write_pmc(pmc, start_value); | ||||
| 
 | ||||
| 	/* Report if we overflowed */ | ||||
| 	return val >= COUNTER_OVERFLOW; | ||||
| } | ||||
| 
 | ||||
| int ebb_event_enable(struct event *e) | ||||
| { | ||||
| 	int rc; | ||||
| 
 | ||||
| 	/* Ensure any SPR writes are ordered vs us */ | ||||
| 	mb(); | ||||
| 
 | ||||
| 	rc = ioctl(e->fd, PERF_EVENT_IOC_ENABLE); | ||||
| 	if (rc) | ||||
| 		return rc; | ||||
| 
 | ||||
| 	rc = event_read(e); | ||||
| 
 | ||||
| 	/* Ditto */ | ||||
| 	mb(); | ||||
| 
 | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| void ebb_freeze_pmcs(void) | ||||
| { | ||||
| 	mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) | MMCR0_FC); | ||||
| 	mb(); | ||||
| } | ||||
| 
 | ||||
| void ebb_unfreeze_pmcs(void) | ||||
| { | ||||
| 	/* Unfreeze counters */ | ||||
| 	mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC); | ||||
| 	mb(); | ||||
| } | ||||
| 
 | ||||
| void ebb_global_enable(void) | ||||
| { | ||||
| 	/* Enable EBBs globally and PMU EBBs */ | ||||
| 	mtspr(SPRN_BESCR, 0x8000000100000000ull); | ||||
| 	mb(); | ||||
| } | ||||
| 
 | ||||
| void ebb_global_disable(void) | ||||
| { | ||||
| 	/* Disable EBBs & freeze counters, events are still scheduled */ | ||||
| 	mtspr(SPRN_BESCRR, BESCR_PME); | ||||
| 	mb(); | ||||
| } | ||||
| 
 | ||||
| void event_ebb_init(struct event *e) | ||||
| { | ||||
| 	e->attr.config |= (1ull << 63); | ||||
| } | ||||
| 
 | ||||
| void event_bhrb_init(struct event *e, unsigned ifm) | ||||
| { | ||||
| 	e->attr.config |= (1ull << 62) | ((u64)ifm << 60); | ||||
| } | ||||
| 
 | ||||
| void event_leader_ebb_init(struct event *e) | ||||
| { | ||||
| 	event_ebb_init(e); | ||||
| 
 | ||||
| 	e->attr.exclusive = 1; | ||||
| 	e->attr.pinned = 1; | ||||
| } | ||||
| 
 | ||||
| int ebb_child(union pipe read_pipe, union pipe write_pipe) | ||||
| { | ||||
| 	struct event event; | ||||
| 	uint64_t val; | ||||
| 
 | ||||
| 	FAIL_IF(wait_for_parent(read_pipe)); | ||||
| 
 | ||||
| 	event_init_named(&event, 0x1001e, "cycles"); | ||||
| 	event_leader_ebb_init(&event); | ||||
| 
 | ||||
| 	event.attr.exclude_kernel = 1; | ||||
| 	event.attr.exclude_hv = 1; | ||||
| 	event.attr.exclude_idle = 1; | ||||
| 
 | ||||
| 	FAIL_IF(event_open(&event)); | ||||
| 
 | ||||
| 	ebb_enable_pmc_counting(1); | ||||
| 	setup_ebb_handler(standard_ebb_callee); | ||||
| 	ebb_global_enable(); | ||||
| 
 | ||||
| 	FAIL_IF(event_enable(&event)); | ||||
| 
 | ||||
| 	if (event_read(&event)) { | ||||
| 		/*
 | ||||
| 		 * Some tests expect to fail here, so don't report an error on | ||||
| 		 * this line, and return a distinguisable error code. Tell the | ||||
| 		 * parent an error happened. | ||||
| 		 */ | ||||
| 		notify_parent_of_error(write_pipe); | ||||
| 		return 2; | ||||
| 	} | ||||
| 
 | ||||
| 	mtspr(SPRN_PMC1, pmc_sample_period(sample_period)); | ||||
| 
 | ||||
| 	FAIL_IF(notify_parent(write_pipe)); | ||||
| 	FAIL_IF(wait_for_parent(read_pipe)); | ||||
| 	FAIL_IF(notify_parent(write_pipe)); | ||||
| 
 | ||||
| 	while (ebb_state.stats.ebb_count < 20) { | ||||
| 		FAIL_IF(core_busy_loop()); | ||||
| 
 | ||||
| 		/* To try and hit SIGILL case */ | ||||
| 		val  = mfspr(SPRN_MMCRA); | ||||
| 		val |= mfspr(SPRN_MMCR2); | ||||
| 		val |= mfspr(SPRN_MMCR0); | ||||
| 	} | ||||
| 
 | ||||
| 	ebb_global_disable(); | ||||
| 	ebb_freeze_pmcs(); | ||||
| 
 | ||||
| 	count_pmc(1, sample_period); | ||||
| 
 | ||||
| 	dump_ebb_state(); | ||||
| 
 | ||||
| 	event_close(&event); | ||||
| 
 | ||||
| 	FAIL_IF(ebb_state.stats.ebb_count == 0); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static jmp_buf setjmp_env; | ||||
| 
 | ||||
| static void sigill_handler(int signal) | ||||
| { | ||||
| 	printf("Took sigill\n"); | ||||
| 	longjmp(setjmp_env, 1); | ||||
| } | ||||
| 
 | ||||
| static struct sigaction sigill_action = { | ||||
| 	.sa_handler = sigill_handler, | ||||
| }; | ||||
| 
 | ||||
| int catch_sigill(void (*func)(void)) | ||||
| { | ||||
| 	if (sigaction(SIGILL, &sigill_action, NULL)) { | ||||
| 		perror("sigaction"); | ||||
| 		return 1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (setjmp(setjmp_env) == 0) { | ||||
| 		func(); | ||||
| 		return 1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void write_pmc1(void) | ||||
| { | ||||
| 	mtspr(SPRN_PMC1, 0); | ||||
| } | ||||
| 
 | ||||
| void write_pmc(int pmc, u64 value) | ||||
| { | ||||
| 	switch (pmc) { | ||||
| 		case 1: mtspr(SPRN_PMC1, value); break; | ||||
| 		case 2: mtspr(SPRN_PMC2, value); break; | ||||
| 		case 3: mtspr(SPRN_PMC3, value); break; | ||||
| 		case 4: mtspr(SPRN_PMC4, value); break; | ||||
| 		case 5: mtspr(SPRN_PMC5, value); break; | ||||
| 		case 6: mtspr(SPRN_PMC6, value); break; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| u64 read_pmc(int pmc) | ||||
| { | ||||
| 	switch (pmc) { | ||||
| 		case 1: return mfspr(SPRN_PMC1); | ||||
| 		case 2: return mfspr(SPRN_PMC2); | ||||
| 		case 3: return mfspr(SPRN_PMC3); | ||||
| 		case 4: return mfspr(SPRN_PMC4); | ||||
| 		case 5: return mfspr(SPRN_PMC5); | ||||
| 		case 6: return mfspr(SPRN_PMC6); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void term_handler(int signal) | ||||
| { | ||||
| 	dump_summary_ebb_state(); | ||||
| 	dump_ebb_hw_state(); | ||||
| 	abort(); | ||||
| } | ||||
| 
 | ||||
| struct sigaction term_action = { | ||||
| 	.sa_handler = term_handler, | ||||
| }; | ||||
| 
 | ||||
| static void __attribute__((constructor)) ebb_init(void) | ||||
| { | ||||
| 	clear_ebb_stats(); | ||||
| 
 | ||||
| 	if (sigaction(SIGTERM, &term_action, NULL)) | ||||
| 		perror("sigaction"); | ||||
| 
 | ||||
| 	ebb_state.trace = trace_buffer_allocate(1 * 1024 * 1024); | ||||
| } | ||||
							
								
								
									
										77
									
								
								tools/testing/selftests/powerpc/pmu/ebb/ebb.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								tools/testing/selftests/powerpc/pmu/ebb/ebb.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,77 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _SELFTESTS_POWERPC_PMU_EBB_EBB_H | ||||
| #define _SELFTESTS_POWERPC_PMU_EBB_EBB_H | ||||
| 
 | ||||
| #include "../event.h" | ||||
| #include "../lib.h" | ||||
| #include "trace.h" | ||||
| #include "reg.h" | ||||
| 
 | ||||
| #define PMC_INDEX(pmc)	((pmc)-1) | ||||
| 
 | ||||
| #define NUM_PMC_VALUES	128 | ||||
| 
 | ||||
| struct ebb_state | ||||
| { | ||||
| 	struct { | ||||
| 		u64 pmc_count[6]; | ||||
| 		volatile int ebb_count; | ||||
| 		int spurious; | ||||
| 		int negative; | ||||
| 		int no_overflow; | ||||
| 	} stats; | ||||
| 
 | ||||
| 	bool pmc_enable[6]; | ||||
| 	struct trace_buffer *trace; | ||||
| }; | ||||
| 
 | ||||
| extern struct ebb_state ebb_state; | ||||
| 
 | ||||
| #define COUNTER_OVERFLOW 0x80000000ull | ||||
| 
 | ||||
| static inline uint32_t pmc_sample_period(uint32_t value) | ||||
| { | ||||
| 	return COUNTER_OVERFLOW - value; | ||||
| } | ||||
| 
 | ||||
| static inline void ebb_enable_pmc_counting(int pmc) | ||||
| { | ||||
| 	ebb_state.pmc_enable[PMC_INDEX(pmc)] = true; | ||||
| } | ||||
| 
 | ||||
| bool ebb_check_count(int pmc, u64 sample_period, int fudge); | ||||
| void event_leader_ebb_init(struct event *e); | ||||
| void event_ebb_init(struct event *e); | ||||
| void event_bhrb_init(struct event *e, unsigned ifm); | ||||
| void setup_ebb_handler(void (*callee)(void)); | ||||
| void standard_ebb_callee(void); | ||||
| int ebb_event_enable(struct event *e); | ||||
| void ebb_global_enable(void); | ||||
| void ebb_global_disable(void); | ||||
| void ebb_freeze_pmcs(void); | ||||
| void ebb_unfreeze_pmcs(void); | ||||
| void event_ebb_init(struct event *e); | ||||
| void event_leader_ebb_init(struct event *e); | ||||
| int count_pmc(int pmc, uint32_t sample_period); | ||||
| void dump_ebb_state(void); | ||||
| void dump_summary_ebb_state(void); | ||||
| void dump_ebb_hw_state(void); | ||||
| void clear_ebb_stats(void); | ||||
| void write_pmc(int pmc, u64 value); | ||||
| u64 read_pmc(int pmc); | ||||
| void reset_ebb_with_clear_mask(unsigned long mmcr0_clear_mask); | ||||
| void reset_ebb(void); | ||||
| int ebb_check_mmcr0(void); | ||||
| 
 | ||||
| extern u64 sample_period; | ||||
| 
 | ||||
| int core_busy_loop(void); | ||||
| int ebb_child(union pipe read_pipe, union pipe write_pipe); | ||||
| int catch_sigill(void (*func)(void)); | ||||
| void write_pmc1(void); | ||||
| 
 | ||||
| #endif /* _SELFTESTS_POWERPC_PMU_EBB_EBB_H */ | ||||
							
								
								
									
										365
									
								
								tools/testing/selftests/powerpc/pmu/ebb/ebb_handler.S
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										365
									
								
								tools/testing/selftests/powerpc/pmu/ebb/ebb_handler.S
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,365 @@ | |||
| /* | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #include <ppc-asm.h> | ||||
| #include "reg.h" | ||||
| 
 | ||||
| 
 | ||||
| /* ppc-asm.h defines most of the reg aliases, but not r1/r2. */ | ||||
| #define r1 1 | ||||
| #define r2 2 | ||||
| 
 | ||||
| #define RFEBB   .long 0x4c000924 | ||||
| 
 | ||||
| /* Stack layout: | ||||
|  * | ||||
|  *                   ^ | ||||
|  *  User stack       | | ||||
|  *  Back chain ------+	<- r1		<-------+ | ||||
|  *  ...						| | ||||
|  *  Red zone / ABI Gap				| | ||||
|  *  ...						| | ||||
|  *  vr63	<+				| | ||||
|  *  vr0		 |				| | ||||
|  *  VSCR	 |				| | ||||
|  *  FSCR	 |				| | ||||
|  *  r31		 | Save area			| | ||||
|  *  r0		 |				| | ||||
|  *  XER		 |				| | ||||
|  *  CTR		 |				| | ||||
|  *  LR		 |				| | ||||
|  *  CCR		<+				| | ||||
|  *  ...		<+				| | ||||
|  *  LR		 | Caller frame			| | ||||
|  *  CCR		 |				| | ||||
|  *  Back chain	<+	<- updated r1	--------+ | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #if defined(_CALL_ELF) && _CALL_ELF == 2 | ||||
| #define ABIGAP		512 | ||||
| #else | ||||
| #define ABIGAP		288 | ||||
| #endif | ||||
| 
 | ||||
| #define NR_GPR		32 | ||||
| #define NR_SPR		6 | ||||
| #define NR_VSR		64 | ||||
| 
 | ||||
| #define SAVE_AREA	((NR_GPR + NR_SPR) * 8 + (NR_VSR * 16)) | ||||
| #define CALLER_FRAME	112 | ||||
| 
 | ||||
| #define STACK_FRAME	(ABIGAP + SAVE_AREA + CALLER_FRAME) | ||||
| 
 | ||||
| #define CCR_SAVE	(CALLER_FRAME) | ||||
| #define LR_SAVE		(CCR_SAVE + 8) | ||||
| #define CTR_SAVE	(LR_SAVE  + 8) | ||||
| #define XER_SAVE	(CTR_SAVE + 8) | ||||
| #define GPR_SAVE(n)	(XER_SAVE + 8 + (8 * n)) | ||||
| #define FSCR_SAVE	(GPR_SAVE(31) + 8) | ||||
| #define VSCR_SAVE	(FSCR_SAVE + 8) | ||||
| #define VSR_SAVE(n)	(VSCR_SAVE + 8 + (16 * n)) | ||||
| 
 | ||||
| #define SAVE_GPR(n)	std n,GPR_SAVE(n)(r1) | ||||
| #define REST_GPR(n)	ld  n,GPR_SAVE(n)(r1) | ||||
| #define TRASH_GPR(n)	lis n,0xaaaa | ||||
| 
 | ||||
| #define SAVE_VSR(n, b)	li b, VSR_SAVE(n); stxvd2x n,b,r1
 | ||||
| #define LOAD_VSR(n, b)	li b, VSR_SAVE(n); lxvd2x  n,b,r1
 | ||||
| 
 | ||||
| #define LOAD_REG_IMMEDIATE(reg,expr)	\ | ||||
| 	lis     reg,(expr)@highest;	\
 | ||||
| 	ori     reg,reg,(expr)@higher;	\
 | ||||
| 	rldicr  reg,reg,32,31;		\
 | ||||
| 	oris    reg,reg,(expr)@h;	\
 | ||||
| 	ori     reg,reg,(expr)@l;
 | ||||
| 
 | ||||
| 
 | ||||
| #if defined(_CALL_ELF) && _CALL_ELF == 2 | ||||
| #define ENTRY_POINT(name) \ | ||||
| 	.type FUNC_NAME(name),@function; \
 | ||||
| 	.globl FUNC_NAME(name); \
 | ||||
| 	FUNC_NAME(name): | ||||
| 
 | ||||
| #define RESTORE_TOC(name)	\ | ||||
| 	/* Restore our TOC pointer using our entry point */	\ | ||||
| 	LOAD_REG_IMMEDIATE(r12, name)				\ | ||||
| 0:	addis	r2,r12,(.TOC.-0b)@ha;				\
 | ||||
| 	addi	r2,r2,(.TOC.-0b)@l;
 | ||||
| 
 | ||||
| #else | ||||
| #define ENTRY_POINT(name) FUNC_START(name) | ||||
| #define RESTORE_TOC(name)	\ | ||||
| 	/* Restore our TOC pointer via our opd entry */	\ | ||||
| 	LOAD_REG_IMMEDIATE(r2, name)			\ | ||||
| 	ld      r2,8(r2);
 | ||||
| #endif | ||||
| 
 | ||||
|     .text | ||||
| 
 | ||||
| ENTRY_POINT(ebb_handler) | ||||
|     stdu    r1,-STACK_FRAME(r1) | ||||
|     SAVE_GPR(0) | ||||
|     mflr    r0 | ||||
|     std     r0,LR_SAVE(r1) | ||||
|     mfcr    r0 | ||||
|     std     r0,CCR_SAVE(r1) | ||||
|     mfctr   r0 | ||||
|     std     r0,CTR_SAVE(r1) | ||||
|     mfxer   r0 | ||||
|     std     r0,XER_SAVE(r1) | ||||
|     SAVE_GPR(2) | ||||
|     SAVE_GPR(3) | ||||
|     SAVE_GPR(4) | ||||
|     SAVE_GPR(5) | ||||
|     SAVE_GPR(6) | ||||
|     SAVE_GPR(7) | ||||
|     SAVE_GPR(8) | ||||
|     SAVE_GPR(9) | ||||
|     SAVE_GPR(10) | ||||
|     SAVE_GPR(11) | ||||
|     SAVE_GPR(12) | ||||
|     SAVE_GPR(13) | ||||
|     SAVE_GPR(14) | ||||
|     SAVE_GPR(15) | ||||
|     SAVE_GPR(16) | ||||
|     SAVE_GPR(17) | ||||
|     SAVE_GPR(18) | ||||
|     SAVE_GPR(19) | ||||
|     SAVE_GPR(20) | ||||
|     SAVE_GPR(21) | ||||
|     SAVE_GPR(22) | ||||
|     SAVE_GPR(23) | ||||
|     SAVE_GPR(24) | ||||
|     SAVE_GPR(25) | ||||
|     SAVE_GPR(26) | ||||
|     SAVE_GPR(27) | ||||
|     SAVE_GPR(28) | ||||
|     SAVE_GPR(29) | ||||
|     SAVE_GPR(30) | ||||
|     SAVE_GPR(31) | ||||
|     SAVE_VSR(0, r3) | ||||
|     mffs     f0 | ||||
|     stfd     f0, FSCR_SAVE(r1) | ||||
|     mfvscr   f0 | ||||
|     stfd     f0, VSCR_SAVE(r1) | ||||
|     SAVE_VSR(1,  r3) | ||||
|     SAVE_VSR(2,  r3) | ||||
|     SAVE_VSR(3,  r3) | ||||
|     SAVE_VSR(4,  r3) | ||||
|     SAVE_VSR(5,  r3) | ||||
|     SAVE_VSR(6,  r3) | ||||
|     SAVE_VSR(7,  r3) | ||||
|     SAVE_VSR(8,  r3) | ||||
|     SAVE_VSR(9,  r3) | ||||
|     SAVE_VSR(10, r3) | ||||
|     SAVE_VSR(11, r3) | ||||
|     SAVE_VSR(12, r3) | ||||
|     SAVE_VSR(13, r3) | ||||
|     SAVE_VSR(14, r3) | ||||
|     SAVE_VSR(15, r3) | ||||
|     SAVE_VSR(16, r3) | ||||
|     SAVE_VSR(17, r3) | ||||
|     SAVE_VSR(18, r3) | ||||
|     SAVE_VSR(19, r3) | ||||
|     SAVE_VSR(20, r3) | ||||
|     SAVE_VSR(21, r3) | ||||
|     SAVE_VSR(22, r3) | ||||
|     SAVE_VSR(23, r3) | ||||
|     SAVE_VSR(24, r3) | ||||
|     SAVE_VSR(25, r3) | ||||
|     SAVE_VSR(26, r3) | ||||
|     SAVE_VSR(27, r3) | ||||
|     SAVE_VSR(28, r3) | ||||
|     SAVE_VSR(29, r3) | ||||
|     SAVE_VSR(30, r3) | ||||
|     SAVE_VSR(31, r3) | ||||
|     SAVE_VSR(32, r3) | ||||
|     SAVE_VSR(33, r3) | ||||
|     SAVE_VSR(34, r3) | ||||
|     SAVE_VSR(35, r3) | ||||
|     SAVE_VSR(36, r3) | ||||
|     SAVE_VSR(37, r3) | ||||
|     SAVE_VSR(38, r3) | ||||
|     SAVE_VSR(39, r3) | ||||
|     SAVE_VSR(40, r3) | ||||
|     SAVE_VSR(41, r3) | ||||
|     SAVE_VSR(42, r3) | ||||
|     SAVE_VSR(43, r3) | ||||
|     SAVE_VSR(44, r3) | ||||
|     SAVE_VSR(45, r3) | ||||
|     SAVE_VSR(46, r3) | ||||
|     SAVE_VSR(47, r3) | ||||
|     SAVE_VSR(48, r3) | ||||
|     SAVE_VSR(49, r3) | ||||
|     SAVE_VSR(50, r3) | ||||
|     SAVE_VSR(51, r3) | ||||
|     SAVE_VSR(52, r3) | ||||
|     SAVE_VSR(53, r3) | ||||
|     SAVE_VSR(54, r3) | ||||
|     SAVE_VSR(55, r3) | ||||
|     SAVE_VSR(56, r3) | ||||
|     SAVE_VSR(57, r3) | ||||
|     SAVE_VSR(58, r3) | ||||
|     SAVE_VSR(59, r3) | ||||
|     SAVE_VSR(60, r3) | ||||
|     SAVE_VSR(61, r3) | ||||
|     SAVE_VSR(62, r3) | ||||
|     SAVE_VSR(63, r3) | ||||
| 
 | ||||
|     TRASH_GPR(2) | ||||
|     TRASH_GPR(3) | ||||
|     TRASH_GPR(4) | ||||
|     TRASH_GPR(5) | ||||
|     TRASH_GPR(6) | ||||
|     TRASH_GPR(7) | ||||
|     TRASH_GPR(8) | ||||
|     TRASH_GPR(9) | ||||
|     TRASH_GPR(10) | ||||
|     TRASH_GPR(11) | ||||
|     TRASH_GPR(12) | ||||
|     TRASH_GPR(14) | ||||
|     TRASH_GPR(15) | ||||
|     TRASH_GPR(16) | ||||
|     TRASH_GPR(17) | ||||
|     TRASH_GPR(18) | ||||
|     TRASH_GPR(19) | ||||
|     TRASH_GPR(20) | ||||
|     TRASH_GPR(21) | ||||
|     TRASH_GPR(22) | ||||
|     TRASH_GPR(23) | ||||
|     TRASH_GPR(24) | ||||
|     TRASH_GPR(25) | ||||
|     TRASH_GPR(26) | ||||
|     TRASH_GPR(27) | ||||
|     TRASH_GPR(28) | ||||
|     TRASH_GPR(29) | ||||
|     TRASH_GPR(30) | ||||
|     TRASH_GPR(31) | ||||
| 
 | ||||
|     RESTORE_TOC(ebb_handler) | ||||
| 
 | ||||
|     /* | ||||
|      * r13 is our TLS pointer. We leave whatever value was in there when the | ||||
|      * EBB fired. That seems to be OK because once set the TLS pointer is not | ||||
|      * changed - but presumably that could change in future. | ||||
|      */ | ||||
| 
 | ||||
|     bl      ebb_hook | ||||
|     nop | ||||
| 
 | ||||
|     /* r2 may be changed here but we don't care */ | ||||
| 
 | ||||
|     lfd      f0, FSCR_SAVE(r1) | ||||
|     mtfsf    0xff,f0 | ||||
|     lfd      f0, VSCR_SAVE(r1) | ||||
|     mtvscr   f0 | ||||
|     LOAD_VSR(0, r3) | ||||
|     LOAD_VSR(1,  r3) | ||||
|     LOAD_VSR(2,  r3) | ||||
|     LOAD_VSR(3,  r3) | ||||
|     LOAD_VSR(4,  r3) | ||||
|     LOAD_VSR(5,  r3) | ||||
|     LOAD_VSR(6,  r3) | ||||
|     LOAD_VSR(7,  r3) | ||||
|     LOAD_VSR(8,  r3) | ||||
|     LOAD_VSR(9,  r3) | ||||
|     LOAD_VSR(10, r3) | ||||
|     LOAD_VSR(11, r3) | ||||
|     LOAD_VSR(12, r3) | ||||
|     LOAD_VSR(13, r3) | ||||
|     LOAD_VSR(14, r3) | ||||
|     LOAD_VSR(15, r3) | ||||
|     LOAD_VSR(16, r3) | ||||
|     LOAD_VSR(17, r3) | ||||
|     LOAD_VSR(18, r3) | ||||
|     LOAD_VSR(19, r3) | ||||
|     LOAD_VSR(20, r3) | ||||
|     LOAD_VSR(21, r3) | ||||
|     LOAD_VSR(22, r3) | ||||
|     LOAD_VSR(23, r3) | ||||
|     LOAD_VSR(24, r3) | ||||
|     LOAD_VSR(25, r3) | ||||
|     LOAD_VSR(26, r3) | ||||
|     LOAD_VSR(27, r3) | ||||
|     LOAD_VSR(28, r3) | ||||
|     LOAD_VSR(29, r3) | ||||
|     LOAD_VSR(30, r3) | ||||
|     LOAD_VSR(31, r3) | ||||
|     LOAD_VSR(32, r3) | ||||
|     LOAD_VSR(33, r3) | ||||
|     LOAD_VSR(34, r3) | ||||
|     LOAD_VSR(35, r3) | ||||
|     LOAD_VSR(36, r3) | ||||
|     LOAD_VSR(37, r3) | ||||
|     LOAD_VSR(38, r3) | ||||
|     LOAD_VSR(39, r3) | ||||
|     LOAD_VSR(40, r3) | ||||
|     LOAD_VSR(41, r3) | ||||
|     LOAD_VSR(42, r3) | ||||
|     LOAD_VSR(43, r3) | ||||
|     LOAD_VSR(44, r3) | ||||
|     LOAD_VSR(45, r3) | ||||
|     LOAD_VSR(46, r3) | ||||
|     LOAD_VSR(47, r3) | ||||
|     LOAD_VSR(48, r3) | ||||
|     LOAD_VSR(49, r3) | ||||
|     LOAD_VSR(50, r3) | ||||
|     LOAD_VSR(51, r3) | ||||
|     LOAD_VSR(52, r3) | ||||
|     LOAD_VSR(53, r3) | ||||
|     LOAD_VSR(54, r3) | ||||
|     LOAD_VSR(55, r3) | ||||
|     LOAD_VSR(56, r3) | ||||
|     LOAD_VSR(57, r3) | ||||
|     LOAD_VSR(58, r3) | ||||
|     LOAD_VSR(59, r3) | ||||
|     LOAD_VSR(60, r3) | ||||
|     LOAD_VSR(61, r3) | ||||
|     LOAD_VSR(62, r3) | ||||
|     LOAD_VSR(63, r3) | ||||
| 
 | ||||
|     ld      r0,XER_SAVE(r1) | ||||
|     mtxer   r0 | ||||
|     ld      r0,CTR_SAVE(r1) | ||||
|     mtctr   r0 | ||||
|     ld      r0,LR_SAVE(r1) | ||||
|     mtlr    r0 | ||||
|     ld      r0,CCR_SAVE(r1) | ||||
|     mtcr    r0 | ||||
|     REST_GPR(0) | ||||
|     REST_GPR(2) | ||||
|     REST_GPR(3) | ||||
|     REST_GPR(4) | ||||
|     REST_GPR(5) | ||||
|     REST_GPR(6) | ||||
|     REST_GPR(7) | ||||
|     REST_GPR(8) | ||||
|     REST_GPR(9) | ||||
|     REST_GPR(10) | ||||
|     REST_GPR(11) | ||||
|     REST_GPR(12) | ||||
|     REST_GPR(13) | ||||
|     REST_GPR(14) | ||||
|     REST_GPR(15) | ||||
|     REST_GPR(16) | ||||
|     REST_GPR(17) | ||||
|     REST_GPR(18) | ||||
|     REST_GPR(19) | ||||
|     REST_GPR(20) | ||||
|     REST_GPR(21) | ||||
|     REST_GPR(22) | ||||
|     REST_GPR(23) | ||||
|     REST_GPR(24) | ||||
|     REST_GPR(25) | ||||
|     REST_GPR(26) | ||||
|     REST_GPR(27) | ||||
|     REST_GPR(28) | ||||
|     REST_GPR(29) | ||||
|     REST_GPR(30) | ||||
|     REST_GPR(31) | ||||
|     addi    r1,r1,STACK_FRAME | ||||
|     RFEBB | ||||
| FUNC_END(ebb_handler) | ||||
							
								
								
									
										86
									
								
								tools/testing/selftests/powerpc/pmu/ebb/ebb_on_child_test.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								tools/testing/selftests/powerpc/pmu/ebb/ebb_on_child_test.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,86 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #include <signal.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdbool.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/wait.h> | ||||
| #include <unistd.h> | ||||
| 
 | ||||
| #include "ebb.h" | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Tests we can setup an EBB on our child. Nothing interesting happens, because | ||||
|  * even though the event is enabled and running the child hasn't enabled the | ||||
|  * actual delivery of the EBBs. | ||||
|  */ | ||||
| 
 | ||||
| static int victim_child(union pipe read_pipe, union pipe write_pipe) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	FAIL_IF(wait_for_parent(read_pipe)); | ||||
| 	FAIL_IF(notify_parent(write_pipe)); | ||||
| 
 | ||||
| 	/* Parent creates EBB event */ | ||||
| 
 | ||||
| 	FAIL_IF(wait_for_parent(read_pipe)); | ||||
| 	FAIL_IF(notify_parent(write_pipe)); | ||||
| 
 | ||||
| 	/* Check the EBB is enabled by writing PMC1 */ | ||||
| 	write_pmc1(); | ||||
| 
 | ||||
| 	/* EBB event is enabled here */ | ||||
| 	for (i = 0; i < 1000000; i++) ; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int ebb_on_child(void) | ||||
| { | ||||
| 	union pipe read_pipe, write_pipe; | ||||
| 	struct event event; | ||||
| 	pid_t pid; | ||||
| 
 | ||||
| 	FAIL_IF(pipe(read_pipe.fds) == -1); | ||||
| 	FAIL_IF(pipe(write_pipe.fds) == -1); | ||||
| 
 | ||||
| 	pid = fork(); | ||||
| 	if (pid == 0) { | ||||
| 		/* NB order of pipes looks reversed */ | ||||
| 		exit(victim_child(write_pipe, read_pipe)); | ||||
| 	} | ||||
| 
 | ||||
| 	FAIL_IF(sync_with_child(read_pipe, write_pipe)); | ||||
| 
 | ||||
| 	/* Child is running now */ | ||||
| 
 | ||||
| 	event_init_named(&event, 0x1001e, "cycles"); | ||||
| 	event_leader_ebb_init(&event); | ||||
| 
 | ||||
| 	event.attr.exclude_kernel = 1; | ||||
| 	event.attr.exclude_hv = 1; | ||||
| 	event.attr.exclude_idle = 1; | ||||
| 
 | ||||
| 	FAIL_IF(event_open_with_pid(&event, pid)); | ||||
| 	FAIL_IF(ebb_event_enable(&event)); | ||||
| 
 | ||||
| 	FAIL_IF(sync_with_child(read_pipe, write_pipe)); | ||||
| 
 | ||||
| 	/* Child should just exit happily */ | ||||
| 	FAIL_IF(wait_for_child(pid)); | ||||
| 
 | ||||
| 	event_close(&event); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int main(void) | ||||
| { | ||||
| 	return test_harness(ebb_on_child, "ebb_on_child"); | ||||
| } | ||||
|  | @ -0,0 +1,92 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #include <signal.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdbool.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/wait.h> | ||||
| #include <unistd.h> | ||||
| 
 | ||||
| #include "ebb.h" | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Tests we can setup an EBB on our child. The child expects this and enables | ||||
|  * EBBs, which are then delivered to the child, even though the event is | ||||
|  * created by the parent. | ||||
|  */ | ||||
| 
 | ||||
| static int victim_child(union pipe read_pipe, union pipe write_pipe) | ||||
| { | ||||
| 	FAIL_IF(wait_for_parent(read_pipe)); | ||||
| 
 | ||||
| 	/* Setup our EBB handler, before the EBB event is created */ | ||||
| 	ebb_enable_pmc_counting(1); | ||||
| 	setup_ebb_handler(standard_ebb_callee); | ||||
| 	ebb_global_enable(); | ||||
| 
 | ||||
| 	FAIL_IF(notify_parent(write_pipe)); | ||||
| 
 | ||||
| 	while (ebb_state.stats.ebb_count < 20) { | ||||
| 		FAIL_IF(core_busy_loop()); | ||||
| 	} | ||||
| 
 | ||||
| 	ebb_global_disable(); | ||||
| 	ebb_freeze_pmcs(); | ||||
| 
 | ||||
| 	count_pmc(1, sample_period); | ||||
| 
 | ||||
| 	dump_ebb_state(); | ||||
| 
 | ||||
| 	FAIL_IF(ebb_state.stats.ebb_count == 0); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* Tests we can setup an EBB on our child - if it's expecting it */ | ||||
| int ebb_on_willing_child(void) | ||||
| { | ||||
| 	union pipe read_pipe, write_pipe; | ||||
| 	struct event event; | ||||
| 	pid_t pid; | ||||
| 
 | ||||
| 	FAIL_IF(pipe(read_pipe.fds) == -1); | ||||
| 	FAIL_IF(pipe(write_pipe.fds) == -1); | ||||
| 
 | ||||
| 	pid = fork(); | ||||
| 	if (pid == 0) { | ||||
| 		/* NB order of pipes looks reversed */ | ||||
| 		exit(victim_child(write_pipe, read_pipe)); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Signal the child to setup its EBB handler */ | ||||
| 	FAIL_IF(sync_with_child(read_pipe, write_pipe)); | ||||
| 
 | ||||
| 	/* Child is running now */ | ||||
| 
 | ||||
| 	event_init_named(&event, 0x1001e, "cycles"); | ||||
| 	event_leader_ebb_init(&event); | ||||
| 
 | ||||
| 	event.attr.exclude_kernel = 1; | ||||
| 	event.attr.exclude_hv = 1; | ||||
| 	event.attr.exclude_idle = 1; | ||||
| 
 | ||||
| 	FAIL_IF(event_open_with_pid(&event, pid)); | ||||
| 	FAIL_IF(ebb_event_enable(&event)); | ||||
| 
 | ||||
| 	/* Child show now take EBBs and then exit */ | ||||
| 	FAIL_IF(wait_for_child(pid)); | ||||
| 
 | ||||
| 	event_close(&event); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int main(void) | ||||
| { | ||||
| 	return test_harness(ebb_on_willing_child, "ebb_on_willing_child"); | ||||
| } | ||||
|  | @ -0,0 +1,86 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #include <signal.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdbool.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/wait.h> | ||||
| #include <unistd.h> | ||||
| 
 | ||||
| #include "ebb.h" | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Tests an EBB vs a cpu event - in that order. The EBB should force the cpu | ||||
|  * event off the PMU. | ||||
|  */ | ||||
| 
 | ||||
| static int setup_cpu_event(struct event *event, int cpu) | ||||
| { | ||||
| 	event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL"); | ||||
| 
 | ||||
| 	event->attr.exclude_kernel = 1; | ||||
| 	event->attr.exclude_hv = 1; | ||||
| 	event->attr.exclude_idle = 1; | ||||
| 
 | ||||
| 	SKIP_IF(require_paranoia_below(1)); | ||||
| 	FAIL_IF(event_open_with_cpu(event, cpu)); | ||||
| 	FAIL_IF(event_enable(event)); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int ebb_vs_cpu_event(void) | ||||
| { | ||||
| 	union pipe read_pipe, write_pipe; | ||||
| 	struct event event; | ||||
| 	int cpu, rc; | ||||
| 	pid_t pid; | ||||
| 
 | ||||
| 	cpu = pick_online_cpu(); | ||||
| 	FAIL_IF(cpu < 0); | ||||
| 	FAIL_IF(bind_to_cpu(cpu)); | ||||
| 
 | ||||
| 	FAIL_IF(pipe(read_pipe.fds) == -1); | ||||
| 	FAIL_IF(pipe(write_pipe.fds) == -1); | ||||
| 
 | ||||
| 	pid = fork(); | ||||
| 	if (pid == 0) { | ||||
| 		/* NB order of pipes looks reversed */ | ||||
| 		exit(ebb_child(write_pipe, read_pipe)); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Signal the child to install its EBB event and wait */ | ||||
| 	FAIL_IF(sync_with_child(read_pipe, write_pipe)); | ||||
| 
 | ||||
| 	/* Now try to install our CPU event */ | ||||
| 	rc = setup_cpu_event(&event, cpu); | ||||
| 	if (rc) { | ||||
| 		kill_child_and_wait(pid); | ||||
| 		return rc; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Signal the child to run */ | ||||
| 	FAIL_IF(sync_with_child(read_pipe, write_pipe)); | ||||
| 
 | ||||
| 	/* .. and wait for it to complete */ | ||||
| 	FAIL_IF(wait_for_child(pid)); | ||||
| 	FAIL_IF(event_disable(&event)); | ||||
| 	FAIL_IF(event_read(&event)); | ||||
| 
 | ||||
| 	event_report(&event); | ||||
| 
 | ||||
| 	/* The cpu event may have run, but we don't expect 100% */ | ||||
| 	FAIL_IF(event.result.enabled >= event.result.running); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int main(void) | ||||
| { | ||||
| 	return test_harness(ebb_vs_cpu_event, "ebb_vs_cpu_event"); | ||||
| } | ||||
							
								
								
									
										131
									
								
								tools/testing/selftests/powerpc/pmu/ebb/event_attributes_test.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								tools/testing/selftests/powerpc/pmu/ebb/event_attributes_test.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,131 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #include "ebb.h" | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Test various attributes of the EBB event are enforced. | ||||
|  */ | ||||
| int event_attributes(void) | ||||
| { | ||||
| 	struct event event, leader; | ||||
| 
 | ||||
| 	event_init(&event, 0x1001e); | ||||
| 	event_leader_ebb_init(&event); | ||||
| 	/* Expected to succeed */ | ||||
| 	FAIL_IF(event_open(&event)); | ||||
| 	event_close(&event); | ||||
| 
 | ||||
| 
 | ||||
| 	event_init(&event, 0x001e); /* CYCLES - no PMC specified */ | ||||
| 	event_leader_ebb_init(&event); | ||||
| 	/* Expected to fail, no PMC specified */ | ||||
| 	FAIL_IF(event_open(&event) == 0); | ||||
| 
 | ||||
| 
 | ||||
| 	event_init(&event, 0x2001e); | ||||
| 	event_leader_ebb_init(&event); | ||||
| 	event.attr.exclusive = 0; | ||||
| 	/* Expected to fail, not exclusive */ | ||||
| 	FAIL_IF(event_open(&event) == 0); | ||||
| 
 | ||||
| 
 | ||||
| 	event_init(&event, 0x3001e); | ||||
| 	event_leader_ebb_init(&event); | ||||
| 	event.attr.freq = 1; | ||||
| 	/* Expected to fail, sets freq */ | ||||
| 	FAIL_IF(event_open(&event) == 0); | ||||
| 
 | ||||
| 
 | ||||
| 	event_init(&event, 0x4001e); | ||||
| 	event_leader_ebb_init(&event); | ||||
| 	event.attr.sample_period = 1; | ||||
| 	/* Expected to fail, sets sample_period */ | ||||
| 	FAIL_IF(event_open(&event) == 0); | ||||
| 
 | ||||
| 
 | ||||
| 	event_init(&event, 0x1001e); | ||||
| 	event_leader_ebb_init(&event); | ||||
| 	event.attr.enable_on_exec = 1; | ||||
| 	/* Expected to fail, sets enable_on_exec */ | ||||
| 	FAIL_IF(event_open(&event) == 0); | ||||
| 
 | ||||
| 
 | ||||
| 	event_init(&event, 0x1001e); | ||||
| 	event_leader_ebb_init(&event); | ||||
| 	event.attr.inherit = 1; | ||||
| 	/* Expected to fail, sets inherit */ | ||||
| 	FAIL_IF(event_open(&event) == 0); | ||||
| 
 | ||||
| 
 | ||||
| 	event_init(&leader, 0x1001e); | ||||
| 	event_leader_ebb_init(&leader); | ||||
| 	FAIL_IF(event_open(&leader)); | ||||
| 
 | ||||
| 	event_init(&event, 0x20002); | ||||
| 	event_ebb_init(&event); | ||||
| 
 | ||||
| 	/* Expected to succeed */ | ||||
| 	FAIL_IF(event_open_with_group(&event, leader.fd)); | ||||
| 	event_close(&leader); | ||||
| 	event_close(&event); | ||||
| 
 | ||||
| 
 | ||||
| 	event_init(&leader, 0x1001e); | ||||
| 	event_leader_ebb_init(&leader); | ||||
| 	FAIL_IF(event_open(&leader)); | ||||
| 
 | ||||
| 	event_init(&event, 0x20002); | ||||
| 
 | ||||
| 	/* Expected to fail, event doesn't request EBB, leader does */ | ||||
| 	FAIL_IF(event_open_with_group(&event, leader.fd) == 0); | ||||
| 	event_close(&leader); | ||||
| 
 | ||||
| 
 | ||||
| 	event_init(&leader, 0x1001e); | ||||
| 	event_leader_ebb_init(&leader); | ||||
| 	/* Clear the EBB flag */ | ||||
| 	leader.attr.config &= ~(1ull << 63); | ||||
| 
 | ||||
| 	FAIL_IF(event_open(&leader)); | ||||
| 
 | ||||
| 	event_init(&event, 0x20002); | ||||
| 	event_ebb_init(&event); | ||||
| 
 | ||||
| 	/* Expected to fail, leader doesn't request EBB */ | ||||
| 	FAIL_IF(event_open_with_group(&event, leader.fd) == 0); | ||||
| 	event_close(&leader); | ||||
| 
 | ||||
| 
 | ||||
| 	event_init(&leader, 0x1001e); | ||||
| 	event_leader_ebb_init(&leader); | ||||
| 	leader.attr.exclusive = 0; | ||||
| 	/* Expected to fail, leader isn't exclusive */ | ||||
| 	FAIL_IF(event_open(&leader) == 0); | ||||
| 
 | ||||
| 
 | ||||
| 	event_init(&leader, 0x1001e); | ||||
| 	event_leader_ebb_init(&leader); | ||||
| 	leader.attr.pinned = 0; | ||||
| 	/* Expected to fail, leader isn't pinned */ | ||||
| 	FAIL_IF(event_open(&leader) == 0); | ||||
| 
 | ||||
| 	event_init(&event, 0x1001e); | ||||
| 	event_leader_ebb_init(&event); | ||||
| 	/* Expected to fail, not a task event */ | ||||
| 	SKIP_IF(require_paranoia_below(1)); | ||||
| 	FAIL_IF(event_open_with_cpu(&event, 0) == 0); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int main(void) | ||||
| { | ||||
| 	return test_harness(event_attributes, "event_attributes"); | ||||
| } | ||||
|  | @ -0,0 +1,43 @@ | |||
| /* | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #include <ppc-asm.h> | ||||
| 
 | ||||
| 	.text | ||||
| 
 | ||||
| FUNC_START(thirty_two_instruction_loop) | ||||
| 	cmpwi	r3,0 | ||||
| 	beqlr | ||||
| 	addi	r4,r3,1 | ||||
| 	addi	r4,r4,1 | ||||
| 	addi	r4,r4,1 | ||||
| 	addi	r4,r4,1 | ||||
| 	addi	r4,r4,1 | ||||
| 	addi	r4,r4,1 | ||||
| 	addi	r4,r4,1 | ||||
| 	addi	r4,r4,1 | ||||
| 	addi	r4,r4,1 | ||||
| 	addi	r4,r4,1 | ||||
| 	addi	r4,r4,1 | ||||
| 	addi	r4,r4,1 | ||||
| 	addi	r4,r4,1 | ||||
| 	addi	r4,r4,1 | ||||
| 	addi	r4,r4,1 | ||||
| 	addi	r4,r4,1 | ||||
| 	addi	r4,r4,1 | ||||
| 	addi	r4,r4,1 | ||||
| 	addi	r4,r4,1 | ||||
| 	addi	r4,r4,1 | ||||
| 	addi	r4,r4,1 | ||||
| 	addi	r4,r4,1 | ||||
| 	addi	r4,r4,1 | ||||
| 	addi	r4,r4,1 | ||||
| 	addi	r4,r4,1 | ||||
| 	addi	r4,r4,1 | ||||
| 	addi	r4,r4,1 | ||||
| 	addi	r4,r4,1	# 28 addi's | ||||
| 	subi	r3,r3,1 | ||||
| 	b	FUNC_NAME(thirty_two_instruction_loop) | ||||
| FUNC_END(thirty_two_instruction_loop) | ||||
							
								
								
									
										79
									
								
								tools/testing/selftests/powerpc/pmu/ebb/fork_cleanup_test.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								tools/testing/selftests/powerpc/pmu/ebb/fork_cleanup_test.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,79 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #include <signal.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdbool.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/wait.h> | ||||
| #include <unistd.h> | ||||
| #include <setjmp.h> | ||||
| #include <signal.h> | ||||
| 
 | ||||
| #include "ebb.h" | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Test that a fork clears the PMU state of the child. eg. BESCR/EBBHR/EBBRR | ||||
|  * are cleared, and MMCR0_PMCC is reset, preventing the child from accessing | ||||
|  * the PMU. | ||||
|  */ | ||||
| 
 | ||||
| static struct event event; | ||||
| 
 | ||||
| static int child(void) | ||||
| { | ||||
| 	/* Even though we have EBE=0 we can still see the EBB regs */ | ||||
| 	FAIL_IF(mfspr(SPRN_BESCR) != 0); | ||||
| 	FAIL_IF(mfspr(SPRN_EBBHR) != 0); | ||||
| 	FAIL_IF(mfspr(SPRN_EBBRR) != 0); | ||||
| 
 | ||||
| 	FAIL_IF(catch_sigill(write_pmc1)); | ||||
| 
 | ||||
| 	/* We can still read from the event, though it is on our parent */ | ||||
| 	FAIL_IF(event_read(&event)); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* Tests that fork clears EBB state */ | ||||
| int fork_cleanup(void) | ||||
| { | ||||
| 	pid_t pid; | ||||
| 
 | ||||
| 	event_init_named(&event, 0x1001e, "cycles"); | ||||
| 	event_leader_ebb_init(&event); | ||||
| 
 | ||||
| 	FAIL_IF(event_open(&event)); | ||||
| 
 | ||||
| 	ebb_enable_pmc_counting(1); | ||||
| 	setup_ebb_handler(standard_ebb_callee); | ||||
| 	ebb_global_enable(); | ||||
| 
 | ||||
| 	FAIL_IF(ebb_event_enable(&event)); | ||||
| 
 | ||||
| 	mtspr(SPRN_MMCR0, MMCR0_FC); | ||||
| 	mtspr(SPRN_PMC1, pmc_sample_period(sample_period)); | ||||
| 
 | ||||
| 	/* Don't need to actually take any EBBs */ | ||||
| 
 | ||||
| 	pid = fork(); | ||||
| 	if (pid == 0) | ||||
| 		exit(child()); | ||||
| 
 | ||||
| 	/* Child does the actual testing */ | ||||
| 	FAIL_IF(wait_for_child(pid)); | ||||
| 
 | ||||
| 	/* After fork */ | ||||
| 	event_close(&event); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int main(void) | ||||
| { | ||||
| 	return test_harness(fork_cleanup, "fork_cleanup"); | ||||
| } | ||||
							
								
								
									
										164
									
								
								tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								tools/testing/selftests/powerpc/pmu/ebb/instruction_count_test.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,164 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #define _GNU_SOURCE | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdbool.h> | ||||
| #include <string.h> | ||||
| #include <sys/prctl.h> | ||||
| 
 | ||||
| #include "ebb.h" | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Run a calibrated instruction loop and count instructions executed using | ||||
|  * EBBs. Make sure the counts look right. | ||||
|  */ | ||||
| 
 | ||||
| extern void thirty_two_instruction_loop(uint64_t loops); | ||||
| 
 | ||||
| static bool counters_frozen = true; | ||||
| 
 | ||||
| static int do_count_loop(struct event *event, uint64_t instructions, | ||||
| 			 uint64_t overhead, bool report) | ||||
| { | ||||
| 	int64_t difference, expected; | ||||
| 	double percentage; | ||||
| 
 | ||||
| 	clear_ebb_stats(); | ||||
| 
 | ||||
| 	counters_frozen = false; | ||||
| 	mb(); | ||||
| 	mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC); | ||||
| 
 | ||||
| 	thirty_two_instruction_loop(instructions >> 5); | ||||
| 
 | ||||
| 	counters_frozen = true; | ||||
| 	mb(); | ||||
| 	mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) | MMCR0_FC); | ||||
| 
 | ||||
| 	count_pmc(4, sample_period); | ||||
| 
 | ||||
| 	event->result.value = ebb_state.stats.pmc_count[4-1]; | ||||
| 	expected = instructions + overhead; | ||||
| 	difference = event->result.value - expected; | ||||
| 	percentage = (double)difference / event->result.value * 100; | ||||
| 
 | ||||
| 	if (report) { | ||||
| 		printf("Looped for %lu instructions, overhead %lu\n", instructions, overhead); | ||||
| 		printf("Expected %lu\n", expected); | ||||
| 		printf("Actual   %llu\n", event->result.value); | ||||
| 		printf("Error    %ld, %f%%\n", difference, percentage); | ||||
| 		printf("Took %d EBBs\n", ebb_state.stats.ebb_count); | ||||
| 	} | ||||
| 
 | ||||
| 	if (difference < 0) | ||||
| 		difference = -difference; | ||||
| 
 | ||||
| 	/* Tolerate a difference of up to 0.0001 % */ | ||||
| 	difference *= 10000 * 100; | ||||
| 	if (difference / event->result.value) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* Count how many instructions it takes to do a null loop */ | ||||
| static uint64_t determine_overhead(struct event *event) | ||||
| { | ||||
| 	uint64_t current, overhead; | ||||
| 	int i; | ||||
| 
 | ||||
| 	do_count_loop(event, 0, 0, false); | ||||
| 	overhead = event->result.value; | ||||
| 
 | ||||
| 	for (i = 0; i < 100; i++) { | ||||
| 		do_count_loop(event, 0, 0, false); | ||||
| 		current = event->result.value; | ||||
| 		if (current < overhead) { | ||||
| 			printf("Replacing overhead %lu with %lu\n", overhead, current); | ||||
| 			overhead = current; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return overhead; | ||||
| } | ||||
| 
 | ||||
| static void pmc4_ebb_callee(void) | ||||
| { | ||||
| 	uint64_t val; | ||||
| 
 | ||||
| 	val = mfspr(SPRN_BESCR); | ||||
| 	if (!(val & BESCR_PMEO)) { | ||||
| 		ebb_state.stats.spurious++; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	ebb_state.stats.ebb_count++; | ||||
| 	count_pmc(4, sample_period); | ||||
| out: | ||||
| 	if (counters_frozen) | ||||
| 		reset_ebb_with_clear_mask(MMCR0_PMAO); | ||||
| 	else | ||||
| 		reset_ebb(); | ||||
| } | ||||
| 
 | ||||
| int instruction_count(void) | ||||
| { | ||||
| 	struct event event; | ||||
| 	uint64_t overhead; | ||||
| 
 | ||||
| 	event_init_named(&event, 0x400FA, "PM_RUN_INST_CMPL"); | ||||
| 	event_leader_ebb_init(&event); | ||||
| 	event.attr.exclude_kernel = 1; | ||||
| 	event.attr.exclude_hv = 1; | ||||
| 	event.attr.exclude_idle = 1; | ||||
| 
 | ||||
| 	FAIL_IF(event_open(&event)); | ||||
| 	FAIL_IF(ebb_event_enable(&event)); | ||||
| 
 | ||||
| 	sample_period = COUNTER_OVERFLOW; | ||||
| 
 | ||||
| 	setup_ebb_handler(pmc4_ebb_callee); | ||||
| 	mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC); | ||||
| 	ebb_global_enable(); | ||||
| 
 | ||||
| 	overhead = determine_overhead(&event); | ||||
| 	printf("Overhead of null loop: %lu instructions\n", overhead); | ||||
| 
 | ||||
| 	/* Run for 1M instructions */ | ||||
| 	FAIL_IF(do_count_loop(&event, 0x100000, overhead, true)); | ||||
| 
 | ||||
| 	/* Run for 10M instructions */ | ||||
| 	FAIL_IF(do_count_loop(&event, 0xa00000, overhead, true)); | ||||
| 
 | ||||
| 	/* Run for 100M instructions */ | ||||
| 	FAIL_IF(do_count_loop(&event, 0x6400000, overhead, true)); | ||||
| 
 | ||||
| 	/* Run for 1G instructions */ | ||||
| 	FAIL_IF(do_count_loop(&event, 0x40000000, overhead, true)); | ||||
| 
 | ||||
| 	/* Run for 16G instructions */ | ||||
| 	FAIL_IF(do_count_loop(&event, 0x400000000, overhead, true)); | ||||
| 
 | ||||
| 	/* Run for 64G instructions */ | ||||
| 	FAIL_IF(do_count_loop(&event, 0x1000000000, overhead, true)); | ||||
| 
 | ||||
| 	/* Run for 128G instructions */ | ||||
| 	FAIL_IF(do_count_loop(&event, 0x2000000000, overhead, true)); | ||||
| 
 | ||||
| 	ebb_global_disable(); | ||||
| 	event_close(&event); | ||||
| 
 | ||||
| 	printf("Finished OK\n"); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int main(void) | ||||
| { | ||||
| 	return test_harness(instruction_count, "instruction_count"); | ||||
| } | ||||
							
								
								
									
										100
									
								
								tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								tools/testing/selftests/powerpc/pmu/ebb/lost_exception_test.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,100 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #include <sched.h> | ||||
| #include <signal.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <sys/mman.h> | ||||
| 
 | ||||
| #include "ebb.h" | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Test that tries to trigger CPU_FTR_PMAO_BUG. Which is a hardware defect | ||||
|  * where an exception triggers but we context switch before it is delivered and | ||||
|  * lose the exception. | ||||
|  */ | ||||
| 
 | ||||
| static int test_body(void) | ||||
| { | ||||
| 	int i, orig_period, max_period; | ||||
| 	struct event event; | ||||
| 
 | ||||
| 	/* We use PMC4 to make sure the kernel switches all counters correctly */ | ||||
| 	event_init_named(&event, 0x40002, "instructions"); | ||||
| 	event_leader_ebb_init(&event); | ||||
| 
 | ||||
| 	event.attr.exclude_kernel = 1; | ||||
| 	event.attr.exclude_hv = 1; | ||||
| 	event.attr.exclude_idle = 1; | ||||
| 
 | ||||
| 	FAIL_IF(event_open(&event)); | ||||
| 
 | ||||
| 	ebb_enable_pmc_counting(4); | ||||
| 	setup_ebb_handler(standard_ebb_callee); | ||||
| 	ebb_global_enable(); | ||||
| 	FAIL_IF(ebb_event_enable(&event)); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * We want a low sample period, but we also want to get out of the EBB | ||||
| 	 * handler without tripping up again. | ||||
| 	 * | ||||
| 	 * This value picked after much experimentation. | ||||
| 	 */ | ||||
| 	orig_period = max_period = sample_period = 400; | ||||
| 
 | ||||
| 	mtspr(SPRN_PMC4, pmc_sample_period(sample_period)); | ||||
| 
 | ||||
| 	while (ebb_state.stats.ebb_count < 1000000) { | ||||
| 		/*
 | ||||
| 		 * We are trying to get the EBB exception to race exactly with | ||||
| 		 * us entering the kernel to do the syscall. We then need the | ||||
| 		 * kernel to decide our timeslice is up and context switch to | ||||
| 		 * the other thread. When we come back our EBB will have been | ||||
| 		 * lost and we'll spin in this while loop forever. | ||||
| 		 */ | ||||
| 
 | ||||
| 		for (i = 0; i < 100000; i++) | ||||
| 			sched_yield(); | ||||
| 
 | ||||
| 		/* Change the sample period slightly to try and hit the race */ | ||||
| 		if (sample_period >= (orig_period + 200)) | ||||
| 			sample_period = orig_period; | ||||
| 		else | ||||
| 			sample_period++; | ||||
| 
 | ||||
| 		if (sample_period > max_period) | ||||
| 			max_period = sample_period; | ||||
| 	} | ||||
| 
 | ||||
| 	ebb_freeze_pmcs(); | ||||
| 	ebb_global_disable(); | ||||
| 
 | ||||
| 	count_pmc(4, sample_period); | ||||
| 	mtspr(SPRN_PMC4, 0xdead); | ||||
| 
 | ||||
| 	dump_summary_ebb_state(); | ||||
| 	dump_ebb_hw_state(); | ||||
| 
 | ||||
| 	event_close(&event); | ||||
| 
 | ||||
| 	FAIL_IF(ebb_state.stats.ebb_count == 0); | ||||
| 
 | ||||
| 	/* We vary our sample period so we need extra fudge here */ | ||||
| 	FAIL_IF(!ebb_check_count(4, orig_period, 2 * (max_period - orig_period))); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int lost_exception(void) | ||||
| { | ||||
| 	return eat_cpu(test_body); | ||||
| } | ||||
| 
 | ||||
| int main(void) | ||||
| { | ||||
| 	return test_harness(lost_exception, "lost_exception"); | ||||
| } | ||||
							
								
								
									
										91
									
								
								tools/testing/selftests/powerpc/pmu/ebb/multi_counter_test.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								tools/testing/selftests/powerpc/pmu/ebb/multi_counter_test.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,91 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <sys/ioctl.h> | ||||
| 
 | ||||
| #include "ebb.h" | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Test counting multiple events using EBBs. | ||||
|  */ | ||||
| int multi_counter(void) | ||||
| { | ||||
| 	struct event events[6]; | ||||
| 	int i, group_fd; | ||||
| 
 | ||||
| 	event_init_named(&events[0], 0x1001C, "PM_CMPLU_STALL_THRD"); | ||||
| 	event_init_named(&events[1], 0x2D016, "PM_CMPLU_STALL_FXU"); | ||||
| 	event_init_named(&events[2], 0x30006, "PM_CMPLU_STALL_OTHER_CMPL"); | ||||
| 	event_init_named(&events[3], 0x4000A, "PM_CMPLU_STALL"); | ||||
| 	event_init_named(&events[4], 0x600f4, "PM_RUN_CYC"); | ||||
| 	event_init_named(&events[5], 0x500fa, "PM_RUN_INST_CMPL"); | ||||
| 
 | ||||
| 	event_leader_ebb_init(&events[0]); | ||||
| 	for (i = 1; i < 6; i++) | ||||
| 		event_ebb_init(&events[i]); | ||||
| 
 | ||||
| 	group_fd = -1; | ||||
| 	for (i = 0; i < 6; i++) { | ||||
| 		events[i].attr.exclude_kernel = 1; | ||||
| 		events[i].attr.exclude_hv = 1; | ||||
| 		events[i].attr.exclude_idle = 1; | ||||
| 
 | ||||
| 		FAIL_IF(event_open_with_group(&events[i], group_fd)); | ||||
| 		if (group_fd == -1) | ||||
| 			group_fd = events[0].fd; | ||||
| 	} | ||||
| 
 | ||||
| 	ebb_enable_pmc_counting(1); | ||||
| 	ebb_enable_pmc_counting(2); | ||||
| 	ebb_enable_pmc_counting(3); | ||||
| 	ebb_enable_pmc_counting(4); | ||||
| 	ebb_enable_pmc_counting(5); | ||||
| 	ebb_enable_pmc_counting(6); | ||||
| 	setup_ebb_handler(standard_ebb_callee); | ||||
| 
 | ||||
| 	FAIL_IF(ioctl(events[0].fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP)); | ||||
| 	FAIL_IF(event_read(&events[0])); | ||||
| 
 | ||||
| 	ebb_global_enable(); | ||||
| 
 | ||||
| 	mtspr(SPRN_PMC1, pmc_sample_period(sample_period)); | ||||
| 	mtspr(SPRN_PMC2, pmc_sample_period(sample_period)); | ||||
| 	mtspr(SPRN_PMC3, pmc_sample_period(sample_period)); | ||||
| 	mtspr(SPRN_PMC4, pmc_sample_period(sample_period)); | ||||
| 	mtspr(SPRN_PMC5, pmc_sample_period(sample_period)); | ||||
| 	mtspr(SPRN_PMC6, pmc_sample_period(sample_period)); | ||||
| 
 | ||||
| 	while (ebb_state.stats.ebb_count < 50) { | ||||
| 		FAIL_IF(core_busy_loop()); | ||||
| 		FAIL_IF(ebb_check_mmcr0()); | ||||
| 	} | ||||
| 
 | ||||
| 	ebb_global_disable(); | ||||
| 	ebb_freeze_pmcs(); | ||||
| 
 | ||||
| 	count_pmc(1, sample_period); | ||||
| 	count_pmc(2, sample_period); | ||||
| 	count_pmc(3, sample_period); | ||||
| 	count_pmc(4, sample_period); | ||||
| 	count_pmc(5, sample_period); | ||||
| 	count_pmc(6, sample_period); | ||||
| 
 | ||||
| 	dump_ebb_state(); | ||||
| 
 | ||||
| 	for (i = 0; i < 6; i++) | ||||
| 		event_close(&events[i]); | ||||
| 
 | ||||
| 	FAIL_IF(ebb_state.stats.ebb_count == 0); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int main(void) | ||||
| { | ||||
| 	return test_harness(multi_counter, "multi_counter"); | ||||
| } | ||||
							
								
								
									
										109
									
								
								tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,109 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdbool.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <signal.h> | ||||
| 
 | ||||
| #include "ebb.h" | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Test running multiple EBB using processes at once on a single CPU. They | ||||
|  * should all run happily without interfering with each other. | ||||
|  */ | ||||
| 
 | ||||
| static bool child_should_exit; | ||||
| 
 | ||||
| static void sigint_handler(int signal) | ||||
| { | ||||
| 	child_should_exit = true; | ||||
| } | ||||
| 
 | ||||
| struct sigaction sigint_action = { | ||||
| 	.sa_handler = sigint_handler, | ||||
| }; | ||||
| 
 | ||||
| static int cycles_child(void) | ||||
| { | ||||
| 	struct event event; | ||||
| 
 | ||||
| 	if (sigaction(SIGINT, &sigint_action, NULL)) { | ||||
| 		perror("sigaction"); | ||||
| 		return 1; | ||||
| 	} | ||||
| 
 | ||||
| 	event_init_named(&event, 0x1001e, "cycles"); | ||||
| 	event_leader_ebb_init(&event); | ||||
| 
 | ||||
| 	event.attr.exclude_kernel = 1; | ||||
| 	event.attr.exclude_hv = 1; | ||||
| 	event.attr.exclude_idle = 1; | ||||
| 
 | ||||
| 	FAIL_IF(event_open(&event)); | ||||
| 
 | ||||
| 	ebb_enable_pmc_counting(1); | ||||
| 	setup_ebb_handler(standard_ebb_callee); | ||||
| 	ebb_global_enable(); | ||||
| 
 | ||||
| 	FAIL_IF(ebb_event_enable(&event)); | ||||
| 
 | ||||
| 	mtspr(SPRN_PMC1, pmc_sample_period(sample_period)); | ||||
| 
 | ||||
| 	while (!child_should_exit) { | ||||
| 		FAIL_IF(core_busy_loop()); | ||||
| 		FAIL_IF(ebb_check_mmcr0()); | ||||
| 	} | ||||
| 
 | ||||
| 	ebb_global_disable(); | ||||
| 	ebb_freeze_pmcs(); | ||||
| 
 | ||||
| 	count_pmc(1, sample_period); | ||||
| 
 | ||||
| 	dump_summary_ebb_state(); | ||||
| 
 | ||||
| 	event_close(&event); | ||||
| 
 | ||||
| 	FAIL_IF(ebb_state.stats.ebb_count == 0); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| #define NR_CHILDREN	4 | ||||
| 
 | ||||
| int multi_ebb_procs(void) | ||||
| { | ||||
| 	pid_t pids[NR_CHILDREN]; | ||||
| 	int cpu, rc, i; | ||||
| 
 | ||||
| 	cpu = pick_online_cpu(); | ||||
| 	FAIL_IF(cpu < 0); | ||||
| 	FAIL_IF(bind_to_cpu(cpu)); | ||||
| 
 | ||||
| 	for (i = 0; i < NR_CHILDREN; i++) { | ||||
| 		pids[i] = fork(); | ||||
| 		if (pids[i] == 0) | ||||
| 			exit(cycles_child()); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Have them all run for "a while" */ | ||||
| 	sleep(10); | ||||
| 
 | ||||
| 	rc = 0; | ||||
| 	for (i = 0; i < NR_CHILDREN; i++) { | ||||
| 		/* Tell them to stop */ | ||||
| 		kill(pids[i], SIGINT); | ||||
| 		/* And wait */ | ||||
| 		rc |= wait_for_child(pids[i]); | ||||
| 	} | ||||
| 
 | ||||
| 	return rc; | ||||
| } | ||||
| 
 | ||||
| int main(void) | ||||
| { | ||||
| 	return test_harness(multi_ebb_procs, "multi_ebb_procs"); | ||||
| } | ||||
							
								
								
									
										61
									
								
								tools/testing/selftests/powerpc/pmu/ebb/no_handler_test.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								tools/testing/selftests/powerpc/pmu/ebb/no_handler_test.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,61 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <setjmp.h> | ||||
| #include <signal.h> | ||||
| 
 | ||||
| #include "ebb.h" | ||||
| 
 | ||||
| 
 | ||||
| /* Test that things work sanely if we have no handler */ | ||||
| 
 | ||||
| static int no_handler_test(void) | ||||
| { | ||||
| 	struct event event; | ||||
| 	u64 val; | ||||
| 	int i; | ||||
| 
 | ||||
| 	event_init_named(&event, 0x1001e, "cycles"); | ||||
| 	event_leader_ebb_init(&event); | ||||
| 
 | ||||
| 	event.attr.exclude_kernel = 1; | ||||
| 	event.attr.exclude_hv = 1; | ||||
| 	event.attr.exclude_idle = 1; | ||||
| 
 | ||||
| 	FAIL_IF(event_open(&event)); | ||||
| 	FAIL_IF(ebb_event_enable(&event)); | ||||
| 
 | ||||
| 	val = mfspr(SPRN_EBBHR); | ||||
| 	FAIL_IF(val != 0); | ||||
| 
 | ||||
| 	/* Make sure it overflows quickly */ | ||||
| 	sample_period = 1000; | ||||
| 	mtspr(SPRN_PMC1, pmc_sample_period(sample_period)); | ||||
| 
 | ||||
| 	/* Spin to make sure the event has time to overflow */ | ||||
| 	for (i = 0; i < 1000; i++) | ||||
| 		mb(); | ||||
| 
 | ||||
| 	dump_ebb_state(); | ||||
| 
 | ||||
| 	/* We expect to see the PMU frozen & PMAO set */ | ||||
| 	val = mfspr(SPRN_MMCR0); | ||||
| 	FAIL_IF(val != 0x0000000080000080); | ||||
| 
 | ||||
| 	event_close(&event); | ||||
| 
 | ||||
| 	dump_ebb_state(); | ||||
| 
 | ||||
| 	/* The real test is that we never took an EBB at 0x0 */ | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int main(void) | ||||
| { | ||||
| 	return test_harness(no_handler_test,"no_handler_test"); | ||||
| } | ||||
							
								
								
									
										106
									
								
								tools/testing/selftests/powerpc/pmu/ebb/pmae_handling_test.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								tools/testing/selftests/powerpc/pmu/ebb/pmae_handling_test.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,106 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #include <sched.h> | ||||
| #include <signal.h> | ||||
| #include <stdbool.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #include "ebb.h" | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Test that the kernel properly handles PMAE across context switches. | ||||
|  * | ||||
|  * We test this by calling into the kernel inside our EBB handler, where PMAE | ||||
|  * is clear. A cpu eater companion thread is running on the same CPU as us to | ||||
|  * encourage the scheduler to switch us. | ||||
|  * | ||||
|  * The kernel must make sure that when it context switches us back in, it | ||||
|  * honours the fact that we had PMAE clear. | ||||
|  * | ||||
|  * Observed to hit the failing case on the first EBB with a broken kernel. | ||||
|  */ | ||||
| 
 | ||||
| static bool mmcr0_mismatch; | ||||
| static uint64_t before, after; | ||||
| 
 | ||||
| static void syscall_ebb_callee(void) | ||||
| { | ||||
| 	uint64_t val; | ||||
| 
 | ||||
| 	val = mfspr(SPRN_BESCR); | ||||
| 	if (!(val & BESCR_PMEO)) { | ||||
| 		ebb_state.stats.spurious++; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	ebb_state.stats.ebb_count++; | ||||
| 	count_pmc(1, sample_period); | ||||
| 
 | ||||
| 	before = mfspr(SPRN_MMCR0); | ||||
| 
 | ||||
| 	/* Try and get ourselves scheduled, to force a PMU context switch */ | ||||
| 	sched_yield(); | ||||
| 
 | ||||
| 	after = mfspr(SPRN_MMCR0); | ||||
| 	if (before != after) | ||||
| 		mmcr0_mismatch = true; | ||||
| 
 | ||||
| out: | ||||
| 	reset_ebb(); | ||||
| } | ||||
| 
 | ||||
| static int test_body(void) | ||||
| { | ||||
| 	struct event event; | ||||
| 
 | ||||
| 	event_init_named(&event, 0x1001e, "cycles"); | ||||
| 	event_leader_ebb_init(&event); | ||||
| 
 | ||||
| 	event.attr.exclude_kernel = 1; | ||||
| 	event.attr.exclude_hv = 1; | ||||
| 	event.attr.exclude_idle = 1; | ||||
| 
 | ||||
| 	FAIL_IF(event_open(&event)); | ||||
| 
 | ||||
| 	setup_ebb_handler(syscall_ebb_callee); | ||||
| 	ebb_global_enable(); | ||||
| 
 | ||||
| 	FAIL_IF(ebb_event_enable(&event)); | ||||
| 
 | ||||
| 	mtspr(SPRN_PMC1, pmc_sample_period(sample_period)); | ||||
| 
 | ||||
| 	while (ebb_state.stats.ebb_count < 20 && !mmcr0_mismatch) | ||||
| 		FAIL_IF(core_busy_loop()); | ||||
| 
 | ||||
| 	ebb_global_disable(); | ||||
| 	ebb_freeze_pmcs(); | ||||
| 
 | ||||
| 	count_pmc(1, sample_period); | ||||
| 
 | ||||
| 	dump_ebb_state(); | ||||
| 
 | ||||
| 	if (mmcr0_mismatch) | ||||
| 		printf("Saw MMCR0 before 0x%lx after 0x%lx\n", before, after); | ||||
| 
 | ||||
| 	event_close(&event); | ||||
| 
 | ||||
| 	FAIL_IF(ebb_state.stats.ebb_count == 0); | ||||
| 	FAIL_IF(mmcr0_mismatch); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int pmae_handling(void) | ||||
| { | ||||
| 	return eat_cpu(test_body); | ||||
| } | ||||
| 
 | ||||
| int main(void) | ||||
| { | ||||
| 	return test_harness(pmae_handling, "pmae_handling"); | ||||
| } | ||||
|  | @ -0,0 +1,93 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #include "ebb.h" | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Test that PMC5 & 6 are frozen (ie. don't overflow) when they are not being | ||||
|  * used. Tests the MMCR0_FC56 logic in the kernel. | ||||
|  */ | ||||
| 
 | ||||
| static int pmc56_overflowed; | ||||
| 
 | ||||
| static void ebb_callee(void) | ||||
| { | ||||
| 	uint64_t val; | ||||
| 
 | ||||
| 	val = mfspr(SPRN_BESCR); | ||||
| 	if (!(val & BESCR_PMEO)) { | ||||
| 		ebb_state.stats.spurious++; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	ebb_state.stats.ebb_count++; | ||||
| 	count_pmc(2, sample_period); | ||||
| 
 | ||||
| 	val = mfspr(SPRN_PMC5); | ||||
| 	if (val >= COUNTER_OVERFLOW) | ||||
| 		pmc56_overflowed++; | ||||
| 
 | ||||
| 	count_pmc(5, COUNTER_OVERFLOW); | ||||
| 
 | ||||
| 	val = mfspr(SPRN_PMC6); | ||||
| 	if (val >= COUNTER_OVERFLOW) | ||||
| 		pmc56_overflowed++; | ||||
| 
 | ||||
| 	count_pmc(6, COUNTER_OVERFLOW); | ||||
| 
 | ||||
| out: | ||||
| 	reset_ebb(); | ||||
| } | ||||
| 
 | ||||
| int pmc56_overflow(void) | ||||
| { | ||||
| 	struct event event; | ||||
| 
 | ||||
| 	/* Use PMC2 so we set PMCjCE, which enables PMC5/6 */ | ||||
| 	event_init(&event, 0x2001e); | ||||
| 	event_leader_ebb_init(&event); | ||||
| 
 | ||||
| 	event.attr.exclude_kernel = 1; | ||||
| 	event.attr.exclude_hv = 1; | ||||
| 	event.attr.exclude_idle = 1; | ||||
| 
 | ||||
| 	FAIL_IF(event_open(&event)); | ||||
| 
 | ||||
| 	setup_ebb_handler(ebb_callee); | ||||
| 	ebb_global_enable(); | ||||
| 
 | ||||
| 	FAIL_IF(ebb_event_enable(&event)); | ||||
| 
 | ||||
| 	mtspr(SPRN_PMC1, pmc_sample_period(sample_period)); | ||||
| 	mtspr(SPRN_PMC5, 0); | ||||
| 	mtspr(SPRN_PMC6, 0); | ||||
| 
 | ||||
| 	while (ebb_state.stats.ebb_count < 10) | ||||
| 		FAIL_IF(core_busy_loop()); | ||||
| 
 | ||||
| 	ebb_global_disable(); | ||||
| 	ebb_freeze_pmcs(); | ||||
| 
 | ||||
| 	count_pmc(2, sample_period); | ||||
| 
 | ||||
| 	dump_ebb_state(); | ||||
| 
 | ||||
| 	printf("PMC5/6 overflow %d\n", pmc56_overflowed); | ||||
| 
 | ||||
| 	event_close(&event); | ||||
| 
 | ||||
| 	FAIL_IF(ebb_state.stats.ebb_count == 0 || pmc56_overflowed != 0); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int main(void) | ||||
| { | ||||
| 	return test_harness(pmc56_overflow, "pmc56_overflow"); | ||||
| } | ||||
							
								
								
									
										49
									
								
								tools/testing/selftests/powerpc/pmu/ebb/reg.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								tools/testing/selftests/powerpc/pmu/ebb/reg.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _SELFTESTS_POWERPC_REG_H | ||||
| #define _SELFTESTS_POWERPC_REG_H | ||||
| 
 | ||||
| #define __stringify_1(x)        #x | ||||
| #define __stringify(x)          __stringify_1(x) | ||||
| 
 | ||||
| #define mfspr(rn)       ({unsigned long rval; \ | ||||
|                          asm volatile("mfspr %0," __stringify(rn) \ | ||||
|                                  : "=r" (rval)); rval; }) | ||||
| #define mtspr(rn, v)    asm volatile("mtspr " __stringify(rn) ",%0" : \ | ||||
|                                     : "r" ((unsigned long)(v)) \ | ||||
|                                     : "memory") | ||||
| 
 | ||||
| #define mb()		asm volatile("sync" : : : "memory"); | ||||
| 
 | ||||
| #define SPRN_MMCR2     769 | ||||
| #define SPRN_MMCRA     770 | ||||
| #define SPRN_MMCR0     779 | ||||
| #define   MMCR0_PMAO   0x00000080 | ||||
| #define   MMCR0_PMAE   0x04000000 | ||||
| #define   MMCR0_FC     0x80000000 | ||||
| #define SPRN_EBBHR     804 | ||||
| #define SPRN_EBBRR     805 | ||||
| #define SPRN_BESCR     806     /* Branch event status & control register */ | ||||
| #define SPRN_BESCRS    800     /* Branch event status & control set (1 bits set to 1) */ | ||||
| #define SPRN_BESCRSU   801     /* Branch event status & control set upper */ | ||||
| #define SPRN_BESCRR    802     /* Branch event status & control REset (1 bits set to 0) */ | ||||
| #define SPRN_BESCRRU   803     /* Branch event status & control REset upper */ | ||||
| 
 | ||||
| #define BESCR_PMEO     0x1     /* PMU Event-based exception Occurred */ | ||||
| #define BESCR_PME      (0x1ul << 32) /* PMU Event-based exception Enable */ | ||||
| 
 | ||||
| #define SPRN_PMC1      771 | ||||
| #define SPRN_PMC2      772 | ||||
| #define SPRN_PMC3      773 | ||||
| #define SPRN_PMC4      774 | ||||
| #define SPRN_PMC5      775 | ||||
| #define SPRN_PMC6      776 | ||||
| 
 | ||||
| #define SPRN_SIAR      780 | ||||
| #define SPRN_SDAR      781 | ||||
| #define SPRN_SIER      768 | ||||
| 
 | ||||
| #endif /* _SELFTESTS_POWERPC_REG_H */ | ||||
							
								
								
									
										39
									
								
								tools/testing/selftests/powerpc/pmu/ebb/reg_access_test.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								tools/testing/selftests/powerpc/pmu/ebb/reg_access_test.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,39 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #include "ebb.h" | ||||
| #include "reg.h" | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Test basic access to the EBB regs, they should be user accessible with no | ||||
|  * kernel interaction required. | ||||
|  */ | ||||
| int reg_access(void) | ||||
| { | ||||
| 	uint64_t val, expected; | ||||
| 
 | ||||
| 	expected = 0x8000000100000000ull; | ||||
| 	mtspr(SPRN_BESCR, expected); | ||||
| 	val = mfspr(SPRN_BESCR); | ||||
| 
 | ||||
| 	FAIL_IF(val != expected); | ||||
| 
 | ||||
| 	expected = 0x0000000001000000ull; | ||||
| 	mtspr(SPRN_EBBHR, expected); | ||||
| 	val = mfspr(SPRN_EBBHR); | ||||
| 
 | ||||
| 	FAIL_IF(val != expected); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int main(void) | ||||
| { | ||||
| 	return test_harness(reg_access, "reg_access"); | ||||
| } | ||||
|  | @ -0,0 +1,91 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #include <signal.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdbool.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/wait.h> | ||||
| #include <unistd.h> | ||||
| 
 | ||||
| #include "ebb.h" | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Tests a pinned per-task event vs an EBB - in that order. The pinned per-task | ||||
|  * event should prevent the EBB event from being enabled. | ||||
|  */ | ||||
| 
 | ||||
| static int setup_child_event(struct event *event, pid_t child_pid) | ||||
| { | ||||
| 	event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL"); | ||||
| 
 | ||||
| 	event->attr.pinned = 1; | ||||
| 
 | ||||
| 	event->attr.exclude_kernel = 1; | ||||
| 	event->attr.exclude_hv = 1; | ||||
| 	event->attr.exclude_idle = 1; | ||||
| 
 | ||||
| 	FAIL_IF(event_open_with_pid(event, child_pid)); | ||||
| 	FAIL_IF(event_enable(event)); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int task_event_pinned_vs_ebb(void) | ||||
| { | ||||
| 	union pipe read_pipe, write_pipe; | ||||
| 	struct event event; | ||||
| 	pid_t pid; | ||||
| 	int rc; | ||||
| 
 | ||||
| 	FAIL_IF(pipe(read_pipe.fds) == -1); | ||||
| 	FAIL_IF(pipe(write_pipe.fds) == -1); | ||||
| 
 | ||||
| 	pid = fork(); | ||||
| 	if (pid == 0) { | ||||
| 		/* NB order of pipes looks reversed */ | ||||
| 		exit(ebb_child(write_pipe, read_pipe)); | ||||
| 	} | ||||
| 
 | ||||
| 	/* We setup the task event first */ | ||||
| 	rc = setup_child_event(&event, pid); | ||||
| 	if (rc) { | ||||
| 		kill_child_and_wait(pid); | ||||
| 		return rc; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Signal the child to install its EBB event and wait */ | ||||
| 	if (sync_with_child(read_pipe, write_pipe)) | ||||
| 		/* If it fails, wait for it to exit */ | ||||
| 		goto wait; | ||||
| 
 | ||||
| 	/* Signal the child to run */ | ||||
| 	FAIL_IF(sync_with_child(read_pipe, write_pipe)); | ||||
| 
 | ||||
| wait: | ||||
| 	/* We expect it to fail to read the event */ | ||||
| 	FAIL_IF(wait_for_child(pid) != 2); | ||||
| 	FAIL_IF(event_disable(&event)); | ||||
| 	FAIL_IF(event_read(&event)); | ||||
| 
 | ||||
| 	event_report(&event); | ||||
| 
 | ||||
| 	FAIL_IF(event.result.value == 0); | ||||
| 	/*
 | ||||
| 	 * For reasons I don't understand enabled is usually just slightly | ||||
| 	 * lower than running. Would be good to confirm why. | ||||
| 	 */ | ||||
| 	FAIL_IF(event.result.enabled == 0); | ||||
| 	FAIL_IF(event.result.running == 0); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int main(void) | ||||
| { | ||||
| 	return test_harness(task_event_pinned_vs_ebb, "task_event_pinned_vs_ebb"); | ||||
| } | ||||
|  | @ -0,0 +1,83 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #include <signal.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdbool.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/wait.h> | ||||
| #include <unistd.h> | ||||
| 
 | ||||
| #include "ebb.h" | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Tests a per-task event vs an EBB - in that order. The EBB should push the | ||||
|  * per-task event off the PMU. | ||||
|  */ | ||||
| 
 | ||||
| static int setup_child_event(struct event *event, pid_t child_pid) | ||||
| { | ||||
| 	event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL"); | ||||
| 
 | ||||
| 	event->attr.exclude_kernel = 1; | ||||
| 	event->attr.exclude_hv = 1; | ||||
| 	event->attr.exclude_idle = 1; | ||||
| 
 | ||||
| 	FAIL_IF(event_open_with_pid(event, child_pid)); | ||||
| 	FAIL_IF(event_enable(event)); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int task_event_vs_ebb(void) | ||||
| { | ||||
| 	union pipe read_pipe, write_pipe; | ||||
| 	struct event event; | ||||
| 	pid_t pid; | ||||
| 	int rc; | ||||
| 
 | ||||
| 	FAIL_IF(pipe(read_pipe.fds) == -1); | ||||
| 	FAIL_IF(pipe(write_pipe.fds) == -1); | ||||
| 
 | ||||
| 	pid = fork(); | ||||
| 	if (pid == 0) { | ||||
| 		/* NB order of pipes looks reversed */ | ||||
| 		exit(ebb_child(write_pipe, read_pipe)); | ||||
| 	} | ||||
| 
 | ||||
| 	/* We setup the task event first */ | ||||
| 	rc = setup_child_event(&event, pid); | ||||
| 	if (rc) { | ||||
| 		kill_child_and_wait(pid); | ||||
| 		return rc; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Signal the child to install its EBB event and wait */ | ||||
| 	if (sync_with_child(read_pipe, write_pipe)) | ||||
| 		/* If it fails, wait for it to exit */ | ||||
| 		goto wait; | ||||
| 
 | ||||
| 	/* Signal the child to run */ | ||||
| 	FAIL_IF(sync_with_child(read_pipe, write_pipe)); | ||||
| 
 | ||||
| wait: | ||||
| 	/* The EBB event should push the task event off so the child should succeed */ | ||||
| 	FAIL_IF(wait_for_child(pid)); | ||||
| 	FAIL_IF(event_disable(&event)); | ||||
| 	FAIL_IF(event_read(&event)); | ||||
| 
 | ||||
| 	event_report(&event); | ||||
| 
 | ||||
| 	/* The task event may have run, or not so we can't assert anything about it */ | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int main(void) | ||||
| { | ||||
| 	return test_harness(task_event_vs_ebb, "task_event_vs_ebb"); | ||||
| } | ||||
							
								
								
									
										300
									
								
								tools/testing/selftests/powerpc/pmu/ebb/trace.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										300
									
								
								tools/testing/selftests/powerpc/pmu/ebb/trace.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,300 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #include <errno.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <sys/mman.h> | ||||
| 
 | ||||
| #include "trace.h" | ||||
| 
 | ||||
| 
 | ||||
| struct trace_buffer *trace_buffer_allocate(u64 size) | ||||
| { | ||||
| 	struct trace_buffer *tb; | ||||
| 
 | ||||
| 	if (size < sizeof(*tb)) { | ||||
| 		fprintf(stderr, "Error: trace buffer too small\n"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	tb = mmap(NULL, size, PROT_READ | PROT_WRITE, | ||||
| 		  MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); | ||||
| 	if (tb == MAP_FAILED) { | ||||
| 		perror("mmap"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	tb->size = size; | ||||
| 	tb->tail = tb->data; | ||||
| 	tb->overflow = false; | ||||
| 
 | ||||
| 	return tb; | ||||
| } | ||||
| 
 | ||||
| static bool trace_check_bounds(struct trace_buffer *tb, void *p) | ||||
| { | ||||
| 	return p < ((void *)tb + tb->size); | ||||
| } | ||||
| 
 | ||||
| static bool trace_check_alloc(struct trace_buffer *tb, void *p) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * If we ever overflowed don't allow any more input. This prevents us | ||||
| 	 * from dropping a large item and then later logging a small one. The | ||||
| 	 * buffer should just stop when overflow happened, not be patchy. If | ||||
| 	 * you're overflowing, make your buffer bigger. | ||||
| 	 */ | ||||
| 	if (tb->overflow) | ||||
| 		return false; | ||||
| 
 | ||||
| 	if (!trace_check_bounds(tb, p)) { | ||||
| 		tb->overflow = true; | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| static void *trace_alloc(struct trace_buffer *tb, int bytes) | ||||
| { | ||||
| 	void *p, *newtail; | ||||
| 
 | ||||
| 	p = tb->tail; | ||||
| 	newtail = tb->tail + bytes; | ||||
| 	if (!trace_check_alloc(tb, newtail)) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	tb->tail = newtail; | ||||
| 
 | ||||
| 	return p; | ||||
| } | ||||
| 
 | ||||
| static struct trace_entry *trace_alloc_entry(struct trace_buffer *tb, int payload_size) | ||||
| { | ||||
| 	struct trace_entry *e; | ||||
| 
 | ||||
| 	e = trace_alloc(tb, sizeof(*e) + payload_size); | ||||
| 	if (e) | ||||
| 		e->length = payload_size; | ||||
| 
 | ||||
| 	return e; | ||||
| } | ||||
| 
 | ||||
| int trace_log_reg(struct trace_buffer *tb, u64 reg, u64 value) | ||||
| { | ||||
| 	struct trace_entry *e; | ||||
| 	u64 *p; | ||||
| 
 | ||||
| 	e = trace_alloc_entry(tb, sizeof(reg) + sizeof(value)); | ||||
| 	if (!e) | ||||
| 		return -ENOSPC; | ||||
| 
 | ||||
| 	e->type = TRACE_TYPE_REG; | ||||
| 	p = (u64 *)e->data; | ||||
| 	*p++ = reg; | ||||
| 	*p++ = value; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int trace_log_counter(struct trace_buffer *tb, u64 value) | ||||
| { | ||||
| 	struct trace_entry *e; | ||||
| 	u64 *p; | ||||
| 
 | ||||
| 	e = trace_alloc_entry(tb, sizeof(value)); | ||||
| 	if (!e) | ||||
| 		return -ENOSPC; | ||||
| 
 | ||||
| 	e->type = TRACE_TYPE_COUNTER; | ||||
| 	p = (u64 *)e->data; | ||||
| 	*p++ = value; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int trace_log_string(struct trace_buffer *tb, char *str) | ||||
| { | ||||
| 	struct trace_entry *e; | ||||
| 	char *p; | ||||
| 	int len; | ||||
| 
 | ||||
| 	len = strlen(str); | ||||
| 
 | ||||
| 	/* We NULL terminate to make printing easier */ | ||||
| 	e = trace_alloc_entry(tb, len + 1); | ||||
| 	if (!e) | ||||
| 		return -ENOSPC; | ||||
| 
 | ||||
| 	e->type = TRACE_TYPE_STRING; | ||||
| 	p = (char *)e->data; | ||||
| 	memcpy(p, str, len); | ||||
| 	p += len; | ||||
| 	*p = '\0'; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int trace_log_indent(struct trace_buffer *tb) | ||||
| { | ||||
| 	struct trace_entry *e; | ||||
| 
 | ||||
| 	e = trace_alloc_entry(tb, 0); | ||||
| 	if (!e) | ||||
| 		return -ENOSPC; | ||||
| 
 | ||||
| 	e->type = TRACE_TYPE_INDENT; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int trace_log_outdent(struct trace_buffer *tb) | ||||
| { | ||||
| 	struct trace_entry *e; | ||||
| 
 | ||||
| 	e = trace_alloc_entry(tb, 0); | ||||
| 	if (!e) | ||||
| 		return -ENOSPC; | ||||
| 
 | ||||
| 	e->type = TRACE_TYPE_OUTDENT; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void trace_print_header(int seq, int prefix) | ||||
| { | ||||
| 	printf("%*s[%d]: ", prefix, "", seq); | ||||
| } | ||||
| 
 | ||||
| static char *trace_decode_reg(int reg) | ||||
| { | ||||
| 	switch (reg) { | ||||
| 		case 769: return "SPRN_MMCR2"; break; | ||||
| 		case 770: return "SPRN_MMCRA"; break; | ||||
| 		case 779: return "SPRN_MMCR0"; break; | ||||
| 		case 804: return "SPRN_EBBHR"; break; | ||||
| 		case 805: return "SPRN_EBBRR"; break; | ||||
| 		case 806: return "SPRN_BESCR"; break; | ||||
| 		case 800: return "SPRN_BESCRS"; break; | ||||
| 		case 801: return "SPRN_BESCRSU"; break; | ||||
| 		case 802: return "SPRN_BESCRR"; break; | ||||
| 		case 803: return "SPRN_BESCRRU"; break; | ||||
| 		case 771: return "SPRN_PMC1"; break; | ||||
| 		case 772: return "SPRN_PMC2"; break; | ||||
| 		case 773: return "SPRN_PMC3"; break; | ||||
| 		case 774: return "SPRN_PMC4"; break; | ||||
| 		case 775: return "SPRN_PMC5"; break; | ||||
| 		case 776: return "SPRN_PMC6"; break; | ||||
| 		case 780: return "SPRN_SIAR"; break; | ||||
| 		case 781: return "SPRN_SDAR"; break; | ||||
| 		case 768: return "SPRN_SIER"; break; | ||||
| 	} | ||||
| 
 | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static void trace_print_reg(struct trace_entry *e) | ||||
| { | ||||
| 	u64 *p, *reg, *value; | ||||
| 	char *name; | ||||
| 
 | ||||
| 	p = (u64 *)e->data; | ||||
| 	reg = p++; | ||||
| 	value = p; | ||||
| 
 | ||||
| 	name = trace_decode_reg(*reg); | ||||
| 	if (name) | ||||
| 		printf("register %-10s = 0x%016llx\n", name, *value); | ||||
| 	else | ||||
| 		printf("register %lld = 0x%016llx\n", *reg, *value); | ||||
| } | ||||
| 
 | ||||
| static void trace_print_counter(struct trace_entry *e) | ||||
| { | ||||
| 	u64 *value; | ||||
| 
 | ||||
| 	value = (u64 *)e->data; | ||||
| 	printf("counter = %lld\n", *value); | ||||
| } | ||||
| 
 | ||||
| static void trace_print_string(struct trace_entry *e) | ||||
| { | ||||
| 	char *str; | ||||
| 
 | ||||
| 	str = (char *)e->data; | ||||
| 	puts(str); | ||||
| } | ||||
| 
 | ||||
| #define BASE_PREFIX	2 | ||||
| #define PREFIX_DELTA	8 | ||||
| 
 | ||||
| static void trace_print_entry(struct trace_entry *e, int seq, int *prefix) | ||||
| { | ||||
| 	switch (e->type) { | ||||
| 	case TRACE_TYPE_REG: | ||||
| 		trace_print_header(seq, *prefix); | ||||
| 		trace_print_reg(e); | ||||
| 		break; | ||||
| 	case TRACE_TYPE_COUNTER: | ||||
| 		trace_print_header(seq, *prefix); | ||||
| 		trace_print_counter(e); | ||||
| 		break; | ||||
| 	case TRACE_TYPE_STRING: | ||||
| 		trace_print_header(seq, *prefix); | ||||
| 		trace_print_string(e); | ||||
| 		break; | ||||
| 	case TRACE_TYPE_INDENT: | ||||
| 		trace_print_header(seq, *prefix); | ||||
| 		puts("{"); | ||||
| 		*prefix += PREFIX_DELTA; | ||||
| 		break; | ||||
| 	case TRACE_TYPE_OUTDENT: | ||||
| 		*prefix -= PREFIX_DELTA; | ||||
| 		if (*prefix < BASE_PREFIX) | ||||
| 			*prefix = BASE_PREFIX; | ||||
| 		trace_print_header(seq, *prefix); | ||||
| 		puts("}"); | ||||
| 		break; | ||||
| 	default: | ||||
| 		trace_print_header(seq, *prefix); | ||||
| 		printf("entry @ %p type %d\n", e, e->type); | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void trace_buffer_print(struct trace_buffer *tb) | ||||
| { | ||||
| 	struct trace_entry *e; | ||||
| 	int i, prefix; | ||||
| 	void *p; | ||||
| 
 | ||||
| 	printf("Trace buffer dump:\n"); | ||||
| 	printf("  address  %p \n", tb); | ||||
| 	printf("  tail     %p\n", tb->tail); | ||||
| 	printf("  size     %llu\n", tb->size); | ||||
| 	printf("  overflow %s\n", tb->overflow ? "TRUE" : "false"); | ||||
| 	printf("  Content:\n"); | ||||
| 
 | ||||
| 	p = tb->data; | ||||
| 
 | ||||
| 	i = 0; | ||||
| 	prefix = BASE_PREFIX; | ||||
| 
 | ||||
| 	while (trace_check_bounds(tb, p) && p < tb->tail) { | ||||
| 		e = p; | ||||
| 
 | ||||
| 		trace_print_entry(e, i, &prefix); | ||||
| 
 | ||||
| 		i++; | ||||
| 		p = (void *)e + sizeof(*e) + e->length; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void trace_print_location(struct trace_buffer *tb) | ||||
| { | ||||
| 	printf("Trace buffer 0x%llx bytes @ %p\n", tb->size, tb); | ||||
| } | ||||
							
								
								
									
										41
									
								
								tools/testing/selftests/powerpc/pmu/ebb/trace.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								tools/testing/selftests/powerpc/pmu/ebb/trace.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,41 @@ | |||
| /*
 | ||||
|  * Copyright 2014, Michael Ellerman, IBM Corp. | ||||
|  * Licensed under GPLv2. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _SELFTESTS_POWERPC_PMU_EBB_TRACE_H | ||||
| #define _SELFTESTS_POWERPC_PMU_EBB_TRACE_H | ||||
| 
 | ||||
| #include "utils.h" | ||||
| 
 | ||||
| #define TRACE_TYPE_REG		1 | ||||
| #define TRACE_TYPE_COUNTER	2 | ||||
| #define TRACE_TYPE_STRING	3 | ||||
| #define TRACE_TYPE_INDENT	4 | ||||
| #define TRACE_TYPE_OUTDENT	5 | ||||
| 
 | ||||
| struct trace_entry | ||||
| { | ||||
| 	u8 type; | ||||
| 	u8 length; | ||||
| 	u8 data[0]; | ||||
| }; | ||||
| 
 | ||||
| struct trace_buffer | ||||
| { | ||||
| 	u64  size; | ||||
| 	bool overflow; | ||||
| 	void *tail; | ||||
| 	u8   data[0]; | ||||
| }; | ||||
| 
 | ||||
| struct trace_buffer *trace_buffer_allocate(u64 size); | ||||
| int trace_log_reg(struct trace_buffer *tb, u64 reg, u64 value); | ||||
| int trace_log_counter(struct trace_buffer *tb, u64 value); | ||||
| int trace_log_string(struct trace_buffer *tb, char *str); | ||||
| int trace_log_indent(struct trace_buffer *tb); | ||||
| int trace_log_outdent(struct trace_buffer *tb); | ||||
| void trace_buffer_print(struct trace_buffer *tb); | ||||
| void trace_print_location(struct trace_buffer *tb); | ||||
| 
 | ||||
| #endif /* _SELFTESTS_POWERPC_PMU_EBB_TRACE_H */ | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 awab228
						awab228