2005-04-17 02:20:36 +04:00
/* -*- linux-c -*-
*
* $ Id : sysrq . c , v 1.15 1998 / 08 / 23 14 : 56 : 41 mj Exp $
*
* Linux Magic System Request Key Hacks
*
* ( c ) 1997 Martin Mares < mj @ atrey . karlin . mff . cuni . cz >
* based on ideas by Pavel Machek < pavel @ atrey . karlin . mff . cuni . cz >
*
* ( c ) 2000 Crutcher Dunnavant < crutcher + kernel @ datastacks . com >
* overhauled to use key registration
* based upon discusions in irc : //irc.openprojects.net/#kernelnewbies
*/
# include <linux/sched.h>
# include <linux/interrupt.h>
# include <linux/mm.h>
# include <linux/fs.h>
# include <linux/tty.h>
# include <linux/mount.h>
# include <linux/kdev_t.h>
# include <linux/major.h>
# include <linux/reboot.h>
# include <linux/sysrq.h>
# include <linux/kbd_kern.h>
# include <linux/quotaops.h>
# include <linux/smp_lock.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/suspend.h>
# include <linux/writeback.h>
# include <linux/buffer_head.h> /* for fsync_bdev() */
# include <linux/swap.h>
# include <linux/spinlock.h>
# include <linux/vt_kern.h>
# include <linux/workqueue.h>
2005-06-26 01:58:25 +04:00
# include <linux/kexec.h>
2005-04-17 02:20:36 +04:00
# include <asm/ptrace.h>
/* Whether we react on sysrq keys or just ignore them */
int sysrq_enabled = 1 ;
static void sysrq_handle_loglevel ( int key , struct pt_regs * pt_regs ,
2006-03-25 14:07:08 +03:00
struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
int i ;
i = key - ' 0 ' ;
console_loglevel = 7 ;
printk ( " Loglevel set to %d \n " , i ) ;
console_loglevel = i ;
2006-03-25 14:07:08 +03:00
}
2005-04-17 02:20:36 +04:00
static struct sysrq_key_op sysrq_loglevel_op = {
. handler = sysrq_handle_loglevel ,
. help_msg = " loglevel0-8 " ,
. action_msg = " Changing Loglevel " ,
. enable_mask = SYSRQ_ENABLE_LOG ,
} ;
# ifdef CONFIG_VT
static void sysrq_handle_SAK ( int key , struct pt_regs * pt_regs ,
2006-03-25 14:07:08 +03:00
struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
if ( tty )
do_SAK ( tty ) ;
reset_vc ( vc_cons [ fg_console ] . d ) ;
}
static struct sysrq_key_op sysrq_SAK_op = {
. handler = sysrq_handle_SAK ,
. help_msg = " saK " ,
. action_msg = " SAK " ,
. enable_mask = SYSRQ_ENABLE_KEYBOARD ,
} ;
2006-03-25 14:07:08 +03:00
# else
# define sysrq_SAK_op (*(struct sysrq_key_op *)0)
2005-04-17 02:20:36 +04:00
# endif
# ifdef CONFIG_VT
static void sysrq_handle_unraw ( int key , struct pt_regs * pt_regs ,
2006-03-25 14:07:08 +03:00
struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
struct kbd_struct * kbd = & kbd_table [ fg_console ] ;
if ( kbd )
kbd - > kbdmode = VC_XLATE ;
}
static struct sysrq_key_op sysrq_unraw_op = {
. handler = sysrq_handle_unraw ,
. help_msg = " unRaw " ,
. action_msg = " Keyboard mode set to XLATE " ,
. enable_mask = SYSRQ_ENABLE_KEYBOARD ,
} ;
2006-03-25 14:07:08 +03:00
# else
# define sysrq_unraw_op (*(struct sysrq_key_op *)0)
2005-04-17 02:20:36 +04:00
# endif /* CONFIG_VT */
2005-06-26 01:58:25 +04:00
# ifdef CONFIG_KEXEC
static void sysrq_handle_crashdump ( int key , struct pt_regs * pt_regs ,
struct tty_struct * tty )
{
2005-06-26 01:58:26 +04:00
crash_kexec ( pt_regs ) ;
2005-06-26 01:58:25 +04:00
}
static struct sysrq_key_op sysrq_crashdump_op = {
. handler = sysrq_handle_crashdump ,
. help_msg = " Crashdump " ,
. action_msg = " Trigger a crashdump " ,
. enable_mask = SYSRQ_ENABLE_DUMP ,
} ;
2006-03-25 14:07:08 +03:00
# else
# define sysrq_crashdump_op (*(struct sysrq_key_op *)0)
2005-06-26 01:58:25 +04:00
# endif
2005-04-17 02:20:36 +04:00
static void sysrq_handle_reboot ( int key , struct pt_regs * pt_regs ,
2006-03-25 14:07:08 +03:00
struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
local_irq_enable ( ) ;
2005-07-26 21:51:06 +04:00
emergency_restart ( ) ;
2005-04-17 02:20:36 +04:00
}
static struct sysrq_key_op sysrq_reboot_op = {
. handler = sysrq_handle_reboot ,
. help_msg = " reBoot " ,
. action_msg = " Resetting " ,
. enable_mask = SYSRQ_ENABLE_BOOT ,
} ;
static void sysrq_handle_sync ( int key , struct pt_regs * pt_regs ,
2006-03-25 14:07:08 +03:00
struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
emergency_sync ( ) ;
}
static struct sysrq_key_op sysrq_sync_op = {
. handler = sysrq_handle_sync ,
. help_msg = " Sync " ,
. action_msg = " Emergency Sync " ,
. enable_mask = SYSRQ_ENABLE_SYNC ,
} ;
static void sysrq_handle_mountro ( int key , struct pt_regs * pt_regs ,
2006-03-25 14:07:08 +03:00
struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
emergency_remount ( ) ;
}
static struct sysrq_key_op sysrq_mountro_op = {
. handler = sysrq_handle_mountro ,
. help_msg = " Unmount " ,
. action_msg = " Emergency Remount R/O " ,
. enable_mask = SYSRQ_ENABLE_REMOUNT ,
} ;
2006-01-10 02:59:21 +03:00
# ifdef CONFIG_DEBUG_MUTEXES
2006-03-25 14:07:08 +03:00
static void sysrq_handle_showlocks ( int key , struct pt_regs * pt_regs ,
struct tty_struct * tty )
2006-01-10 02:59:21 +03:00
{
2006-07-03 11:24:33 +04:00
debug_show_all_locks ( ) ;
2006-01-10 02:59:21 +03:00
}
static struct sysrq_key_op sysrq_showlocks_op = {
. handler = sysrq_handle_showlocks ,
. help_msg = " show-all-locks(D) " ,
. action_msg = " Show Locks Held " ,
} ;
2006-03-25 14:07:08 +03:00
# else
# define sysrq_showlocks_op (*(struct sysrq_key_op *)0)
2006-01-10 02:59:21 +03:00
# endif
2005-04-17 02:20:36 +04:00
static void sysrq_handle_showregs ( int key , struct pt_regs * pt_regs ,
2006-03-25 14:07:08 +03:00
struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
if ( pt_regs )
show_regs ( pt_regs ) ;
}
static struct sysrq_key_op sysrq_showregs_op = {
. handler = sysrq_handle_showregs ,
. help_msg = " showPc " ,
. action_msg = " Show Regs " ,
. enable_mask = SYSRQ_ENABLE_DUMP ,
} ;
static void sysrq_handle_showstate ( int key , struct pt_regs * pt_regs ,
2006-03-25 14:07:08 +03:00
struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
show_state ( ) ;
}
static struct sysrq_key_op sysrq_showstate_op = {
. handler = sysrq_handle_showstate ,
. help_msg = " showTasks " ,
. action_msg = " Show State " ,
. enable_mask = SYSRQ_ENABLE_DUMP ,
} ;
static void sysrq_handle_showmem ( int key , struct pt_regs * pt_regs ,
2006-03-25 14:07:08 +03:00
struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
show_mem ( ) ;
}
static struct sysrq_key_op sysrq_showmem_op = {
. handler = sysrq_handle_showmem ,
. help_msg = " showMem " ,
. action_msg = " Show Memory " ,
. enable_mask = SYSRQ_ENABLE_DUMP ,
} ;
2006-03-25 14:07:08 +03:00
/*
* Signal sysrq helper function . Sends a signal to all user processes .
*/
2005-04-17 02:20:36 +04:00
static void send_sig_all ( int sig )
{
struct task_struct * p ;
for_each_process ( p ) {
if ( p - > mm & & p - > pid ! = 1 )
/* Not swapper, init nor kernel thread */
force_sig ( sig , p ) ;
}
}
static void sysrq_handle_term ( int key , struct pt_regs * pt_regs ,
2006-03-25 14:07:08 +03:00
struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
send_sig_all ( SIGTERM ) ;
console_loglevel = 8 ;
}
static struct sysrq_key_op sysrq_term_op = {
. handler = sysrq_handle_term ,
. help_msg = " tErm " ,
. action_msg = " Terminate All Tasks " ,
. enable_mask = SYSRQ_ENABLE_SIGNAL ,
} ;
static void moom_callback ( void * ignored )
{
2006-03-25 14:07:08 +03:00
out_of_memory ( & NODE_DATA ( 0 ) - > node_zonelists [ ZONE_NORMAL ] ,
GFP_KERNEL , 0 ) ;
2005-04-17 02:20:36 +04:00
}
static DECLARE_WORK ( moom_work , moom_callback , NULL ) ;
static void sysrq_handle_moom ( int key , struct pt_regs * pt_regs ,
struct tty_struct * tty )
{
schedule_work ( & moom_work ) ;
}
static struct sysrq_key_op sysrq_moom_op = {
. handler = sysrq_handle_moom ,
. help_msg = " Full " ,
. action_msg = " Manual OOM execution " ,
} ;
static void sysrq_handle_kill ( int key , struct pt_regs * pt_regs ,
2006-03-25 14:07:08 +03:00
struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
send_sig_all ( SIGKILL ) ;
console_loglevel = 8 ;
}
static struct sysrq_key_op sysrq_kill_op = {
. handler = sysrq_handle_kill ,
. help_msg = " kIll " ,
. action_msg = " Kill All Tasks " ,
. enable_mask = SYSRQ_ENABLE_SIGNAL ,
} ;
static void sysrq_handle_unrt ( int key , struct pt_regs * pt_regs ,
struct tty_struct * tty )
{
normalize_rt_tasks ( ) ;
}
static struct sysrq_key_op sysrq_unrt_op = {
. handler = sysrq_handle_unrt ,
. help_msg = " Nice " ,
. action_msg = " Nice All RT Tasks " ,
. enable_mask = SYSRQ_ENABLE_RTNICE ,
} ;
/* Key Operations table and lock */
static DEFINE_SPINLOCK ( sysrq_key_table_lock ) ;
2006-03-25 14:07:08 +03:00
static struct sysrq_key_op * sysrq_key_table [ 36 ] = {
& sysrq_loglevel_op , /* 0 */
& sysrq_loglevel_op , /* 1 */
& sysrq_loglevel_op , /* 2 */
& sysrq_loglevel_op , /* 3 */
& sysrq_loglevel_op , /* 4 */
& sysrq_loglevel_op , /* 5 */
& sysrq_loglevel_op , /* 6 */
& sysrq_loglevel_op , /* 7 */
& sysrq_loglevel_op , /* 8 */
& sysrq_loglevel_op , /* 9 */
/*
* Don ' t use for system provided sysrqs , it is handled specially on
* sparc and will never arrive
*/
NULL , /* a */
& sysrq_reboot_op , /* b */
& sysrq_crashdump_op , /* c */
& sysrq_showlocks_op , /* d */
& sysrq_term_op , /* e */
& sysrq_moom_op , /* f */
NULL , /* g */
NULL , /* h */
& sysrq_kill_op , /* i */
NULL , /* j */
& sysrq_SAK_op , /* k */
NULL , /* l */
& sysrq_showmem_op , /* m */
& sysrq_unrt_op , /* n */
/* This will often be registered as 'Off' at init time */
NULL , /* o */
& sysrq_showregs_op , /* p */
NULL , /* q */
& sysrq_unraw_op , /* r */
& sysrq_sync_op , /* s */
& sysrq_showstate_op , /* t */
& sysrq_mountro_op , /* u */
/* May be assigned at init time by SMP VOYAGER */
NULL , /* v */
NULL , /* w */
NULL , /* x */
NULL , /* y */
NULL /* z */
2005-04-17 02:20:36 +04:00
} ;
/* key2index calculation, -1 on invalid index */
2006-03-25 14:07:08 +03:00
static int sysrq_key_table_key2index ( int key )
{
2005-04-17 02:20:36 +04:00
int retval ;
2006-03-25 14:07:08 +03:00
if ( ( key > = ' 0 ' ) & & ( key < = ' 9 ' ) )
2005-04-17 02:20:36 +04:00
retval = key - ' 0 ' ;
2006-03-25 14:07:08 +03:00
else if ( ( key > = ' a ' ) & & ( key < = ' z ' ) )
2005-04-17 02:20:36 +04:00
retval = key + 10 - ' a ' ;
2006-03-25 14:07:08 +03:00
else
2005-04-17 02:20:36 +04:00
retval = - 1 ;
return retval ;
}
/*
* get and put functions for the table , exposed to modules .
*/
2006-03-25 14:07:08 +03:00
struct sysrq_key_op * __sysrq_get_key_op ( int key )
{
struct sysrq_key_op * op_p = NULL ;
2005-04-17 02:20:36 +04:00
int i ;
2006-03-25 14:07:08 +03:00
2005-04-17 02:20:36 +04:00
i = sysrq_key_table_key2index ( key ) ;
2006-03-25 14:07:08 +03:00
if ( i ! = - 1 )
op_p = sysrq_key_table [ i ] ;
2005-04-17 02:20:36 +04:00
return op_p ;
}
2006-03-25 14:07:08 +03:00
static void __sysrq_put_key_op ( int key , struct sysrq_key_op * op_p )
{
int i = sysrq_key_table_key2index ( key ) ;
2005-04-17 02:20:36 +04:00
if ( i ! = - 1 )
sysrq_key_table [ i ] = op_p ;
}
/*
2006-03-25 14:07:08 +03:00
* This is the non - locking version of handle_sysrq . It must / can only be called
* by sysrq key handlers , as they are inside of the lock
2005-04-17 02:20:36 +04:00
*/
2006-03-25 14:07:08 +03:00
void __handle_sysrq ( int key , struct pt_regs * pt_regs , struct tty_struct * tty ,
int check_mask )
2005-04-17 02:20:36 +04:00
{
struct sysrq_key_op * op_p ;
int orig_log_level ;
2006-03-25 14:07:08 +03:00
int i ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
spin_lock_irqsave ( & sysrq_key_table_lock , flags ) ;
orig_log_level = console_loglevel ;
console_loglevel = 7 ;
printk ( KERN_INFO " SysRq : " ) ;
op_p = __sysrq_get_key_op ( key ) ;
if ( op_p ) {
2006-03-25 14:07:08 +03:00
/*
* Should we check for enabled operations ( / proc / sysrq - trigger
* should not ) and is the invoked operation enabled ?
*/
2005-04-17 02:20:36 +04:00
if ( ! check_mask | | sysrq_enabled = = 1 | |
( sysrq_enabled & op_p - > enable_mask ) ) {
2006-03-25 14:07:08 +03:00
printk ( " %s \n " , op_p - > action_msg ) ;
2005-04-17 02:20:36 +04:00
console_loglevel = orig_log_level ;
op_p - > handler ( key , pt_regs , tty ) ;
2006-03-25 14:07:08 +03:00
} else {
2005-04-17 02:20:36 +04:00
printk ( " This sysrq operation is disabled. \n " ) ;
2006-03-25 14:07:08 +03:00
}
2005-04-17 02:20:36 +04:00
} else {
printk ( " HELP : " ) ;
/* Only print the help msg once per handler */
2006-03-25 14:07:08 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( sysrq_key_table ) ; i + + ) {
if ( sysrq_key_table [ i ] ) {
int j ;
for ( j = 0 ; sysrq_key_table [ i ] ! =
sysrq_key_table [ j ] ; j + + )
;
if ( j ! = i )
continue ;
printk ( " %s " , sysrq_key_table [ i ] - > help_msg ) ;
}
2005-04-17 02:20:36 +04:00
}
2006-03-25 14:07:08 +03:00
printk ( " \n " ) ;
2005-04-17 02:20:36 +04:00
console_loglevel = orig_log_level ;
}
spin_unlock_irqrestore ( & sysrq_key_table_lock , flags ) ;
}
/*
* This function is called by the keyboard handler when SysRq is pressed
* and any other keycode arrives .
*/
void handle_sysrq ( int key , struct pt_regs * pt_regs , struct tty_struct * tty )
{
if ( ! sysrq_enabled )
return ;
__handle_sysrq ( key , pt_regs , tty , 1 ) ;
}
2006-03-25 14:07:08 +03:00
EXPORT_SYMBOL ( handle_sysrq ) ;
2005-04-17 02:20:36 +04:00
2005-11-09 08:39:47 +03:00
static int __sysrq_swap_key_ops ( int key , struct sysrq_key_op * insert_op_p ,
2006-03-25 14:07:08 +03:00
struct sysrq_key_op * remove_op_p )
{
2005-04-17 02:20:36 +04:00
int retval ;
unsigned long flags ;
spin_lock_irqsave ( & sysrq_key_table_lock , flags ) ;
if ( __sysrq_get_key_op ( key ) = = remove_op_p ) {
__sysrq_put_key_op ( key , insert_op_p ) ;
retval = 0 ;
} else {
retval = - 1 ;
}
spin_unlock_irqrestore ( & sysrq_key_table_lock , flags ) ;
return retval ;
}
int register_sysrq_key ( int key , struct sysrq_key_op * op_p )
{
return __sysrq_swap_key_ops ( key , op_p , NULL ) ;
}
2006-03-25 14:07:08 +03:00
EXPORT_SYMBOL ( register_sysrq_key ) ;
2005-04-17 02:20:36 +04:00
int unregister_sysrq_key ( int key , struct sysrq_key_op * op_p )
{
return __sysrq_swap_key_ops ( key , NULL , op_p ) ;
}
EXPORT_SYMBOL ( unregister_sysrq_key ) ;