Commit 1ab196f3 authored by Paul Mackerras's avatar Paul Mackerras Committed by Linus Torvalds

[PATCH] ppc64: Optimize exception/syscall entry/exit

This rewrites the PPC64 exception entry/exit routines to make them
smaller and faster.

In particular we no longer save all of the registers for the common
exceptions - system calls, hardware interrupts and decrementer (timer)
interrupts - only the volatile registers.  The other registers are saved
and restored (if used) by the C functions we call.  This involved
changing the registers we use in early exception processing from r20-r23
to r9-r12, which ended up changing quite a lot of code in head.S. 
Overall this gives us about a 20% reduction in null syscall time. 

Some system calls need all the registers (e.g.  fork/clone/vfork and
[rt_]sigsuspend).  For these the syscall dispatch code calls a stub that
saves the nonvolatile registers before calling the real handler.

This also implements the force_successful_syscall_return() thing for
ppc64.
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 23932693
......@@ -49,6 +49,7 @@ int main(void)
DEFINE(THREAD_SIZE, THREAD_SIZE);
DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count));
DEFINE(TI_SC_NOERR, offsetof(struct thread_info, syscall_noerror));
/* task_struct->thread */
DEFINE(THREAD, offsetof(struct task_struct, thread));
......@@ -100,7 +101,10 @@ int main(void)
DEFINE(PACALPPACA, offsetof(struct paca_struct, xLpPaca));
DEFINE(LPPACA, offsetof(struct paca_struct, xLpPaca));
DEFINE(PACAREGSAV, offsetof(struct paca_struct, xRegSav));
DEFINE(PACAEXC, offsetof(struct paca_struct, exception_stack));
DEFINE(PACA_EXGEN, offsetof(struct paca_struct, exgen));
DEFINE(PACA_EXMC, offsetof(struct paca_struct, exmc));
DEFINE(PACA_EXSLB, offsetof(struct paca_struct, exslb));
DEFINE(PACA_EXDSI, offsetof(struct paca_struct, exdsi));
DEFINE(PACAGUARD, offsetof(struct paca_struct, guard));
DEFINE(LPPACASRR0, offsetof(struct ItLpPaca, xSavedSrr0));
DEFINE(LPPACASRR1, offsetof(struct ItLpPaca, xSavedSrr1));
......@@ -137,6 +141,10 @@ int main(void)
DEFINE(GPR7, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[7]));
DEFINE(GPR8, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[8]));
DEFINE(GPR9, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[9]));
DEFINE(GPR10, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[10]));
DEFINE(GPR11, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[11]));
DEFINE(GPR12, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[12]));
DEFINE(GPR13, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[13]));
DEFINE(GPR20, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[20]));
DEFINE(GPR21, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[21]));
DEFINE(GPR22, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[22]));
......@@ -155,7 +163,7 @@ int main(void)
DEFINE(_DSISR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dsisr));
DEFINE(ORIG_GPR3, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, orig_gpr3));
DEFINE(RESULT, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, result));
DEFINE(TRAP, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, trap));
DEFINE(_TRAP, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, trap));
DEFINE(SOFTE, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, softe));
/* These _only_ to be used with {PROM,RTAS}_FRAME_SIZE!!! */
......
This diff is collapsed.
This diff is collapsed.
......@@ -85,16 +85,17 @@ _GLOBAL(local_irq_restore)
cmpw 0,r3,r5
beqlr
/* are we enabling interrupts? */
cmpi 0,r3,0
cmpdi 0,r3,0
stb r3,PACAPROCENABLED(r13)
beqlr
/* Check pending interrupts */
/* A decrementer, IPI or PMC interrupt may have occurred
* while we were in the hypervisor (which enables) */
CHECKANYINT(r4,r5)
ld r4,PACALPPACA+LPPACAANYINT(r13)
cmpdi r4,0
beqlr
/*
/*
* Handle pending interrupts in interrupt context
*/
li r0,0x5555
......@@ -608,7 +609,7 @@ _GLOBAL(kernel_thread)
_GLOBAL(sys_call_table32)
.llong .sys_restart_syscall /* 0 */
.llong .sys_exit
.llong .sys_fork
.llong .ppc_fork
.llong .sys_read
.llong .sys_write
.llong .sys32_open /* 5 */
......@@ -678,7 +679,7 @@ _GLOBAL(sys_call_table32)
.llong .sys32_ssetmask
.llong .sys_setreuid /* 70 */
.llong .sys_setregid
.llong .sys32_sigsuspend
.llong .ppc32_sigsuspend
.llong .compat_sys_sigpending
.llong .sys32_sethostname
.llong .compat_sys_setrlimit /* 75 */
......@@ -726,7 +727,7 @@ _GLOBAL(sys_call_table32)
.llong .sys32_ipc
.llong .sys_fsync
.llong .ppc32_sigreturn
.llong .sys_clone /* 120 */
.llong .ppc_clone /* 120 */
.llong .sys32_setdomainname
.llong .ppc64_newuname
.llong .sys_ni_syscall /* old modify_ldt syscall */
......@@ -784,7 +785,7 @@ _GLOBAL(sys_call_table32)
.llong .sys32_rt_sigpending /* 175 */
.llong .sys32_rt_sigtimedwait
.llong .sys32_rt_sigqueueinfo
.llong .sys32_rt_sigsuspend
.llong .ppc32_rt_sigsuspend
.llong .sys32_pread64
.llong .sys32_pwrite64 /* 180 */
.llong .sys_chown
......@@ -795,7 +796,7 @@ _GLOBAL(sys_call_table32)
.llong .sys32_sendfile
.llong .sys_ni_syscall /* reserved for streams1 */
.llong .sys_ni_syscall /* reserved for streams2 */
.llong .sys_vfork
.llong .ppc_vfork
.llong .compat_sys_getrlimit /* 190 */
.llong .sys32_readahead
.llong .sys32_mmap2
......@@ -880,7 +881,7 @@ _GLOBAL(sys_call_table32)
_GLOBAL(sys_call_table)
.llong .sys_restart_syscall /* 0 */
.llong .sys_exit
.llong .sys_fork
.llong .ppc_fork
.llong .sys_read
.llong .sys_write
.llong .sys_open /* 5 */
......@@ -998,7 +999,7 @@ _GLOBAL(sys_call_table)
.llong .sys_ipc
.llong .sys_fsync
.llong .sys_ni_syscall
.llong .sys_clone /* 120 */
.llong .ppc_clone /* 120 */
.llong .sys_setdomainname
.llong .ppc64_newuname
.llong .sys_ni_syscall /* old modify_ldt syscall */
......@@ -1056,7 +1057,7 @@ _GLOBAL(sys_call_table)
.llong .sys_rt_sigpending /* 175 */
.llong .sys_rt_sigtimedwait
.llong .sys_rt_sigqueueinfo
.llong .sys_rt_sigsuspend
.llong .ppc64_rt_sigsuspend
.llong .sys_pread64
.llong .sys_pwrite64 /* 180 */
.llong .sys_chown
......@@ -1067,7 +1068,7 @@ _GLOBAL(sys_call_table)
.llong .sys_sendfile64
.llong .sys_ni_syscall /* reserved for streams1 */
.llong .sys_ni_syscall /* reserved for streams2 */
.llong .sys_vfork
.llong .ppc_vfork
.llong .sys_getrlimit /* 190 */
.llong .sys_readahead
.llong .sys_ni_syscall /* 32bit only mmap2 */
......
......@@ -62,8 +62,6 @@ struct systemcfg *systemcfg;
.xDesc = 0xd397d9e2, /* "LpRS" */ \
.xSize = sizeof(struct ItLpRegSave) \
}, \
.exception_sp = \
(&paca[number].exception_stack[0]) - EXC_FRAME_SIZE, \
}
struct paca_struct paca[] __page_aligned = {
......
......@@ -219,6 +219,7 @@ struct task_struct *__switch_to(struct task_struct *prev,
void show_regs(struct pt_regs * regs)
{
int i;
unsigned long trap;
printk("NIP: %016lX XER: %016lX LR: %016lX\n",
regs->nip, regs->xer, regs->link);
......@@ -229,7 +230,8 @@ void show_regs(struct pt_regs * regs)
regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0,
regs->msr&MSR_IR ? 1 : 0,
regs->msr&MSR_DR ? 1 : 0);
if (regs->trap == 0x300 || regs->trap == 0x380 || regs->trap == 0x600)
trap = TRAP(regs);
if (trap == 0x300 || trap == 0x380 || trap == 0x600)
printk("DAR: %016lx, DSISR: %016lx\n", regs->dar, regs->dsisr);
printk("TASK: %p[%d] '%s' THREAD: %p",
current, current->pid, current->comm, current->thread_info);
......@@ -244,6 +246,8 @@ void show_regs(struct pt_regs * regs)
}
printk("%016lX ", regs->gpr[i]);
if (i == 13 && !FULL_REGS(regs))
break;
}
printk("\n");
/*
......
......@@ -528,13 +528,13 @@ int do_signal(sigset_t *oldset, struct pt_regs *regs)
struct k_sigaction *ka = &current->sighand->action[signr-1];
/* Whee! Actually deliver the signal. */
if (regs->trap == 0x0C00)
if (TRAP(regs) == 0x0C00)
syscall_restart(regs, ka);
handle_signal(signr, ka, &info, oldset, regs);
return 1;
}
if (regs->trap == 0x0C00) { /* System Call! */
if (TRAP(regs) == 0x0C00) { /* System Call! */
if ((int)regs->result == -ERESTARTNOHAND ||
(int)regs->result == -ERESTARTSYS ||
(int)regs->result == -ERESTARTNOINTR) {
......
......@@ -932,7 +932,7 @@ int do_signal32(sigset_t *oldset, struct pt_regs *regs)
ka = (signr == 0)? NULL: &current->sighand->action[signr-1];
if (regs->trap == 0x0C00 /* System Call! */
if (TRAP(regs) == 0x0C00 /* System Call! */
&& regs->ccr & 0x10000000 /* error signalled */
&& ((ret = regs->gpr[3]) == ERESTARTSYS
|| ret == ERESTARTNOHAND || ret == ERESTARTNOINTR
......
......@@ -237,5 +237,19 @@ asmlinkage time_t sys64_time(time_t __user * tloc)
return secs;
}
void do_show_syscall(unsigned long r3, unsigned long r4, unsigned long r5,
unsigned long r6, unsigned long r7, unsigned long r8,
struct pt_regs *regs)
{
printk("syscall %ld(%lx, %lx, %lx, %lx, %lx, %lx) regs=%p current=%p"
" cpu=%d\n", regs->gpr[0], r3, r4, r5, r6, r7, r8, regs,
current, smp_processor_id());
}
void do_show_syscall_exit(unsigned long r3)
{
printk(" -> %lx, current=%p cpu=%d\n", r3, current, smp_processor_id());
}
/* Only exists on P-series. */
cond_syscall(ppc_rtas);
......@@ -441,8 +441,22 @@ void KernelFPUnavailableException(struct pt_regs *regs)
die("Unrecoverable FP Unavailable Exception", regs, SIGABRT);
}
void KernelAltivecUnavailableException(struct pt_regs *regs)
void AltivecUnavailableException(struct pt_regs *regs)
{
#ifndef CONFIG_ALTIVEC
if (user_mode(regs)) {
/* A user program has executed an altivec instruction,
but this kernel doesn't support altivec. */
siginfo_t info;
memset(&info, 0, sizeof(info));
info.si_signo = SIGILL;
info.si_code = ILL_ILLOPC;
info.si_addr = (void *) regs->nip;
_exception(SIGILL, &info, regs);
return;
}
#endif
printk(KERN_EMERG "Unrecoverable VMX/Altivec Unavailable Exception "
"%lx at %lx\n", regs->trap, regs->nip);
die("Unrecoverable VMX/Altivec Unavailable Exception", regs, SIGABRT);
......
......@@ -80,36 +80,45 @@ static int store_updates_sp(struct pt_regs *regs)
* - DSISR for a non-SLB data access fault,
* - SRR1 & 0x08000000 for a non-SLB instruction access fault
* - 0 any SLB fault.
* The return value is 0 if the fault was handled, or the signal
* number if this is a kernel fault that can't be handled here.
*/
void do_page_fault(struct pt_regs *regs, unsigned long address,
unsigned long error_code)
int do_page_fault(struct pt_regs *regs, unsigned long address,
unsigned long error_code)
{
struct vm_area_struct * vma;
struct mm_struct *mm = current->mm;
siginfo_t info;
unsigned long code = SEGV_MAPERR;
unsigned long is_write = error_code & 0x02000000;
unsigned long trap = TRAP(regs);
if (regs->trap == 0x300 || regs->trap == 0x380) {
if (trap == 0x300 || trap == 0x380) {
if (debugger_fault_handler(regs))
return;
return 0;
}
/* On a kernel SLB miss we can only check for a valid exception entry */
if (!user_mode(regs) && (regs->trap == 0x380)) {
bad_page_fault(regs, address, SIGSEGV);
return;
}
if (!user_mode(regs) && (trap == 0x380 || address >= TASK_SIZE))
return SIGSEGV;
if (error_code & 0x00400000) {
if (debugger_dabr_match(regs))
return;
return 0;
}
if (in_atomic() || mm == NULL) {
bad_page_fault(regs, address, SIGSEGV);
return;
if (!user_mode(regs))
return SIGSEGV;
/* in_atomic() in user mode is really bad,
as is current->mm == NULL. */
printk(KERN_EMERG "Page fault in user mode with"
"in_atomic() = %d mm = %p\n", in_atomic(), mm);
printk(KERN_EMERG "NIP = %lx MSR = %lx\n",
regs->nip, regs->msr);
die("Weird page fault", regs, SIGSEGV);
}
down_read(&mm->mmap_sem);
vma = find_vma(mm, address);
if (!vma)
......@@ -195,7 +204,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,
}
up_read(&mm->mmap_sem);
return;
return 0;
bad_area:
up_read(&mm->mmap_sem);
......@@ -207,11 +216,10 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,
info.si_code = code;
info.si_addr = (void *) address;
force_sig_info(SIGSEGV, &info, current);
return;
return 0;
}
bad_page_fault(regs, address, SIGSEGV);
return;
return SIGSEGV;
/*
* We ran out of memory, or some other thing happened to us that made
......@@ -227,18 +235,19 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,
printk("VM: killing process %s\n", current->comm);
if (user_mode(regs))
do_exit(SIGKILL);
bad_page_fault(regs, address, SIGKILL);
return;
return SIGKILL;
do_sigbus:
up_read(&mm->mmap_sem);
info.si_signo = SIGBUS;
info.si_errno = 0;
info.si_code = BUS_ADRERR;
info.si_addr = (void *)address;
force_sig_info (SIGBUS, &info, current);
if (!user_mode(regs))
bad_page_fault(regs, address, SIGBUS);
if (user_mode(regs)) {
info.si_signo = SIGBUS;
info.si_errno = 0;
info.si_code = BUS_ADRERR;
info.si_addr = (void *)address;
force_sig_info(SIGBUS, &info, current);
return 0;
}
return SIGBUS;
}
/*
......
......@@ -44,9 +44,6 @@ static int xmon_owner;
static int xmon_gate;
#endif /* CONFIG_SMP */
#define TRAP(regs) ((regs)->trap)
#define FULL_REGS(regs) 1
static unsigned long in_xmon = 0;
static unsigned long adrs;
......
......@@ -136,23 +136,21 @@ struct paca_struct {
u8 rsvd6[0x500 - 0x8];
/*=====================================================================================
* CACHE_LINE_31 0x0F00 - 0x0F7F Exception stack
* CACHE_LINE_31-32 0x0F00 - 0x0FFF Exception register save areas
*=====================================================================================
*/
u8 exception_stack[N_EXC_STACK*EXC_FRAME_SIZE];
u64 exgen[8]; /* used for most interrupts/exceptions */
u64 exmc[8]; /* used for machine checks */
u64 exslb[8]; /* used for SLB/segment table misses
* on the linear mapping */
u64 exdsi[8]; /* used for linear mapping hash table misses */
/*=====================================================================================
* CACHE_LINE_32 0x0F80 - 0x0FFF Reserved
* Page 2 used as a stack when we detect a bad kernel stack pointer,
* and early in SMP boots before relocation is enabled.
*=====================================================================================
*/
u8 rsvd7[0x80]; /* Give the stack some rope ... */
/*=====================================================================================
* Page 2 Reserved for guard page. Also used as a stack early in SMP boots before
* relocation is enabled.
*=====================================================================================
*/
u8 guard[0x1000]; /* ... and then hang 'em */
u8 guard[0x1000];
};
#endif /* _PPC64_PACA_H */
......@@ -28,6 +28,9 @@
#define REST_8GPRS(n, base) REST_4GPRS(n, base); REST_4GPRS(n+4, base)
#define REST_10GPRS(n, base) REST_8GPRS(n, base); REST_2GPRS(n+8, base)
#define SAVE_NVGPRS(base) SAVE_8GPRS(14, base); SAVE_10GPRS(22, base)
#define REST_NVGPRS(base) REST_8GPRS(14, base); REST_10GPRS(22, base)
#define SAVE_FPR(n, base) stfd n,THREAD_FPR0+8*(n)(base)
#define SAVE_2FPRS(n, base) SAVE_FPR(n, base); SAVE_FPR(n+1, base)
#define SAVE_4FPRS(n, base) SAVE_2FPRS(n, base); SAVE_2FPRS(n+2, base)
......@@ -54,11 +57,6 @@
#define REST_16VRS(n,b,base) REST_8VRS(n,b,base); REST_8VRS(n+8,b,base)
#define REST_32VRS(n,b,base) REST_16VRS(n,b,base); REST_16VRS(n+16,b,base)
#define CHECKANYINT(ra,rb) \
mfspr rb,SPRG3; /* Get Paca address */\
ld ra,PACALPPACA+LPPACAANYINT(rb); /* Get pending interrupt flags */\
cmpldi 0,ra,0;
/* Macros to adjust thread priority for Iseries hardware multithreading */
#define HMT_LOW or 1,1,1
#define HMT_MEDIUM or 2,2,2
......
......@@ -543,8 +543,7 @@ struct thread_struct {
double fpr[32]; /* Complete floating point set */
unsigned long fpscr; /* Floating point status (plus pad) */
unsigned long fpexc_mode; /* Floating-point exception mode */
unsigned long saved_msr; /* Save MSR across signal handlers */
unsigned long saved_softe; /* Ditto for Soft Enable/Disable */
unsigned long pad[3]; /* was saved_msr, saved_softe */
#ifdef CONFIG_ALTIVEC
/* Complete AltiVec register set */
vector128 vr[32] __attribute((aligned(16)));
......
......@@ -71,6 +71,18 @@ struct pt_regs32 {
#define instruction_pointer(regs) ((regs)->nip)
#define user_mode(regs) ((((regs)->msr) >> MSR_PR_LG) & 0x1)
#define force_successful_syscall_return() \
(current_thread_info()->syscall_noerror = 1)
/*
* We use the least-significant bit of the trap field to indicate
* whether we have saved the full set of registers, or only a
* partial set. A 1 there means the partial set.
*/
#define FULL_REGS(regs) (((regs)->trap & 1) == 0)
#define TRAP(regs) ((regs)->trap & ~0xF)
#define CHECK_FULL_REGS(regs) BUG_ON(regs->trap & 1)
/*
* Offsets used by 'ptrace' system call interface.
*/
......
......@@ -26,6 +26,8 @@ struct thread_info {
int cpu; /* cpu we're on */
int preempt_count;
struct restart_block restart_block;
/* set by force_successful_syscall_return */
unsigned char syscall_noerror;
};
/*
......@@ -84,8 +86,6 @@ static inline struct thread_info *current_thread_info(void)
/*
* thread information flag bit numbers
* N.B. If TIF_SIGPENDING or TIF_NEED_RESCHED are changed
* to be >= 4, code in entry.S will need to be changed.
*/
#define TIF_SYSCALL_TRACE 0 /* syscall trace active */
#define TIF_NOTIFY_RESUME 1 /* resumption notification requested */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment