2005-04-17 02:20:36 +04:00
/*
* linux / arch / arm / kernel / process . c
*
* Copyright ( C ) 1996 - 2000 Russell King - Converted to ARM .
* Original Copyright ( C ) 1995 Linus Torvalds
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <stdarg.h>
# include <linux/module.h>
# include <linux/sched.h>
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/stddef.h>
# include <linux/unistd.h>
# include <linux/slab.h>
# include <linux/user.h>
# include <linux/a.out.h>
# include <linux/delay.h>
# include <linux/reboot.h>
# include <linux/interrupt.h>
# include <linux/kallsyms.h>
# include <linux/init.h>
2005-11-03 01:24:33 +03:00
# include <linux/cpu.h>
2006-03-16 02:17:23 +03:00
# include <linux/elfcore.h>
2006-06-19 22:57:12 +04:00
# include <linux/pm.h>
2005-04-17 02:20:36 +04:00
# include <asm/leds.h>
# include <asm/processor.h>
2006-01-03 20:39:34 +03:00
# include <asm/system.h>
2006-06-21 16:31:52 +04:00
# include <asm/thread_notify.h>
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
2005-06-27 17:04:05 +04:00
# include <asm/mach/time.h>
2005-04-17 02:20:36 +04:00
2007-01-09 15:57:37 +03:00
static const char * processor_modes [ ] = {
" USER_26 " , " FIQ_26 " , " IRQ_26 " , " SVC_26 " , " UK4_26 " , " UK5_26 " , " UK6_26 " , " UK7_26 " ,
" UK8_26 " , " UK9_26 " , " UK10_26 " , " UK11_26 " , " UK12_26 " , " UK13_26 " , " UK14_26 " , " UK15_26 " ,
" USER_32 " , " FIQ_32 " , " IRQ_32 " , " SVC_32 " , " UK4_32 " , " UK5_32 " , " UK6_32 " , " ABT_32 " ,
" UK8_32 " , " UK9_32 " , " UK10_32 " , " UND_32 " , " UK12_32 " , " UK13_32 " , " UK14_32 " , " SYS_32 "
} ;
2005-04-17 02:20:36 +04:00
extern void setup_mm_for_reboot ( char mode ) ;
static volatile int hlt_counter ;
# include <asm/arch/system.h>
void disable_hlt ( void )
{
hlt_counter + + ;
}
EXPORT_SYMBOL ( disable_hlt ) ;
void enable_hlt ( void )
{
hlt_counter - - ;
}
EXPORT_SYMBOL ( enable_hlt ) ;
static int __init nohlt_setup ( char * __unused )
{
hlt_counter = 1 ;
return 1 ;
}
static int __init hlt_setup ( char * __unused )
{
hlt_counter = 0 ;
return 1 ;
}
__setup ( " nohlt " , nohlt_setup ) ;
__setup ( " hlt " , hlt_setup ) ;
2006-06-19 22:57:12 +04:00
void arm_machine_restart ( char mode )
{
/*
* Clean and disable cache , and turn off interrupts
*/
cpu_proc_fin ( ) ;
/*
* Tell the mm system that we are going to reboot -
* we may need it to insert some 1 : 1 mappings so that
* soft boot works .
*/
setup_mm_for_reboot ( mode ) ;
/*
* Now call the architecture specific reboot code .
*/
arch_reset ( mode ) ;
/*
* Whoops - the architecture was unable to reboot .
* Tell the user !
*/
mdelay ( 1000 ) ;
printk ( " Reboot failed -- System halted \n " ) ;
while ( 1 ) ;
}
2005-04-17 02:20:36 +04:00
/*
2006-06-19 22:57:12 +04:00
* Function pointers to optional machine specific functions
2005-04-17 02:20:36 +04:00
*/
void ( * pm_idle ) ( void ) ;
EXPORT_SYMBOL ( pm_idle ) ;
void ( * pm_power_off ) ( void ) ;
EXPORT_SYMBOL ( pm_power_off ) ;
2006-06-19 22:57:12 +04:00
void ( * arm_pm_restart ) ( char str ) = arm_machine_restart ;
EXPORT_SYMBOL_GPL ( arm_pm_restart ) ;
2005-04-17 02:20:36 +04:00
/*
* This is our default idle handler . We need to disable
* interrupts here to ensure we don ' t miss a wakeup call .
*/
2006-03-16 02:17:23 +03:00
static void default_idle ( void )
2005-04-17 02:20:36 +04:00
{
[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 ( hlt_counter )
cpu_relax ( ) ;
else {
local_irq_disable ( ) ;
if ( ! need_resched ( ) ) {
timer_dyn_reprogram ( ) ;
arch_idle ( ) ;
}
local_irq_enable ( ) ;
2005-06-27 17:04:05 +04:00
}
2005-04-17 02:20:36 +04:00
}
/*
* The idle thread . We try to conserve power , while trying to keep
* overall latency low . The architecture specific idle is passed
* a value to indicate the level of " idleness " of the system .
*/
void cpu_idle ( void )
{
local_fiq_enable ( ) ;
/* endless idle loop with no priority at all */
while ( 1 ) {
void ( * idle ) ( void ) = pm_idle ;
2005-11-03 01:24:33 +03:00
# ifdef CONFIG_HOTPLUG_CPU
if ( cpu_is_offline ( smp_processor_id ( ) ) ) {
leds_event ( led_idle_start ) ;
cpu_die ( ) ;
}
# endif
2005-04-17 02:20:36 +04:00
if ( ! idle )
idle = default_idle ;
leds_event ( led_idle_start ) ;
while ( ! need_resched ( ) )
idle ( ) ;
leds_event ( led_idle_end ) ;
2005-11-09 08:39:01 +03:00
preempt_enable_no_resched ( ) ;
2005-04-17 02:20:36 +04:00
schedule ( ) ;
2005-11-09 08:39:01 +03:00
preempt_disable ( ) ;
2005-04-17 02:20:36 +04:00
}
}
static char reboot_mode = ' h ' ;
int __init reboot_setup ( char * str )
{
reboot_mode = str [ 0 ] ;
return 1 ;
}
__setup ( " reboot= " , reboot_setup ) ;
void machine_halt ( void )
{
}
void machine_power_off ( void )
{
if ( pm_power_off )
pm_power_off ( ) ;
}
void machine_restart ( char * __unused )
{
2006-06-19 22:57:12 +04:00
arm_pm_restart ( reboot_mode ) ;
2005-04-17 02:20:36 +04:00
}
2005-04-17 18:50:36 +04:00
void __show_regs ( struct pt_regs * regs )
2005-04-17 02:20:36 +04:00
{
2005-04-17 18:50:36 +04:00
unsigned long flags = condition_codes ( regs ) ;
2005-04-17 02:20:36 +04:00
2005-04-17 18:50:36 +04:00
printk ( " CPU: %d \n " , smp_processor_id ( ) ) ;
2005-04-17 02:20:36 +04:00
print_symbol ( " PC is at %s \n " , instruction_pointer ( regs ) ) ;
print_symbol ( " LR is at %s \n " , regs - > ARM_lr ) ;
printk ( " pc : [<%08lx>] lr : [<%08lx>] %s \n "
" sp : %08lx ip : %08lx fp : %08lx \n " ,
instruction_pointer ( regs ) ,
regs - > ARM_lr , print_tainted ( ) , regs - > ARM_sp ,
regs - > ARM_ip , regs - > ARM_fp ) ;
printk ( " r10: %08lx r9 : %08lx r8 : %08lx \n " ,
regs - > ARM_r10 , regs - > ARM_r9 ,
regs - > ARM_r8 ) ;
printk ( " r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx \n " ,
regs - > ARM_r7 , regs - > ARM_r6 ,
regs - > ARM_r5 , regs - > ARM_r4 ) ;
printk ( " r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx \n " ,
regs - > ARM_r3 , regs - > ARM_r2 ,
regs - > ARM_r1 , regs - > ARM_r0 ) ;
printk ( " Flags: %c%c%c%c " ,
flags & PSR_N_BIT ? ' N ' : ' n ' ,
flags & PSR_Z_BIT ? ' Z ' : ' z ' ,
flags & PSR_C_BIT ? ' C ' : ' c ' ,
flags & PSR_V_BIT ? ' V ' : ' v ' ) ;
printk ( " IRQs o%s FIQs o%s Mode %s%s Segment %s \n " ,
interrupts_enabled ( regs ) ? " n " : " ff " ,
fast_interrupts_enabled ( regs ) ? " n " : " ff " ,
processor_modes [ processor_mode ( regs ) ] ,
thumb_mode ( regs ) ? " (T) " : " " ,
get_fs ( ) = = get_ds ( ) ? " kernel " : " user " ) ;
2006-09-26 12:36:37 +04:00
# if CONFIG_CPU_CP15
2005-04-17 02:20:36 +04:00
{
2006-09-26 12:36:37 +04:00
unsigned int ctrl ;
2005-04-17 02:20:36 +04:00
__asm__ (
" mrc p15, 0, %0, c1, c0 \n "
2006-09-26 12:36:37 +04:00
: " =r " ( ctrl ) ) ;
printk ( " Control: %04X \n " , ctrl ) ;
2005-04-17 02:20:36 +04:00
}
2006-09-26 12:36:37 +04:00
# ifdef CONFIG_CPU_CP15_MMU
{
unsigned int transbase , dac ;
__asm__ (
" mrc p15, 0, %0, c2, c0 \n "
" mrc p15, 0, %1, c3, c0 \n "
: " =r " ( transbase ) , " =r " ( dac ) ) ;
printk ( " Table: %08X DAC: %08X \n " ,
transbase , dac ) ;
}
# endif
# endif
2005-04-17 02:20:36 +04:00
}
2005-04-17 18:50:36 +04:00
void show_regs ( struct pt_regs * regs )
{
printk ( " \n " ) ;
printk ( " Pid: %d, comm: %20s \n " , current - > pid , current - > comm ) ;
__show_regs ( regs ) ;
__backtrace ( ) ;
}
2005-04-17 02:20:36 +04:00
void show_fpregs ( struct user_fp * regs )
{
int i ;
for ( i = 0 ; i < 8 ; i + + ) {
unsigned long * p ;
char type ;
p = ( unsigned long * ) ( regs - > fpregs + i ) ;
switch ( regs - > ftype [ i ] ) {
case 1 : type = ' f ' ; break ;
case 2 : type = ' d ' ; break ;
case 3 : type = ' e ' ; break ;
default : type = ' ? ' ; break ;
}
if ( regs - > init_flag )
type = ' ? ' ;
printk ( " f%d(%c): %08lx %08lx %08lx%c " ,
i , type , p [ 0 ] , p [ 1 ] , p [ 2 ] , i & 1 ? ' \n ' : ' ' ) ;
}
printk ( " FPSR: %08lx FPCR: %08lx \n " ,
( unsigned long ) regs - > fpsr ,
( unsigned long ) regs - > fpcr ) ;
}
/*
* Free current thread data structures etc . .
*/
void exit_thread ( void )
{
}
2006-06-21 16:31:52 +04:00
ATOMIC_NOTIFIER_HEAD ( thread_notify_head ) ;
2005-04-17 02:20:36 +04:00
2006-06-21 16:31:52 +04:00
EXPORT_SYMBOL_GPL ( thread_notify_head ) ;
2005-04-17 02:20:36 +04:00
void flush_thread ( void )
{
struct thread_info * thread = current_thread_info ( ) ;
struct task_struct * tsk = current ;
memset ( thread - > used_cp , 0 , sizeof ( thread - > used_cp ) ) ;
memset ( & tsk - > thread . debug , 0 , sizeof ( struct debug_info ) ) ;
2006-06-21 16:31:52 +04:00
memset ( & thread - > fpstate , 0 , sizeof ( union fp_state ) ) ;
thread_notify ( THREAD_NOTIFY_FLUSH , thread ) ;
2005-04-17 02:20:36 +04:00
}
void release_thread ( struct task_struct * dead_task )
{
2006-06-21 16:31:52 +04:00
struct thread_info * thread = task_thread_info ( dead_task ) ;
thread_notify ( THREAD_NOTIFY_RELEASE , thread ) ;
2005-04-17 02:20:36 +04:00
}
asmlinkage void ret_from_fork ( void ) __asm__ ( " ret_from_fork " ) ;
int
copy_thread ( int nr , unsigned long clone_flags , unsigned long stack_start ,
unsigned long stk_sz , struct task_struct * p , struct pt_regs * regs )
{
2006-01-12 12:05:57 +03:00
struct thread_info * thread = task_thread_info ( p ) ;
struct pt_regs * childregs = task_pt_regs ( p ) ;
2005-04-17 02:20:36 +04:00
* childregs = * regs ;
childregs - > ARM_r0 = 0 ;
childregs - > ARM_sp = stack_start ;
memset ( & thread - > cpu_context , 0 , sizeof ( struct cpu_context_save ) ) ;
thread - > cpu_context . sp = ( unsigned long ) childregs ;
thread - > cpu_context . pc = ( unsigned long ) ret_from_fork ;
if ( clone_flags & CLONE_SETTLS )
thread - > tp_value = regs - > ARM_r3 ;
return 0 ;
}
/*
* fill in the fpe structure for a core dump . . .
*/
int dump_fpu ( struct pt_regs * regs , struct user_fp * fp )
{
struct thread_info * thread = current_thread_info ( ) ;
int used_math = thread - > used_cp [ 1 ] | thread - > used_cp [ 2 ] ;
if ( used_math )
memcpy ( fp , & thread - > fpstate . soft , sizeof ( * fp ) ) ;
return used_math ! = 0 ;
}
EXPORT_SYMBOL ( dump_fpu ) ;
/*
* fill in the user structure for a core dump . .
*/
void dump_thread ( struct pt_regs * regs , struct user * dump )
{
struct task_struct * tsk = current ;
dump - > magic = CMAGIC ;
dump - > start_code = tsk - > mm - > start_code ;
dump - > start_stack = regs - > ARM_sp & ~ ( PAGE_SIZE - 1 ) ;
dump - > u_tsize = ( tsk - > mm - > end_code - tsk - > mm - > start_code ) > > PAGE_SHIFT ;
dump - > u_dsize = ( tsk - > mm - > brk - tsk - > mm - > start_data + PAGE_SIZE - 1 ) > > PAGE_SHIFT ;
dump - > u_ssize = 0 ;
dump - > u_debugreg [ 0 ] = tsk - > thread . debug . bp [ 0 ] . address ;
dump - > u_debugreg [ 1 ] = tsk - > thread . debug . bp [ 1 ] . address ;
dump - > u_debugreg [ 2 ] = tsk - > thread . debug . bp [ 0 ] . insn . arm ;
dump - > u_debugreg [ 3 ] = tsk - > thread . debug . bp [ 1 ] . insn . arm ;
dump - > u_debugreg [ 4 ] = tsk - > thread . debug . nsaved ;
if ( dump - > start_stack < 0x04000000 )
dump - > u_ssize = ( 0x04000000 - dump - > start_stack ) > > PAGE_SHIFT ;
dump - > regs = * regs ;
dump - > u_fpvalid = dump_fpu ( regs , & dump - > u_fp ) ;
}
EXPORT_SYMBOL ( dump_thread ) ;
/*
* Shuffle the argument into the correct register before calling the
* thread function . r1 is the thread argument , r2 is the pointer to
* the thread function , and r3 points to the exit function .
*/
extern void kernel_thread_helper ( void ) ;
asm ( " .section .text \n "
" .align \n "
" .type kernel_thread_helper, #function \n "
" kernel_thread_helper: \n "
" mov r0, r1 \n "
" mov lr, r3 \n "
" mov pc, r2 \n "
" .size kernel_thread_helper, . - kernel_thread_helper \n "
" .previous " ) ;
/*
* Create a kernel thread .
*/
pid_t kernel_thread ( int ( * fn ) ( void * ) , void * arg , unsigned long flags )
{
struct pt_regs regs ;
memset ( & regs , 0 , sizeof ( regs ) ) ;
regs . ARM_r1 = ( unsigned long ) arg ;
regs . ARM_r2 = ( unsigned long ) fn ;
regs . ARM_r3 = ( unsigned long ) do_exit ;
regs . ARM_pc = ( unsigned long ) kernel_thread_helper ;
regs . ARM_cpsr = SVC_MODE ;
return do_fork ( flags | CLONE_VM | CLONE_UNTRACED , 0 , & regs , 0 , NULL , NULL ) ;
}
EXPORT_SYMBOL ( kernel_thread ) ;
unsigned long get_wchan ( struct task_struct * p )
{
unsigned long fp , lr ;
2005-05-05 16:11:00 +04:00
unsigned long stack_start , stack_end ;
2005-04-17 02:20:36 +04:00
int count = 0 ;
if ( ! p | | p = = current | | p - > state = = TASK_RUNNING )
return 0 ;
2006-01-12 12:05:57 +03:00
stack_start = ( unsigned long ) end_of_stack ( p ) ;
2006-01-12 12:05:58 +03:00
stack_end = ( unsigned long ) task_stack_page ( p ) + THREAD_SIZE ;
2005-05-05 16:11:00 +04:00
2005-04-17 02:20:36 +04:00
fp = thread_saved_fp ( p ) ;
do {
2005-05-05 16:11:00 +04:00
if ( fp < stack_start | | fp > stack_end )
2005-04-17 02:20:36 +04:00
return 0 ;
lr = pc_pointer ( ( ( unsigned long * ) fp ) [ - 1 ] ) ;
if ( ! in_sched_functions ( lr ) )
return lr ;
fp = * ( unsigned long * ) ( fp - 12 ) ;
} while ( count + + < 16 ) ;
return 0 ;
}