[PARISC] factor syscall_restart code out of do_signal

looks better this way... ;)

Signed-off-by: Kyle McMartin <kyle@parisc-linux.org>
This commit is contained in:
Kyle McMartin 2007-01-15 00:36:26 -05:00 committed by Kyle McMartin
parent f354ef8abe
commit 2b163b71e6
1 changed files with 97 additions and 78 deletions

View File

@ -471,6 +471,97 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
return 1;
}
static inline void
syscall_restart(struct pt_regs *regs, struct k_sigaction *ka)
{
/* Check the return code */
switch (regs->gr[28]) {
case -ERESTART_RESTARTBLOCK:
current_thread_info()->restart_block.fn =
do_no_restart_syscall;
case -ERESTARTNOHAND:
DBG(1,"ERESTARTNOHAND: returning -EINTR\n");
regs->gr[28] = -EINTR;
break;
case -ERESTARTSYS:
if (!(ka->sa.sa_flags & SA_RESTART)) {
DBG(1,"ERESTARTSYS: putting -EINTR\n");
regs->gr[28] = -EINTR;
break;
}
/* fallthrough */
case -ERESTARTNOINTR:
/* A syscall is just a branch, so all
* we have to do is fiddle the return pointer.
*/
regs->gr[31] -= 8; /* delayed branching */
/* Preserve original r28. */
regs->gr[28] = regs->orig_r28;
break;
}
}
static inline void
insert_restart_trampoline(struct pt_regs *regs)
{
switch(regs->gr[28]) {
case -ERESTART_RESTARTBLOCK: {
/* Restart the system call - no handlers present */
unsigned int *usp = (unsigned int *)regs->gr[30];
/* Setup a trampoline to restart the syscall
* with __NR_restart_syscall
*
* 0: <return address (orig r31)>
* 4: <2nd half for 64-bit>
* 8: ldw 0(%sp), %r31
* 12: be 0x100(%sr2, %r0)
* 16: ldi __NR_restart_syscall, %r20
*/
#ifdef CONFIG_64BIT
put_user(regs->gr[31] >> 32, &usp[0]);
put_user(regs->gr[31] & 0xffffffff, &usp[1]);
put_user(0x0fc010df, &usp[2]);
#else
put_user(regs->gr[31], &usp[0]);
put_user(0x0fc0109f, &usp[2]);
#endif
put_user(0xe0008200, &usp[3]);
put_user(0x34140000, &usp[4]);
/* Stack is 64-byte aligned, and we only need
* to flush 1 cache line.
* Flushing one cacheline is cheap.
* "sync" on bigger (> 4 way) boxes is not.
*/
flush_icache_range(regs->gr[30], regs->gr[30] + 4);
regs->gr[31] = regs->gr[30] + 8;
/* Preserve original r28. */
regs->gr[28] = regs->orig_r28;
return;
}
case -ERESTARTNOHAND:
case -ERESTARTSYS:
case -ERESTARTNOINTR: {
/* Hooray for delayed branching. We don't
* have to restore %r20 (the system call
* number) because it gets loaded in the delay
* slot of the branch external instruction.
*/
regs->gr[31] -= 8;
/* Preserve original r28. */
regs->gr[28] = regs->orig_r28;
return;
}
default:
break;
}
}
/*
* Note that 'init' is a special process: it doesn't get signals it doesn't
* want to handle. Thus you cannot kill init even with a SIGKILL even by
@ -482,7 +573,6 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
* registers). As noted below, the syscall number gets restored for
* us due to the magic of delayed branching.
*/
asmlinkage void
do_signal(struct pt_regs *regs, long in_syscall)
{
@ -518,36 +608,14 @@ do_signal(struct pt_regs *regs, long in_syscall)
break;
/* Restart a system call if necessary. */
if (in_syscall) {
/* Check the return code */
switch (regs->gr[28]) {
case -ERESTART_RESTARTBLOCK:
current_thread_info()->restart_block.fn = do_no_restart_syscall;
case -ERESTARTNOHAND:
DBG(1,"ERESTARTNOHAND: returning -EINTR\n");
regs->gr[28] = -EINTR;
break;
if (in_syscall)
syscall_restart(regs, &ka);
case -ERESTARTSYS:
if (!(ka.sa.sa_flags & SA_RESTART)) {
DBG(1,"ERESTARTSYS: putting -EINTR\n");
regs->gr[28] = -EINTR;
break;
}
/* fallthrough */
case -ERESTARTNOINTR:
/* A syscall is just a branch, so all
we have to do is fiddle the return pointer. */
regs->gr[31] -= 8; /* delayed branching */
/* Preserve original r28. */
regs->gr[28] = regs->orig_r28;
break;
}
}
/* Whee! Actually deliver the signal. If the
delivery failed, we need to continue to iterate in
this loop so we can deliver the SIGSEGV... */
if (handle_signal(signr, &info, &ka, oldset, regs, in_syscall)) {
if (handle_signal(signr, &info, &ka, oldset,
regs, in_syscall)) {
DBG(1,KERN_DEBUG "do_signal: Exit (success), regs->gr[28] = %ld\n",
regs->gr[28]);
if (test_thread_flag(TIF_RESTORE_SIGMASK))
@ -558,57 +626,8 @@ do_signal(struct pt_regs *regs, long in_syscall)
/* end of while(1) looping forever if we can't force a signal */
/* Did we come from a system call? */
if (in_syscall) {
/* Restart the system call - no handlers present */
if (regs->gr[28] == -ERESTART_RESTARTBLOCK) {
unsigned int *usp = (unsigned int *)regs->gr[30];
/* Setup a trampoline to restart the syscall
* with __NR_restart_syscall
*
* 0: <return address (orig r31)>
* 4: <2nd half for 64-bit>
* 8: ldw 0(%sp), %r31
* 12: be 0x100(%sr2, %r0)
* 16: ldi __NR_restart_syscall, %r20
*/
#ifndef __LP64__
put_user(regs->gr[31], &usp[0]);
put_user(0x0fc0109f, &usp[2]);
#else
put_user(regs->gr[31] >> 32, &usp[0]);
put_user(regs->gr[31] & 0xffffffff, &usp[1]);
put_user(0x0fc010df, &usp[2]);
#endif
put_user(0xe0008200, &usp[3]);
put_user(0x34140000, &usp[4]);
/* Stack is 64-byte aligned, and we only need
* to flush 1 cache line.
* Flushing one cacheline is cheap.
* "sync" on bigger (> 4 way) boxes is not.
*/
asm("fdc %%r0(%%sr3, %0)\n"
"sync\n"
"fic %%r0(%%sr3, %0)\n"
"sync\n"
: : "r"(regs->gr[30]));
regs->gr[31] = regs->gr[30] + 8;
/* Preserve original r28. */
regs->gr[28] = regs->orig_r28;
} else if (regs->gr[28] == -ERESTARTNOHAND ||
regs->gr[28] == -ERESTARTSYS ||
regs->gr[28] == -ERESTARTNOINTR) {
/* Hooray for delayed branching. We don't
have to restore %r20 (the system call
number) because it gets loaded in the delay
slot of the branch external instruction. */
regs->gr[31] -= 8;
/* Preserve original r28. */
regs->gr[28] = regs->orig_r28;
}
}
if (in_syscall)
insert_restart_trampoline(regs);
DBG(1,"do_signal: Exit (not delivered), regs->gr[28] = %ld\n",
regs->gr[28]);