2005-04-17 02:20:36 +04:00
/*
* arch / s390 / kernel / process . c
*
* S390 version
* Copyright ( C ) 1999 IBM Deutschland Entwicklung GmbH , IBM Corporation
* Author ( s ) : Martin Schwidefsky ( schwidefsky @ de . ibm . com ) ,
* Hartmut Penner ( hp @ de . ibm . com ) ,
* Denis Joseph Barrow ( djbarrow @ de . ibm . com , barrow_dj @ yahoo . com ) ,
*
* Derived from " arch/i386/kernel/process.c "
* Copyright ( C ) 1995 , Linus Torvalds
*/
/*
* This file handles the architecture - dependent parts of process handling . .
*/
# include <linux/config.h>
# include <linux/compiler.h>
# include <linux/cpu.h>
# include <linux/errno.h>
# include <linux/sched.h>
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/smp.h>
# include <linux/smp_lock.h>
# include <linux/stddef.h>
# include <linux/unistd.h>
# include <linux/ptrace.h>
# include <linux/slab.h>
# include <linux/vmalloc.h>
# include <linux/user.h>
# include <linux/a.out.h>
# include <linux/interrupt.h>
# include <linux/delay.h>
# include <linux/reboot.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/notifier.h>
# include <asm/uaccess.h>
# include <asm/pgtable.h>
# include <asm/system.h>
# include <asm/io.h>
# include <asm/processor.h>
# include <asm/irq.h>
# include <asm/timer.h>
asmlinkage void ret_from_fork ( void ) __asm__ ( " ret_from_fork " ) ;
/*
* Return saved PC of a blocked thread . used in kernel / sched .
* resume in entry . S does not create a new stack frame , it
* just stores the registers % r6 - % r15 to the frame given by
* schedule . We want to return the address of the caller of
* schedule , so we have to walk the backchain one time to
* find the frame schedule ( ) store its return address .
*/
unsigned long thread_saved_pc ( struct task_struct * tsk )
{
2006-01-15 00:20:57 +03:00
struct stack_frame * sf , * low , * high ;
2005-04-17 02:20:36 +04:00
2006-01-15 00:20:57 +03:00
if ( ! tsk | | ! task_stack_page ( tsk ) )
return 0 ;
low = task_stack_page ( tsk ) ;
high = ( struct stack_frame * ) task_pt_regs ( tsk ) ;
sf = ( struct stack_frame * ) ( tsk - > thread . ksp & PSW_ADDR_INSN ) ;
if ( sf < = low | | sf > high )
return 0 ;
sf = ( struct stack_frame * ) ( sf - > back_chain & PSW_ADDR_INSN ) ;
if ( sf < = low | | sf > high )
return 0 ;
2005-04-17 02:20:36 +04:00
return sf - > gprs [ 8 ] ;
}
/*
* Need to know about CPUs going idle ?
*/
static struct notifier_block * idle_chain ;
int register_idle_notifier ( struct notifier_block * nb )
{
return notifier_chain_register ( & idle_chain , nb ) ;
}
EXPORT_SYMBOL ( register_idle_notifier ) ;
int unregister_idle_notifier ( struct notifier_block * nb )
{
return notifier_chain_unregister ( & idle_chain , nb ) ;
}
EXPORT_SYMBOL ( unregister_idle_notifier ) ;
void do_monitor_call ( struct pt_regs * regs , long interruption_code )
{
/* disable monitor call class 0 */
__ctl_clear_bit ( 8 , 15 ) ;
notifier_call_chain ( & idle_chain , CPU_NOT_IDLE ,
( void * ) ( long ) smp_processor_id ( ) ) ;
}
2005-06-26 01:55:30 +04:00
extern void s390_handle_mcck ( void ) ;
2005-04-17 02:20:36 +04:00
/*
* The idle loop on a S390 . . .
*/
void default_idle ( void )
{
int cpu , rc ;
[PATCH] sched: resched and cpu_idle rework
Make some changes to the NEED_RESCHED and POLLING_NRFLAG to reduce
confusion, and make their semantics rigid. Improves efficiency of
resched_task and some cpu_idle routines.
* In resched_task:
- TIF_NEED_RESCHED is only cleared with the task's runqueue lock held,
and as we hold it during resched_task, then there is no need for an
atomic test and set there. The only other time this should be set is
when the task's quantum expires, in the timer interrupt - this is
protected against because the rq lock is irq-safe.
- If TIF_NEED_RESCHED is set, then we don't need to do anything. It
won't get unset until the task get's schedule()d off.
- If we are running on the same CPU as the task we resched, then set
TIF_NEED_RESCHED and no further action is required.
- If we are running on another CPU, and TIF_POLLING_NRFLAG is *not* set
after TIF_NEED_RESCHED has been set, then we need to send an IPI.
Using these rules, we are able to remove the test and set operation in
resched_task, and make clear the previously vague semantics of
POLLING_NRFLAG.
* In idle routines:
- Enter cpu_idle with preempt disabled. When the need_resched() condition
becomes true, explicitly call schedule(). This makes things a bit clearer
(IMO), but haven't updated all architectures yet.
- Many do a test and clear of TIF_NEED_RESCHED for some reason. According
to the resched_task rules, this isn't needed (and actually breaks the
assumption that TIF_NEED_RESCHED is only cleared with the runqueue lock
held). So remove that. Generally one less locked memory op when switching
to the idle thread.
- Many idle routines clear TIF_POLLING_NRFLAG, and only set it in the inner
most polling idle loops. The above resched_task semantics allow it to be
set until before the last time need_resched() is checked before going into
a halt requiring interrupt wakeup.
Many idle routines simply never enter such a halt, and so POLLING_NRFLAG
can be always left set, completely eliminating resched IPIs when rescheduling
the idle task.
POLLING_NRFLAG width can be increased, to reduce the chance of resched IPIs.
Signed-off-by: Nick Piggin <npiggin@suse.de>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Con Kolivas <kernel@kolivas.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 08:39:04 +03:00
/* CPU is going idle. */
cpu = smp_processor_id ( ) ;
2005-04-17 02:20:36 +04:00
local_irq_disable ( ) ;
[PATCH] sched: resched and cpu_idle rework
Make some changes to the NEED_RESCHED and POLLING_NRFLAG to reduce
confusion, and make their semantics rigid. Improves efficiency of
resched_task and some cpu_idle routines.
* In resched_task:
- TIF_NEED_RESCHED is only cleared with the task's runqueue lock held,
and as we hold it during resched_task, then there is no need for an
atomic test and set there. The only other time this should be set is
when the task's quantum expires, in the timer interrupt - this is
protected against because the rq lock is irq-safe.
- If TIF_NEED_RESCHED is set, then we don't need to do anything. It
won't get unset until the task get's schedule()d off.
- If we are running on the same CPU as the task we resched, then set
TIF_NEED_RESCHED and no further action is required.
- If we are running on another CPU, and TIF_POLLING_NRFLAG is *not* set
after TIF_NEED_RESCHED has been set, then we need to send an IPI.
Using these rules, we are able to remove the test and set operation in
resched_task, and make clear the previously vague semantics of
POLLING_NRFLAG.
* In idle routines:
- Enter cpu_idle with preempt disabled. When the need_resched() condition
becomes true, explicitly call schedule(). This makes things a bit clearer
(IMO), but haven't updated all architectures yet.
- Many do a test and clear of TIF_NEED_RESCHED for some reason. According
to the resched_task rules, this isn't needed (and actually breaks the
assumption that TIF_NEED_RESCHED is only cleared with the runqueue lock
held). So remove that. Generally one less locked memory op when switching
to the idle thread.
- Many idle routines clear TIF_POLLING_NRFLAG, and only set it in the inner
most polling idle loops. The above resched_task semantics allow it to be
set until before the last time need_resched() is checked before going into
a halt requiring interrupt wakeup.
Many idle routines simply never enter such a halt, and so POLLING_NRFLAG
can be always left set, completely eliminating resched IPIs when rescheduling
the idle task.
POLLING_NRFLAG width can be increased, to reduce the chance of resched IPIs.
Signed-off-by: Nick Piggin <npiggin@suse.de>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Con Kolivas <kernel@kolivas.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 08:39:04 +03:00
if ( need_resched ( ) ) {
2005-04-17 02:20:36 +04:00
local_irq_enable ( ) ;
[PATCH] sched: resched and cpu_idle rework
Make some changes to the NEED_RESCHED and POLLING_NRFLAG to reduce
confusion, and make their semantics rigid. Improves efficiency of
resched_task and some cpu_idle routines.
* In resched_task:
- TIF_NEED_RESCHED is only cleared with the task's runqueue lock held,
and as we hold it during resched_task, then there is no need for an
atomic test and set there. The only other time this should be set is
when the task's quantum expires, in the timer interrupt - this is
protected against because the rq lock is irq-safe.
- If TIF_NEED_RESCHED is set, then we don't need to do anything. It
won't get unset until the task get's schedule()d off.
- If we are running on the same CPU as the task we resched, then set
TIF_NEED_RESCHED and no further action is required.
- If we are running on another CPU, and TIF_POLLING_NRFLAG is *not* set
after TIF_NEED_RESCHED has been set, then we need to send an IPI.
Using these rules, we are able to remove the test and set operation in
resched_task, and make clear the previously vague semantics of
POLLING_NRFLAG.
* In idle routines:
- Enter cpu_idle with preempt disabled. When the need_resched() condition
becomes true, explicitly call schedule(). This makes things a bit clearer
(IMO), but haven't updated all architectures yet.
- Many do a test and clear of TIF_NEED_RESCHED for some reason. According
to the resched_task rules, this isn't needed (and actually breaks the
assumption that TIF_NEED_RESCHED is only cleared with the runqueue lock
held). So remove that. Generally one less locked memory op when switching
to the idle thread.
- Many idle routines clear TIF_POLLING_NRFLAG, and only set it in the inner
most polling idle loops. The above resched_task semantics allow it to be
set until before the last time need_resched() is checked before going into
a halt requiring interrupt wakeup.
Many idle routines simply never enter such a halt, and so POLLING_NRFLAG
can be always left set, completely eliminating resched IPIs when rescheduling
the idle task.
POLLING_NRFLAG width can be increased, to reduce the chance of resched IPIs.
Signed-off-by: Nick Piggin <npiggin@suse.de>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Con Kolivas <kernel@kolivas.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-11-09 08:39:04 +03:00
return ;
}
2005-04-17 02:20:36 +04:00
rc = notifier_call_chain ( & idle_chain , CPU_IDLE , ( void * ) ( long ) cpu ) ;
if ( rc ! = NOTIFY_OK & & rc ! = NOTIFY_DONE )
BUG ( ) ;
if ( rc ! = NOTIFY_OK ) {
local_irq_enable ( ) ;
return ;
}
/* enable monitor call class 0 */
__ctl_set_bit ( 8 , 15 ) ;
# ifdef CONFIG_HOTPLUG_CPU
2006-02-18 00:52:46 +03:00
if ( cpu_is_offline ( cpu ) ) {
preempt_enable_no_resched ( ) ;
2005-04-17 02:20:36 +04:00
cpu_die ( ) ;
2006-02-18 00:52:46 +03:00
}
2005-04-17 02:20:36 +04:00
# endif
2005-06-26 01:55:30 +04:00
local_mcck_disable ( ) ;
if ( test_thread_flag ( TIF_MCCK_PENDING ) ) {
local_mcck_enable ( ) ;
local_irq_enable ( ) ;
s390_handle_mcck ( ) ;
return ;
}
/* Wait for external, I/O or machine check interrupt. */
__load_psw_mask ( PSW_KERNEL_BITS | PSW_MASK_WAIT |
PSW_MASK_IO | PSW_MASK_EXT ) ;
2005-04-17 02:20:36 +04:00
}
void cpu_idle ( void )
{
2005-11-09 08:39:01 +03:00
for ( ; ; ) {
while ( ! need_resched ( ) )
default_idle ( ) ;
preempt_enable_no_resched ( ) ;
schedule ( ) ;
preempt_disable ( ) ;
}
2005-04-17 02:20:36 +04:00
}
void show_regs ( struct pt_regs * regs )
{
struct task_struct * tsk = current ;
2006-01-12 12:05:50 +03:00
printk ( " CPU: %d %s \n " , task_thread_info ( tsk ) - > cpu , print_tainted ( ) ) ;
2005-04-17 02:20:36 +04:00
printk ( " Process %s (pid: %d, task: %p, ksp: %p) \n " ,
current - > comm , current - > pid , ( void * ) tsk ,
( void * ) tsk - > thread . ksp ) ;
show_registers ( regs ) ;
/* Show stack backtrace if pt_regs is from kernel mode */
if ( ! ( regs - > psw . mask & PSW_MASK_PSTATE ) )
show_trace ( 0 , ( unsigned long * ) regs - > gprs [ 15 ] ) ;
}
extern void kernel_thread_starter ( void ) ;
__asm__ ( " .align 4 \n "
" kernel_thread_starter: \n "
" la 2,0(10) \n "
" basr 14,9 \n "
" la 2,0 \n "
" br 11 \n " ) ;
int kernel_thread ( int ( * fn ) ( void * ) , void * arg , unsigned long flags )
{
struct pt_regs regs ;
memset ( & regs , 0 , sizeof ( regs ) ) ;
regs . psw . mask = PSW_KERNEL_BITS | PSW_MASK_IO | PSW_MASK_EXT ;
regs . psw . addr = ( unsigned long ) kernel_thread_starter | PSW_ADDR_AMODE ;
regs . gprs [ 9 ] = ( unsigned long ) fn ;
regs . gprs [ 10 ] = ( unsigned long ) arg ;
regs . gprs [ 11 ] = ( unsigned long ) do_exit ;
regs . orig_gpr2 = - 1 ;
/* Ok, create the new process.. */
return do_fork ( flags | CLONE_VM | CLONE_UNTRACED ,
0 , & regs , 0 , NULL , NULL ) ;
}
/*
* Free current thread data structures etc . .
*/
void exit_thread ( void )
{
}
void flush_thread ( void )
{
clear_used_math ( ) ;
clear_tsk_thread_flag ( current , TIF_USEDFPU ) ;
}
void release_thread ( struct task_struct * dead_task )
{
}
int copy_thread ( int nr , unsigned long clone_flags , unsigned long new_stackp ,
unsigned long unused ,
struct task_struct * p , struct pt_regs * regs )
{
struct fake_frame
{
struct stack_frame sf ;
struct pt_regs childregs ;
} * frame ;
2006-01-12 12:05:49 +03:00
frame = container_of ( task_pt_regs ( p ) , struct fake_frame , childregs ) ;
2005-04-17 02:20:36 +04:00
p - > thread . ksp = ( unsigned long ) frame ;
/* Store access registers to kernel stack of new process. */
frame - > childregs = * regs ;
frame - > childregs . gprs [ 2 ] = 0 ; /* child returns 0 on fork. */
frame - > childregs . gprs [ 15 ] = new_stackp ;
frame - > sf . back_chain = 0 ;
/* new return point is ret_from_fork */
frame - > sf . gprs [ 8 ] = ( unsigned long ) ret_from_fork ;
/* fake return stack for resume(), don't go back to schedule */
frame - > sf . gprs [ 9 ] = ( unsigned long ) frame ;
/* Save access registers to new thread structure. */
save_access_regs ( & p - > thread . acrs [ 0 ] ) ;
2006-01-06 11:19:28 +03:00
# ifndef CONFIG_64BIT
2005-04-17 02:20:36 +04:00
/*
* save fprs to current - > thread . fp_regs to merge them with
* the emulated registers and then copy the result to the child .
*/
save_fp_regs ( & current - > thread . fp_regs ) ;
memcpy ( & p - > thread . fp_regs , & current - > thread . fp_regs ,
sizeof ( s390_fp_regs ) ) ;
p - > thread . user_seg = __pa ( ( unsigned long ) p - > mm - > pgd ) | _SEGMENT_TABLE ;
/* Set a new TLS ? */
if ( clone_flags & CLONE_SETTLS )
p - > thread . acrs [ 0 ] = regs - > gprs [ 6 ] ;
2006-01-06 11:19:28 +03:00
# else /* CONFIG_64BIT */
2005-04-17 02:20:36 +04:00
/* Save the fpu registers to new thread structure. */
save_fp_regs ( & p - > thread . fp_regs ) ;
p - > thread . user_seg = __pa ( ( unsigned long ) p - > mm - > pgd ) | _REGION_TABLE ;
/* Set a new TLS ? */
if ( clone_flags & CLONE_SETTLS ) {
if ( test_thread_flag ( TIF_31BIT ) ) {
p - > thread . acrs [ 0 ] = ( unsigned int ) regs - > gprs [ 6 ] ;
} else {
p - > thread . acrs [ 0 ] = ( unsigned int ) ( regs - > gprs [ 6 ] > > 32 ) ;
p - > thread . acrs [ 1 ] = ( unsigned int ) regs - > gprs [ 6 ] ;
}
}
2006-01-06 11:19:28 +03:00
# endif /* CONFIG_64BIT */
2005-04-17 02:20:36 +04:00
/* start new process with ar4 pointing to the correct address space */
p - > thread . mm_segment = get_fs ( ) ;
/* Don't copy debug registers */
memset ( & p - > thread . per_info , 0 , sizeof ( p - > thread . per_info ) ) ;
return 0 ;
}
asmlinkage long sys_fork ( struct pt_regs regs )
{
return do_fork ( SIGCHLD , regs . gprs [ 15 ] , & regs , 0 , NULL , NULL ) ;
}
asmlinkage long sys_clone ( struct pt_regs regs )
{
unsigned long clone_flags ;
unsigned long newsp ;
int __user * parent_tidptr , * child_tidptr ;
clone_flags = regs . gprs [ 3 ] ;
newsp = regs . orig_gpr2 ;
parent_tidptr = ( int __user * ) regs . gprs [ 4 ] ;
child_tidptr = ( int __user * ) regs . gprs [ 5 ] ;
if ( ! newsp )
newsp = regs . gprs [ 15 ] ;
return do_fork ( clone_flags , newsp , & regs , 0 ,
parent_tidptr , child_tidptr ) ;
}
/*
* This is trivial , and on the face of it looks like it
* could equally well be done in user mode .
*
* Not so , for quite unobvious reasons - register pressure .
* In user mode vfork ( ) cannot have a stack frame , and if
* done by calling the " clone() " system call directly , you
* do not have enough call - clobbered registers to hold all
* the information you need .
*/
asmlinkage long sys_vfork ( struct pt_regs regs )
{
return do_fork ( CLONE_VFORK | CLONE_VM | SIGCHLD ,
regs . gprs [ 15 ] , & regs , 0 , NULL , NULL ) ;
}
/*
* sys_execve ( ) executes a new program .
*/
asmlinkage long sys_execve ( struct pt_regs regs )
{
int error ;
char * filename ;
filename = getname ( ( char __user * ) regs . orig_gpr2 ) ;
error = PTR_ERR ( filename ) ;
if ( IS_ERR ( filename ) )
goto out ;
error = do_execve ( filename , ( char __user * __user * ) regs . gprs [ 3 ] ,
( char __user * __user * ) regs . gprs [ 4 ] , & regs ) ;
if ( error = = 0 ) {
task_lock ( current ) ;
current - > ptrace & = ~ PT_DTRACE ;
task_unlock ( current ) ;
current - > thread . fp_regs . fpc = 0 ;
if ( MACHINE_HAS_IEEE )
asm volatile ( " sfpc %0,%0 " : : " d " ( 0 ) ) ;
}
putname ( filename ) ;
out :
return error ;
}
/*
* fill in the FPU structure for a core dump .
*/
int dump_fpu ( struct pt_regs * regs , s390_fp_regs * fpregs )
{
2006-01-06 11:19:28 +03:00
# ifndef CONFIG_64BIT
2005-04-17 02:20:36 +04:00
/*
* save fprs to current - > thread . fp_regs to merge them with
* the emulated registers and then copy the result to the dump .
*/
save_fp_regs ( & current - > thread . fp_regs ) ;
memcpy ( fpregs , & current - > thread . fp_regs , sizeof ( s390_fp_regs ) ) ;
2006-01-06 11:19:28 +03:00
# else /* CONFIG_64BIT */
2005-04-17 02:20:36 +04:00
save_fp_regs ( fpregs ) ;
2006-01-06 11:19:28 +03:00
# endif /* CONFIG_64BIT */
2005-04-17 02:20:36 +04:00
return 1 ;
}
unsigned long get_wchan ( struct task_struct * p )
{
struct stack_frame * sf , * low , * high ;
unsigned long return_address ;
int count ;
2006-01-12 12:05:50 +03:00
if ( ! p | | p = = current | | p - > state = = TASK_RUNNING | | ! task_stack_page ( p ) )
2005-04-17 02:20:36 +04:00
return 0 ;
2006-01-12 12:05:50 +03:00
low = task_stack_page ( p ) ;
high = ( struct stack_frame * ) task_pt_regs ( p ) ;
2005-04-17 02:20:36 +04:00
sf = ( struct stack_frame * ) ( p - > thread . ksp & PSW_ADDR_INSN ) ;
if ( sf < = low | | sf > high )
return 0 ;
for ( count = 0 ; count < 16 ; count + + ) {
sf = ( struct stack_frame * ) ( sf - > back_chain & PSW_ADDR_INSN ) ;
if ( sf < = low | | sf > high )
return 0 ;
return_address = sf - > gprs [ 8 ] & PSW_ADDR_INSN ;
if ( ! in_sched_functions ( return_address ) )
return return_address ;
}
return 0 ;
}