 35000870fc
			
		
	
	
	35000870fc
	
	
	
		
			
			Add two optimisations to enable_kernel_altivec: - enable_kernel_altivec has already determined if we need to save the previous task's state but we call giveup_altivec in both cases, requiring an extra branch in giveup_altivec. Create giveup_altivec_notask which only turns on the VMX bit in the MSR. - We write the VMX MSR bit each time we call enable_kernel_altivec even it was already set. Check the bit and branch out if we have already set it. The classic case for this is vectored IO where we have to copy multiple buffers to or from userspace. The following testcase was used to confirm this patch improves performance: http://ozlabs.org/~anton/junkcode/copy_to_user.c Since the current breakpoint for using VMX in copy_tofrom_user is 4096 bytes, I'm using buffers of 4096 + 1 cacheline (4224) bytes. A benchmark of 16 entry readvs (-s 16): time copy_to_user -l 4224 -s 16 -i 1000000 completes 5.2% faster on a POWER7 PS700. Signed-off-by: Anton Blanchard <anton@samba.org> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
		
			
				
	
	
		
			417 lines
		
	
	
	
		
			8.7 KiB
			
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
			
		
		
	
	
			417 lines
		
	
	
	
		
			8.7 KiB
			
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
| #include <asm/processor.h>
 | |
| #include <asm/ppc_asm.h>
 | |
| #include <asm/reg.h>
 | |
| #include <asm/asm-offsets.h>
 | |
| #include <asm/cputable.h>
 | |
| #include <asm/thread_info.h>
 | |
| #include <asm/page.h>
 | |
| #include <asm/ptrace.h>
 | |
| 
 | |
| /*
 | |
|  * load_up_altivec(unused, unused, tsk)
 | |
|  * Disable VMX for the task which had it previously,
 | |
|  * and save its vector registers in its thread_struct.
 | |
|  * Enables the VMX for use in the kernel on return.
 | |
|  * On SMP we know the VMX is free, since we give it up every
 | |
|  * switch (ie, no lazy save of the vector registers).
 | |
|  */
 | |
| _GLOBAL(load_up_altivec)
 | |
| 	mfmsr	r5			/* grab the current MSR */
 | |
| 	oris	r5,r5,MSR_VEC@h
 | |
| 	MTMSRD(r5)			/* enable use of AltiVec now */
 | |
| 	isync
 | |
| 
 | |
| /*
 | |
|  * For SMP, we don't do lazy VMX switching because it just gets too
 | |
|  * horrendously complex, especially when a task switches from one CPU
 | |
|  * to another.  Instead we call giveup_altvec in switch_to.
 | |
|  * VRSAVE isn't dealt with here, that is done in the normal context
 | |
|  * switch code. Note that we could rely on vrsave value to eventually
 | |
|  * avoid saving all of the VREGs here...
 | |
|  */
 | |
| #ifndef CONFIG_SMP
 | |
| 	LOAD_REG_ADDRBASE(r3, last_task_used_altivec)
 | |
| 	toreal(r3)
 | |
| 	PPC_LL	r4,ADDROFF(last_task_used_altivec)(r3)
 | |
| 	PPC_LCMPI	0,r4,0
 | |
| 	beq	1f
 | |
| 
 | |
| 	/* Save VMX state to last_task_used_altivec's THREAD struct */
 | |
| 	toreal(r4)
 | |
| 	addi	r4,r4,THREAD
 | |
| 	SAVE_32VRS(0,r5,r4)
 | |
| 	mfvscr	vr0
 | |
| 	li	r10,THREAD_VSCR
 | |
| 	stvx	vr0,r10,r4
 | |
| 	/* Disable VMX for last_task_used_altivec */
 | |
| 	PPC_LL	r5,PT_REGS(r4)
 | |
| 	toreal(r5)
 | |
| 	PPC_LL	r4,_MSR-STACK_FRAME_OVERHEAD(r5)
 | |
| 	lis	r10,MSR_VEC@h
 | |
| 	andc	r4,r4,r10
 | |
| 	PPC_STL	r4,_MSR-STACK_FRAME_OVERHEAD(r5)
 | |
| 1:
 | |
| #endif /* CONFIG_SMP */
 | |
| 
 | |
| 	/* Hack: if we get an altivec unavailable trap with VRSAVE
 | |
| 	 * set to all zeros, we assume this is a broken application
 | |
| 	 * that fails to set it properly, and thus we switch it to
 | |
| 	 * all 1's
 | |
| 	 */
 | |
| 	mfspr	r4,SPRN_VRSAVE
 | |
| 	cmpwi	0,r4,0
 | |
| 	bne+	1f
 | |
| 	li	r4,-1
 | |
| 	mtspr	SPRN_VRSAVE,r4
 | |
| 1:
 | |
| 	/* enable use of VMX after return */
 | |
| #ifdef CONFIG_PPC32
 | |
| 	mfspr	r5,SPRN_SPRG_THREAD		/* current task's THREAD (phys) */
 | |
| 	oris	r9,r9,MSR_VEC@h
 | |
| #else
 | |
| 	ld	r4,PACACURRENT(r13)
 | |
| 	addi	r5,r4,THREAD		/* Get THREAD */
 | |
| 	oris	r12,r12,MSR_VEC@h
 | |
| 	std	r12,_MSR(r1)
 | |
| #endif
 | |
| 	li	r4,1
 | |
| 	li	r10,THREAD_VSCR
 | |
| 	stw	r4,THREAD_USED_VR(r5)
 | |
| 	lvx	vr0,r10,r5
 | |
| 	mtvscr	vr0
 | |
| 	REST_32VRS(0,r4,r5)
 | |
| #ifndef CONFIG_SMP
 | |
| 	/* Update last_task_used_altivec to 'current' */
 | |
| 	subi	r4,r5,THREAD		/* Back to 'current' */
 | |
| 	fromreal(r4)
 | |
| 	PPC_STL	r4,ADDROFF(last_task_used_altivec)(r3)
 | |
| #endif /* CONFIG_SMP */
 | |
| 	/* restore registers and return */
 | |
| 	blr
 | |
| 
 | |
| _GLOBAL(giveup_altivec_notask)
 | |
| 	mfmsr	r3
 | |
| 	andis.	r4,r3,MSR_VEC@h
 | |
| 	bnelr				/* Already enabled? */
 | |
| 	oris	r3,r3,MSR_VEC@h
 | |
| 	SYNC
 | |
| 	MTMSRD(r3)			/* enable use of VMX now */
 | |
| 	isync
 | |
| 	blr
 | |
| 
 | |
| /*
 | |
|  * giveup_altivec(tsk)
 | |
|  * Disable VMX for the task given as the argument,
 | |
|  * and save the vector registers in its thread_struct.
 | |
|  * Enables the VMX for use in the kernel on return.
 | |
|  */
 | |
| _GLOBAL(giveup_altivec)
 | |
| 	mfmsr	r5
 | |
| 	oris	r5,r5,MSR_VEC@h
 | |
| 	SYNC
 | |
| 	MTMSRD(r5)			/* enable use of VMX now */
 | |
| 	isync
 | |
| 	PPC_LCMPI	0,r3,0
 | |
| 	beqlr				/* if no previous owner, done */
 | |
| 	addi	r3,r3,THREAD		/* want THREAD of task */
 | |
| 	PPC_LL	r5,PT_REGS(r3)
 | |
| 	PPC_LCMPI	0,r5,0
 | |
| 	SAVE_32VRS(0,r4,r3)
 | |
| 	mfvscr	vr0
 | |
| 	li	r4,THREAD_VSCR
 | |
| 	stvx	vr0,r4,r3
 | |
| 	beq	1f
 | |
| 	PPC_LL	r4,_MSR-STACK_FRAME_OVERHEAD(r5)
 | |
| #ifdef CONFIG_VSX
 | |
| BEGIN_FTR_SECTION
 | |
| 	lis	r3,(MSR_VEC|MSR_VSX)@h
 | |
| FTR_SECTION_ELSE
 | |
| 	lis	r3,MSR_VEC@h
 | |
| ALT_FTR_SECTION_END_IFSET(CPU_FTR_VSX)
 | |
| #else
 | |
| 	lis	r3,MSR_VEC@h
 | |
| #endif
 | |
| 	andc	r4,r4,r3		/* disable FP for previous task */
 | |
| 	PPC_STL	r4,_MSR-STACK_FRAME_OVERHEAD(r5)
 | |
| 1:
 | |
| #ifndef CONFIG_SMP
 | |
| 	li	r5,0
 | |
| 	LOAD_REG_ADDRBASE(r4,last_task_used_altivec)
 | |
| 	PPC_STL	r5,ADDROFF(last_task_used_altivec)(r4)
 | |
| #endif /* CONFIG_SMP */
 | |
| 	blr
 | |
| 
 | |
| #ifdef CONFIG_VSX
 | |
| 
 | |
| #ifdef CONFIG_PPC32
 | |
| #error This asm code isn't ready for 32-bit kernels
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|  * load_up_vsx(unused, unused, tsk)
 | |
|  * Disable VSX for the task which had it previously,
 | |
|  * and save its vector registers in its thread_struct.
 | |
|  * Reuse the fp and vsx saves, but first check to see if they have
 | |
|  * been saved already.
 | |
|  */
 | |
| _GLOBAL(load_up_vsx)
 | |
| /* Load FP and VSX registers if they haven't been done yet */
 | |
| 	andi.	r5,r12,MSR_FP
 | |
| 	beql+	load_up_fpu		/* skip if already loaded */
 | |
| 	andis.	r5,r12,MSR_VEC@h
 | |
| 	beql+	load_up_altivec		/* skip if already loaded */
 | |
| 
 | |
| #ifndef CONFIG_SMP
 | |
| 	ld	r3,last_task_used_vsx@got(r2)
 | |
| 	ld	r4,0(r3)
 | |
| 	cmpdi	0,r4,0
 | |
| 	beq	1f
 | |
| 	/* Disable VSX for last_task_used_vsx */
 | |
| 	addi	r4,r4,THREAD
 | |
| 	ld	r5,PT_REGS(r4)
 | |
| 	ld	r4,_MSR-STACK_FRAME_OVERHEAD(r5)
 | |
| 	lis	r6,MSR_VSX@h
 | |
| 	andc	r6,r4,r6
 | |
| 	std	r6,_MSR-STACK_FRAME_OVERHEAD(r5)
 | |
| 1:
 | |
| #endif /* CONFIG_SMP */
 | |
| 	ld	r4,PACACURRENT(r13)
 | |
| 	addi	r4,r4,THREAD		/* Get THREAD */
 | |
| 	li	r6,1
 | |
| 	stw	r6,THREAD_USED_VSR(r4) /* ... also set thread used vsr */
 | |
| 	/* enable use of VSX after return */
 | |
| 	oris	r12,r12,MSR_VSX@h
 | |
| 	std	r12,_MSR(r1)
 | |
| #ifndef CONFIG_SMP
 | |
| 	/* Update last_task_used_vsx to 'current' */
 | |
| 	ld	r4,PACACURRENT(r13)
 | |
| 	std	r4,0(r3)
 | |
| #endif /* CONFIG_SMP */
 | |
| 	b	fast_exception_return
 | |
| 
 | |
| /*
 | |
|  * __giveup_vsx(tsk)
 | |
|  * Disable VSX for the task given as the argument.
 | |
|  * Does NOT save vsx registers.
 | |
|  * Enables the VSX for use in the kernel on return.
 | |
|  */
 | |
| _GLOBAL(__giveup_vsx)
 | |
| 	mfmsr	r5
 | |
| 	oris	r5,r5,MSR_VSX@h
 | |
| 	mtmsrd	r5			/* enable use of VSX now */
 | |
| 	isync
 | |
| 
 | |
| 	cmpdi	0,r3,0
 | |
| 	beqlr-				/* if no previous owner, done */
 | |
| 	addi	r3,r3,THREAD		/* want THREAD of task */
 | |
| 	ld	r5,PT_REGS(r3)
 | |
| 	cmpdi	0,r5,0
 | |
| 	beq	1f
 | |
| 	ld	r4,_MSR-STACK_FRAME_OVERHEAD(r5)
 | |
| 	lis	r3,MSR_VSX@h
 | |
| 	andc	r4,r4,r3		/* disable VSX for previous task */
 | |
| 	std	r4,_MSR-STACK_FRAME_OVERHEAD(r5)
 | |
| 1:
 | |
| #ifndef CONFIG_SMP
 | |
| 	li	r5,0
 | |
| 	ld	r4,last_task_used_vsx@got(r2)
 | |
| 	std	r5,0(r4)
 | |
| #endif /* CONFIG_SMP */
 | |
| 	blr
 | |
| 
 | |
| #endif /* CONFIG_VSX */
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * The routines below are in assembler so we can closely control the
 | |
|  * usage of floating-point registers.  These routines must be called
 | |
|  * with preempt disabled.
 | |
|  */
 | |
| #ifdef CONFIG_PPC32
 | |
| 	.data
 | |
| fpzero:
 | |
| 	.long	0
 | |
| fpone:
 | |
| 	.long	0x3f800000	/* 1.0 in single-precision FP */
 | |
| fphalf:
 | |
| 	.long	0x3f000000	/* 0.5 in single-precision FP */
 | |
| 
 | |
| #define LDCONST(fr, name)	\
 | |
| 	lis	r11,name@ha;	\
 | |
| 	lfs	fr,name@l(r11)
 | |
| #else
 | |
| 
 | |
| 	.section ".toc","aw"
 | |
| fpzero:
 | |
| 	.tc	FD_0_0[TC],0
 | |
| fpone:
 | |
| 	.tc	FD_3ff00000_0[TC],0x3ff0000000000000	/* 1.0 */
 | |
| fphalf:
 | |
| 	.tc	FD_3fe00000_0[TC],0x3fe0000000000000	/* 0.5 */
 | |
| 
 | |
| #define LDCONST(fr, name)	\
 | |
| 	lfd	fr,name@toc(r2)
 | |
| #endif
 | |
| 
 | |
| 	.text
 | |
| /*
 | |
|  * Internal routine to enable floating point and set FPSCR to 0.
 | |
|  * Don't call it from C; it doesn't use the normal calling convention.
 | |
|  */
 | |
| fpenable:
 | |
| #ifdef CONFIG_PPC32
 | |
| 	stwu	r1,-64(r1)
 | |
| #else
 | |
| 	stdu	r1,-64(r1)
 | |
| #endif
 | |
| 	mfmsr	r10
 | |
| 	ori	r11,r10,MSR_FP
 | |
| 	mtmsr	r11
 | |
| 	isync
 | |
| 	stfd	fr0,24(r1)
 | |
| 	stfd	fr1,16(r1)
 | |
| 	stfd	fr31,8(r1)
 | |
| 	LDCONST(fr1, fpzero)
 | |
| 	mffs	fr31
 | |
| 	MTFSF_L(fr1)
 | |
| 	blr
 | |
| 
 | |
| fpdisable:
 | |
| 	mtlr	r12
 | |
| 	MTFSF_L(fr31)
 | |
| 	lfd	fr31,8(r1)
 | |
| 	lfd	fr1,16(r1)
 | |
| 	lfd	fr0,24(r1)
 | |
| 	mtmsr	r10
 | |
| 	isync
 | |
| 	addi	r1,r1,64
 | |
| 	blr
 | |
| 
 | |
| /*
 | |
|  * Vector add, floating point.
 | |
|  */
 | |
| _GLOBAL(vaddfp)
 | |
| 	mflr	r12
 | |
| 	bl	fpenable
 | |
| 	li	r0,4
 | |
| 	mtctr	r0
 | |
| 	li	r6,0
 | |
| 1:	lfsx	fr0,r4,r6
 | |
| 	lfsx	fr1,r5,r6
 | |
| 	fadds	fr0,fr0,fr1
 | |
| 	stfsx	fr0,r3,r6
 | |
| 	addi	r6,r6,4
 | |
| 	bdnz	1b
 | |
| 	b	fpdisable
 | |
| 
 | |
| /*
 | |
|  * Vector subtract, floating point.
 | |
|  */
 | |
| _GLOBAL(vsubfp)
 | |
| 	mflr	r12
 | |
| 	bl	fpenable
 | |
| 	li	r0,4
 | |
| 	mtctr	r0
 | |
| 	li	r6,0
 | |
| 1:	lfsx	fr0,r4,r6
 | |
| 	lfsx	fr1,r5,r6
 | |
| 	fsubs	fr0,fr0,fr1
 | |
| 	stfsx	fr0,r3,r6
 | |
| 	addi	r6,r6,4
 | |
| 	bdnz	1b
 | |
| 	b	fpdisable
 | |
| 
 | |
| /*
 | |
|  * Vector multiply and add, floating point.
 | |
|  */
 | |
| _GLOBAL(vmaddfp)
 | |
| 	mflr	r12
 | |
| 	bl	fpenable
 | |
| 	stfd	fr2,32(r1)
 | |
| 	li	r0,4
 | |
| 	mtctr	r0
 | |
| 	li	r7,0
 | |
| 1:	lfsx	fr0,r4,r7
 | |
| 	lfsx	fr1,r5,r7
 | |
| 	lfsx	fr2,r6,r7
 | |
| 	fmadds	fr0,fr0,fr2,fr1
 | |
| 	stfsx	fr0,r3,r7
 | |
| 	addi	r7,r7,4
 | |
| 	bdnz	1b
 | |
| 	lfd	fr2,32(r1)
 | |
| 	b	fpdisable
 | |
| 
 | |
| /*
 | |
|  * Vector negative multiply and subtract, floating point.
 | |
|  */
 | |
| _GLOBAL(vnmsubfp)
 | |
| 	mflr	r12
 | |
| 	bl	fpenable
 | |
| 	stfd	fr2,32(r1)
 | |
| 	li	r0,4
 | |
| 	mtctr	r0
 | |
| 	li	r7,0
 | |
| 1:	lfsx	fr0,r4,r7
 | |
| 	lfsx	fr1,r5,r7
 | |
| 	lfsx	fr2,r6,r7
 | |
| 	fnmsubs	fr0,fr0,fr2,fr1
 | |
| 	stfsx	fr0,r3,r7
 | |
| 	addi	r7,r7,4
 | |
| 	bdnz	1b
 | |
| 	lfd	fr2,32(r1)
 | |
| 	b	fpdisable
 | |
| 
 | |
| /*
 | |
|  * Vector reciprocal estimate.  We just compute 1.0/x.
 | |
|  * r3 -> destination, r4 -> source.
 | |
|  */
 | |
| _GLOBAL(vrefp)
 | |
| 	mflr	r12
 | |
| 	bl	fpenable
 | |
| 	li	r0,4
 | |
| 	LDCONST(fr1, fpone)
 | |
| 	mtctr	r0
 | |
| 	li	r6,0
 | |
| 1:	lfsx	fr0,r4,r6
 | |
| 	fdivs	fr0,fr1,fr0
 | |
| 	stfsx	fr0,r3,r6
 | |
| 	addi	r6,r6,4
 | |
| 	bdnz	1b
 | |
| 	b	fpdisable
 | |
| 
 | |
| /*
 | |
|  * Vector reciprocal square-root estimate, floating point.
 | |
|  * We use the frsqrte instruction for the initial estimate followed
 | |
|  * by 2 iterations of Newton-Raphson to get sufficient accuracy.
 | |
|  * r3 -> destination, r4 -> source.
 | |
|  */
 | |
| _GLOBAL(vrsqrtefp)
 | |
| 	mflr	r12
 | |
| 	bl	fpenable
 | |
| 	stfd	fr2,32(r1)
 | |
| 	stfd	fr3,40(r1)
 | |
| 	stfd	fr4,48(r1)
 | |
| 	stfd	fr5,56(r1)
 | |
| 	li	r0,4
 | |
| 	LDCONST(fr4, fpone)
 | |
| 	LDCONST(fr5, fphalf)
 | |
| 	mtctr	r0
 | |
| 	li	r6,0
 | |
| 1:	lfsx	fr0,r4,r6
 | |
| 	frsqrte	fr1,fr0		/* r = frsqrte(s) */
 | |
| 	fmuls	fr3,fr1,fr0	/* r * s */
 | |
| 	fmuls	fr2,fr1,fr5	/* r * 0.5 */
 | |
| 	fnmsubs	fr3,fr1,fr3,fr4	/* 1 - s * r * r */
 | |
| 	fmadds	fr1,fr2,fr3,fr1	/* r = r + 0.5 * r * (1 - s * r * r) */
 | |
| 	fmuls	fr3,fr1,fr0	/* r * s */
 | |
| 	fmuls	fr2,fr1,fr5	/* r * 0.5 */
 | |
| 	fnmsubs	fr3,fr1,fr3,fr4	/* 1 - s * r * r */
 | |
| 	fmadds	fr1,fr2,fr3,fr1	/* r = r + 0.5 * r * (1 - s * r * r) */
 | |
| 	stfsx	fr1,r3,r6
 | |
| 	addi	r6,r6,4
 | |
| 	bdnz	1b
 | |
| 	lfd	fr5,56(r1)
 | |
| 	lfd	fr4,48(r1)
 | |
| 	lfd	fr3,40(r1)
 | |
| 	lfd	fr2,32(r1)
 | |
| 	b	fpdisable
 |