2005-04-16 15:20:36 -07:00
/*
* ipmi_watchdog . c
*
* A watchdog timer based upon the IPMI interface .
*
* Author : MontaVista Software , Inc .
* Corey Minyard < minyard @ mvista . com >
* source @ mvista . com
*
* Copyright 2002 MontaVista Software Inc .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*
*
* THIS SOFTWARE IS PROVIDED ` ` AS IS ' ' AND ANY EXPRESS OR IMPLIED
* WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING ,
* BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS
* OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR
* TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/config.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/ipmi.h>
# include <linux/ipmi_smi.h>
# include <linux/watchdog.h>
# include <linux/miscdevice.h>
# include <linux/init.h>
# include <linux/rwsem.h>
# include <linux/errno.h>
# include <asm/uaccess.h>
# include <linux/notifier.h>
# include <linux/nmi.h>
# include <linux/reboot.h>
# include <linux/wait.h>
# include <linux/poll.h>
2005-11-07 00:59:57 -08:00
# include <linux/string.h>
# include <linux/ctype.h>
2005-11-07 01:00:05 -08:00
# include <asm/atomic.h>
2005-04-16 15:20:36 -07:00
# ifdef CONFIG_X86_LOCAL_APIC
# include <asm/apic.h>
# endif
# define PFX "IPMI Watchdog: "
/*
* The IPMI command / response information for the watchdog timer .
*/
/* values for byte 1 of the set command, byte 2 of the get response. */
# define WDOG_DONT_LOG (1 << 7)
# define WDOG_DONT_STOP_ON_SET (1 << 6)
# define WDOG_SET_TIMER_USE(byte, use) \
byte = ( ( byte ) & 0xf8 ) | ( ( use ) & 0x7 )
# define WDOG_GET_TIMER_USE(byte) ((byte) & 0x7)
# define WDOG_TIMER_USE_BIOS_FRB2 1
# define WDOG_TIMER_USE_BIOS_POST 2
# define WDOG_TIMER_USE_OS_LOAD 3
# define WDOG_TIMER_USE_SMS_OS 4
# define WDOG_TIMER_USE_OEM 5
/* values for byte 2 of the set command, byte 3 of the get response. */
# define WDOG_SET_PRETIMEOUT_ACT(byte, use) \
byte = ( ( byte ) & 0x8f ) | ( ( ( use ) & 0x7 ) < < 4 )
# define WDOG_GET_PRETIMEOUT_ACT(byte) (((byte) >> 4) & 0x7)
# define WDOG_PRETIMEOUT_NONE 0
# define WDOG_PRETIMEOUT_SMI 1
# define WDOG_PRETIMEOUT_NMI 2
# define WDOG_PRETIMEOUT_MSG_INT 3
/* Operations that can be performed on a pretimout. */
# define WDOG_PREOP_NONE 0
# define WDOG_PREOP_PANIC 1
# define WDOG_PREOP_GIVE_DATA 2 / * Cause data to be available to
read . Doesn ' t work in NMI
mode . */
/* Actions to perform on a full timeout. */
# define WDOG_SET_TIMEOUT_ACT(byte, use) \
byte = ( ( byte ) & 0xf8 ) | ( ( use ) & 0x7 )
# define WDOG_GET_TIMEOUT_ACT(byte) ((byte) & 0x7)
# define WDOG_TIMEOUT_NONE 0
# define WDOG_TIMEOUT_RESET 1
# define WDOG_TIMEOUT_POWER_DOWN 2
# define WDOG_TIMEOUT_POWER_CYCLE 3
/* Byte 3 of the get command, byte 4 of the get response is the
pre - timeout in seconds . */
/* Bits for setting byte 4 of the set command, byte 5 of the get response. */
# define WDOG_EXPIRE_CLEAR_BIOS_FRB2 (1 << 1)
# define WDOG_EXPIRE_CLEAR_BIOS_POST (1 << 2)
# define WDOG_EXPIRE_CLEAR_OS_LOAD (1 << 3)
# define WDOG_EXPIRE_CLEAR_SMS_OS (1 << 4)
# define WDOG_EXPIRE_CLEAR_OEM (1 << 5)
/* Setting/getting the watchdog timer value. This is for bytes 5 and
6 ( the timeout time ) of the set command , and bytes 6 and 7 ( the
timeout time ) and 8 and 9 ( the current countdown value ) of the
response . The timeout value is given in seconds ( in the command it
is 100 ms intervals ) . */
# define WDOG_SET_TIMEOUT(byte1, byte2, val) \
( byte1 ) = ( ( ( val ) * 10 ) & 0xff ) , ( byte2 ) = ( ( ( val ) * 10 ) > > 8 )
# define WDOG_GET_TIMEOUT(byte1, byte2) \
( ( ( byte1 ) | ( ( byte2 ) < < 8 ) ) / 10 )
# define IPMI_WDOG_RESET_TIMER 0x22
# define IPMI_WDOG_SET_TIMER 0x24
# define IPMI_WDOG_GET_TIMER 0x25
/* These are here until the real ones get into the watchdog.h interface. */
# ifndef WDIOC_GETTIMEOUT
# define WDIOC_GETTIMEOUT _IOW(WATCHDOG_IOCTL_BASE, 20, int)
# endif
# ifndef WDIOC_SET_PRETIMEOUT
# define WDIOC_SET_PRETIMEOUT _IOW(WATCHDOG_IOCTL_BASE, 21, int)
# endif
# ifndef WDIOC_GET_PRETIMEOUT
# define WDIOC_GET_PRETIMEOUT _IOW(WATCHDOG_IOCTL_BASE, 22, int)
# endif
2005-07-27 11:43:58 -07:00
static int nowayout = WATCHDOG_NOWAYOUT ;
2005-04-16 15:20:36 -07:00
static ipmi_user_t watchdog_user = NULL ;
/* Default the timeout to 10 seconds. */
static int timeout = 10 ;
/* The pre-timeout is disabled by default. */
static int pretimeout = 0 ;
/* Default action is to reset the board on a timeout. */
static unsigned char action_val = WDOG_TIMEOUT_RESET ;
static char action [ 16 ] = " reset " ;
static unsigned char preaction_val = WDOG_PRETIMEOUT_NONE ;
static char preaction [ 16 ] = " pre_none " ;
static unsigned char preop_val = WDOG_PREOP_NONE ;
static char preop [ 16 ] = " preop_none " ;
static DEFINE_SPINLOCK ( ipmi_read_lock ) ;
static char data_to_read = 0 ;
static DECLARE_WAIT_QUEUE_HEAD ( read_q ) ;
static struct fasync_struct * fasync_q = NULL ;
static char pretimeout_since_last_heartbeat = 0 ;
static char expect_close ;
2005-11-07 00:59:57 -08:00
static DECLARE_RWSEM ( register_sem ) ;
/* Parameters to ipmi_set_timeout */
# define IPMI_SET_TIMEOUT_NO_HB 0
# define IPMI_SET_TIMEOUT_HB_IF_NECESSARY 1
# define IPMI_SET_TIMEOUT_FORCE_HB 2
static int ipmi_set_timeout ( int do_heartbeat ) ;
2005-04-16 15:20:36 -07:00
/* If true, the driver will start running as soon as it is configured
and ready . */
static int start_now = 0 ;
2005-11-07 00:59:57 -08:00
static int set_param_int ( const char * val , struct kernel_param * kp )
{
char * endp ;
int l ;
int rv = 0 ;
if ( ! val )
return - EINVAL ;
l = simple_strtoul ( val , & endp , 0 ) ;
if ( endp = = val )
return - EINVAL ;
down_read ( & register_sem ) ;
* ( ( int * ) kp - > arg ) = l ;
if ( watchdog_user )
rv = ipmi_set_timeout ( IPMI_SET_TIMEOUT_HB_IF_NECESSARY ) ;
up_read ( & register_sem ) ;
return rv ;
}
static int get_param_int ( char * buffer , struct kernel_param * kp )
{
return sprintf ( buffer , " %i " , * ( ( int * ) kp - > arg ) ) ;
}
typedef int ( * action_fn ) ( const char * intval , char * outval ) ;
static int action_op ( const char * inval , char * outval ) ;
static int preaction_op ( const char * inval , char * outval ) ;
static int preop_op ( const char * inval , char * outval ) ;
static void check_parms ( void ) ;
static int set_param_str ( const char * val , struct kernel_param * kp )
{
action_fn fn = ( action_fn ) kp - > arg ;
int rv = 0 ;
const char * end ;
char valcp [ 16 ] ;
int len ;
/* Truncate leading and trailing spaces. */
while ( isspace ( * val ) )
val + + ;
end = val + strlen ( val ) - 1 ;
while ( ( end > = val ) & & isspace ( * end ) )
end - - ;
len = end - val + 1 ;
if ( len > sizeof ( valcp ) - 1 )
return - EINVAL ;
memcpy ( valcp , val , len ) ;
valcp [ len ] = ' \0 ' ;
down_read ( & register_sem ) ;
rv = fn ( valcp , NULL ) ;
if ( rv )
goto out_unlock ;
check_parms ( ) ;
if ( watchdog_user )
rv = ipmi_set_timeout ( IPMI_SET_TIMEOUT_HB_IF_NECESSARY ) ;
out_unlock :
up_read ( & register_sem ) ;
return rv ;
}
static int get_param_str ( char * buffer , struct kernel_param * kp )
{
action_fn fn = ( action_fn ) kp - > arg ;
int rv ;
rv = fn ( NULL , buffer ) ;
if ( rv )
return rv ;
return strlen ( buffer ) ;
}
module_param_call ( timeout , set_param_int , get_param_int , & timeout , 0644 ) ;
2005-04-16 15:20:36 -07:00
MODULE_PARM_DESC ( timeout , " Timeout value in seconds. " ) ;
2005-11-07 00:59:57 -08:00
module_param_call ( pretimeout , set_param_int , get_param_int , & pretimeout , 0644 ) ;
2005-04-16 15:20:36 -07:00
MODULE_PARM_DESC ( pretimeout , " Pretimeout value in seconds. " ) ;
2005-11-07 00:59:57 -08:00
module_param_call ( action , set_param_str , get_param_str , action_op , 0644 ) ;
2005-04-16 15:20:36 -07:00
MODULE_PARM_DESC ( action , " Timeout action. One of: "
" reset, none, power_cycle, power_off. " ) ;
2005-11-07 00:59:57 -08:00
module_param_call ( preaction , set_param_str , get_param_str , preaction_op , 0644 ) ;
2005-04-16 15:20:36 -07:00
MODULE_PARM_DESC ( preaction , " Pretimeout action. One of: "
" pre_none, pre_smi, pre_nmi, pre_int. " ) ;
2005-11-07 00:59:57 -08:00
module_param_call ( preop , set_param_str , get_param_str , preop_op , 0644 ) ;
2005-04-16 15:20:36 -07:00
MODULE_PARM_DESC ( preop , " Pretimeout driver operation. One of: "
" preop_none, preop_panic, preop_give_data. " ) ;
2005-11-07 00:59:57 -08:00
2005-04-16 15:20:36 -07:00
module_param ( start_now , int , 0 ) ;
MODULE_PARM_DESC ( start_now , " Set to 1 to start the watchdog as "
" soon as the driver is loaded. " ) ;
2005-11-07 00:59:57 -08:00
module_param ( nowayout , int , 0644 ) ;
2005-04-16 15:20:36 -07:00
MODULE_PARM_DESC ( nowayout , " Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT) " ) ;
/* Default state of the timer. */
static unsigned char ipmi_watchdog_state = WDOG_TIMEOUT_NONE ;
/* If shutting down via IPMI, we ignore the heartbeat. */
static int ipmi_ignore_heartbeat = 0 ;
/* Is someone using the watchdog? Only one user is allowed. */
static unsigned long ipmi_wdog_open = 0 ;
/* If set to 1, the heartbeat command will set the state to reset and
start the timer . The timer doesn ' t normally run when the driver is
first opened until the heartbeat is set the first time , this
variable is used to accomplish this . */
static int ipmi_start_timer_on_heartbeat = 0 ;
/* IPMI version of the BMC. */
static unsigned char ipmi_version_major ;
static unsigned char ipmi_version_minor ;
2005-11-07 01:00:05 -08:00
/* If a pretimeout occurs, this is used to allow only one panic to happen. */
static atomic_t preop_panic_excl = ATOMIC_INIT ( - 1 ) ;
2005-04-16 15:20:36 -07:00
static int ipmi_heartbeat ( void ) ;
static void panic_halt_ipmi_heartbeat ( void ) ;
/* We use a semaphore to make sure that only one thing can send a set
timeout at one time , because we only have one copy of the data .
The semaphore is claimed when the set_timeout is sent and freed
when both messages are free . */
static atomic_t set_timeout_tofree = ATOMIC_INIT ( 0 ) ;
static DECLARE_MUTEX ( set_timeout_lock ) ;
static void set_timeout_free_smi ( struct ipmi_smi_msg * msg )
{
if ( atomic_dec_and_test ( & set_timeout_tofree ) )
up ( & set_timeout_lock ) ;
}
static void set_timeout_free_recv ( struct ipmi_recv_msg * msg )
{
if ( atomic_dec_and_test ( & set_timeout_tofree ) )
up ( & set_timeout_lock ) ;
}
static struct ipmi_smi_msg set_timeout_smi_msg =
{
. done = set_timeout_free_smi
} ;
static struct ipmi_recv_msg set_timeout_recv_msg =
{
. done = set_timeout_free_recv
} ;
static int i_ipmi_set_timeout ( struct ipmi_smi_msg * smi_msg ,
struct ipmi_recv_msg * recv_msg ,
int * send_heartbeat_now )
{
struct kernel_ipmi_msg msg ;
unsigned char data [ 6 ] ;
int rv ;
struct ipmi_system_interface_addr addr ;
int hbnow = 0 ;
data [ 0 ] = 0 ;
WDOG_SET_TIMER_USE ( data [ 0 ] , WDOG_TIMER_USE_SMS_OS ) ;
if ( ( ipmi_version_major > 1 )
| | ( ( ipmi_version_major = = 1 ) & & ( ipmi_version_minor > = 5 ) ) )
{
/* This is an IPMI 1.5-only feature. */
data [ 0 ] | = WDOG_DONT_STOP_ON_SET ;
} else if ( ipmi_watchdog_state ! = WDOG_TIMEOUT_NONE ) {
/* In ipmi 1.0, setting the timer stops the watchdog, we
need to start it back up again . */
hbnow = 1 ;
}
data [ 1 ] = 0 ;
WDOG_SET_TIMEOUT_ACT ( data [ 1 ] , ipmi_watchdog_state ) ;
2005-09-06 15:18:39 -07:00
if ( ( pretimeout > 0 ) & & ( ipmi_watchdog_state ! = WDOG_TIMEOUT_NONE ) ) {
2005-04-16 15:20:36 -07:00
WDOG_SET_PRETIMEOUT_ACT ( data [ 1 ] , preaction_val ) ;
data [ 2 ] = pretimeout ;
} else {
WDOG_SET_PRETIMEOUT_ACT ( data [ 1 ] , WDOG_PRETIMEOUT_NONE ) ;
data [ 2 ] = 0 ; /* No pretimeout. */
}
data [ 3 ] = 0 ;
WDOG_SET_TIMEOUT ( data [ 4 ] , data [ 5 ] , timeout ) ;
addr . addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE ;
addr . channel = IPMI_BMC_CHANNEL ;
addr . lun = 0 ;
msg . netfn = 0x06 ;
msg . cmd = IPMI_WDOG_SET_TIMER ;
msg . data = data ;
msg . data_len = sizeof ( data ) ;
rv = ipmi_request_supply_msgs ( watchdog_user ,
( struct ipmi_addr * ) & addr ,
0 ,
& msg ,
NULL ,
smi_msg ,
recv_msg ,
1 ) ;
if ( rv ) {
printk ( KERN_WARNING PFX " set timeout error: %d \n " ,
rv ) ;
}
if ( send_heartbeat_now )
* send_heartbeat_now = hbnow ;
return rv ;
}
static int ipmi_set_timeout ( int do_heartbeat )
{
int send_heartbeat_now ;
int rv ;
/* We can only send one of these at a time. */
down ( & set_timeout_lock ) ;
atomic_set ( & set_timeout_tofree , 2 ) ;
rv = i_ipmi_set_timeout ( & set_timeout_smi_msg ,
& set_timeout_recv_msg ,
& send_heartbeat_now ) ;
if ( rv ) {
up ( & set_timeout_lock ) ;
} else {
if ( ( do_heartbeat = = IPMI_SET_TIMEOUT_FORCE_HB )
| | ( ( send_heartbeat_now )
& & ( do_heartbeat = = IPMI_SET_TIMEOUT_HB_IF_NECESSARY ) ) )
{
rv = ipmi_heartbeat ( ) ;
}
}
return rv ;
}
static void dummy_smi_free ( struct ipmi_smi_msg * msg )
{
}
static void dummy_recv_free ( struct ipmi_recv_msg * msg )
{
}
static struct ipmi_smi_msg panic_halt_smi_msg =
{
. done = dummy_smi_free
} ;
static struct ipmi_recv_msg panic_halt_recv_msg =
{
. done = dummy_recv_free
} ;
/* Special call, doesn't claim any locks. This is only to be called
at panic or halt time , in run - to - completion mode , when the caller
is the only CPU and the only thing that will be going is these IPMI
calls . */
static void panic_halt_ipmi_set_timeout ( void )
{
int send_heartbeat_now ;
int rv ;
rv = i_ipmi_set_timeout ( & panic_halt_smi_msg ,
& panic_halt_recv_msg ,
& send_heartbeat_now ) ;
if ( ! rv ) {
if ( send_heartbeat_now )
panic_halt_ipmi_heartbeat ( ) ;
}
}
/* We use a semaphore to make sure that only one thing can send a
heartbeat at one time , because we only have one copy of the data .
The semaphore is claimed when the set_timeout is sent and freed
when both messages are free . */
static atomic_t heartbeat_tofree = ATOMIC_INIT ( 0 ) ;
static DECLARE_MUTEX ( heartbeat_lock ) ;
static DECLARE_MUTEX_LOCKED ( heartbeat_wait_lock ) ;
static void heartbeat_free_smi ( struct ipmi_smi_msg * msg )
{
if ( atomic_dec_and_test ( & heartbeat_tofree ) )
up ( & heartbeat_wait_lock ) ;
}
static void heartbeat_free_recv ( struct ipmi_recv_msg * msg )
{
if ( atomic_dec_and_test ( & heartbeat_tofree ) )
up ( & heartbeat_wait_lock ) ;
}
static struct ipmi_smi_msg heartbeat_smi_msg =
{
. done = heartbeat_free_smi
} ;
static struct ipmi_recv_msg heartbeat_recv_msg =
{
. done = heartbeat_free_recv
} ;
static struct ipmi_smi_msg panic_halt_heartbeat_smi_msg =
{
. done = dummy_smi_free
} ;
static struct ipmi_recv_msg panic_halt_heartbeat_recv_msg =
{
. done = dummy_recv_free
} ;
static int ipmi_heartbeat ( void )
{
struct kernel_ipmi_msg msg ;
int rv ;
struct ipmi_system_interface_addr addr ;
if ( ipmi_ignore_heartbeat ) {
return 0 ;
}
if ( ipmi_start_timer_on_heartbeat ) {
ipmi_start_timer_on_heartbeat = 0 ;
ipmi_watchdog_state = action_val ;
return ipmi_set_timeout ( IPMI_SET_TIMEOUT_FORCE_HB ) ;
} else if ( pretimeout_since_last_heartbeat ) {
/* A pretimeout occurred, make sure we set the timeout.
We don ' t want to set the action , though , we want to
leave that alone ( thus it can ' t be combined with the
above operation . */
pretimeout_since_last_heartbeat = 0 ;
return ipmi_set_timeout ( IPMI_SET_TIMEOUT_HB_IF_NECESSARY ) ;
}
down ( & heartbeat_lock ) ;
atomic_set ( & heartbeat_tofree , 2 ) ;
/* Don't reset the timer if we have the timer turned off, that
re - enables the watchdog . */
if ( ipmi_watchdog_state = = WDOG_TIMEOUT_NONE ) {
up ( & heartbeat_lock ) ;
return 0 ;
}
addr . addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE ;
addr . channel = IPMI_BMC_CHANNEL ;
addr . lun = 0 ;
msg . netfn = 0x06 ;
msg . cmd = IPMI_WDOG_RESET_TIMER ;
msg . data = NULL ;
msg . data_len = 0 ;
rv = ipmi_request_supply_msgs ( watchdog_user ,
( struct ipmi_addr * ) & addr ,
0 ,
& msg ,
NULL ,
& heartbeat_smi_msg ,
& heartbeat_recv_msg ,
1 ) ;
if ( rv ) {
up ( & heartbeat_lock ) ;
printk ( KERN_WARNING PFX " heartbeat failure: %d \n " ,
rv ) ;
return rv ;
}
/* Wait for the heartbeat to be sent. */
down ( & heartbeat_wait_lock ) ;
if ( heartbeat_recv_msg . msg . data [ 0 ] ! = 0 ) {
/* Got an error in the heartbeat response. It was already
reported in ipmi_wdog_msg_handler , but we should return
an error here . */
rv = - EINVAL ;
}
up ( & heartbeat_lock ) ;
return rv ;
}
static void panic_halt_ipmi_heartbeat ( void )
{
struct kernel_ipmi_msg msg ;
struct ipmi_system_interface_addr addr ;
/* Don't reset the timer if we have the timer turned off, that
re - enables the watchdog . */
if ( ipmi_watchdog_state = = WDOG_TIMEOUT_NONE )
return ;
addr . addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE ;
addr . channel = IPMI_BMC_CHANNEL ;
addr . lun = 0 ;
msg . netfn = 0x06 ;
msg . cmd = IPMI_WDOG_RESET_TIMER ;
msg . data = NULL ;
msg . data_len = 0 ;
ipmi_request_supply_msgs ( watchdog_user ,
( struct ipmi_addr * ) & addr ,
0 ,
& msg ,
NULL ,
& panic_halt_heartbeat_smi_msg ,
& panic_halt_heartbeat_recv_msg ,
1 ) ;
}
static struct watchdog_info ident =
{
. options = 0 , /* WDIOF_SETTIMEOUT, */
. firmware_version = 1 ,
. identity = " IPMI "
} ;
static int ipmi_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , unsigned long arg )
{
void __user * argp = ( void __user * ) arg ;
int i ;
int val ;
switch ( cmd ) {
case WDIOC_GETSUPPORT :
i = copy_to_user ( argp , & ident , sizeof ( ident ) ) ;
return i ? - EFAULT : 0 ;
case WDIOC_SETTIMEOUT :
i = copy_from_user ( & val , argp , sizeof ( int ) ) ;
if ( i )
return - EFAULT ;
timeout = val ;
return ipmi_set_timeout ( IPMI_SET_TIMEOUT_HB_IF_NECESSARY ) ;
case WDIOC_GETTIMEOUT :
i = copy_to_user ( argp , & timeout , sizeof ( timeout ) ) ;
if ( i )
return - EFAULT ;
return 0 ;
case WDIOC_SET_PRETIMEOUT :
i = copy_from_user ( & val , argp , sizeof ( int ) ) ;
if ( i )
return - EFAULT ;
pretimeout = val ;
return ipmi_set_timeout ( IPMI_SET_TIMEOUT_HB_IF_NECESSARY ) ;
case WDIOC_GET_PRETIMEOUT :
i = copy_to_user ( argp , & pretimeout , sizeof ( pretimeout ) ) ;
if ( i )
return - EFAULT ;
return 0 ;
case WDIOC_KEEPALIVE :
return ipmi_heartbeat ( ) ;
case WDIOC_SETOPTIONS :
i = copy_from_user ( & val , argp , sizeof ( int ) ) ;
if ( i )
return - EFAULT ;
if ( val & WDIOS_DISABLECARD )
{
ipmi_watchdog_state = WDOG_TIMEOUT_NONE ;
ipmi_set_timeout ( IPMI_SET_TIMEOUT_NO_HB ) ;
ipmi_start_timer_on_heartbeat = 0 ;
}
if ( val & WDIOS_ENABLECARD )
{
ipmi_watchdog_state = action_val ;
ipmi_set_timeout ( IPMI_SET_TIMEOUT_FORCE_HB ) ;
}
return 0 ;
case WDIOC_GETSTATUS :
val = 0 ;
i = copy_to_user ( argp , & val , sizeof ( val ) ) ;
if ( i )
return - EFAULT ;
return 0 ;
default :
return - ENOIOCTLCMD ;
}
}
static ssize_t ipmi_write ( struct file * file ,
const char __user * buf ,
size_t len ,
loff_t * ppos )
{
int rv ;
if ( len ) {
if ( ! nowayout ) {
size_t i ;
/* In case it was set long ago */
expect_close = 0 ;
for ( i = 0 ; i ! = len ; i + + ) {
char c ;
if ( get_user ( c , buf + i ) )
return - EFAULT ;
if ( c = = ' V ' )
expect_close = 42 ;
}
}
rv = ipmi_heartbeat ( ) ;
if ( rv )
return rv ;
return 1 ;
}
return 0 ;
}
static ssize_t ipmi_read ( struct file * file ,
char __user * buf ,
size_t count ,
loff_t * ppos )
{
int rv = 0 ;
wait_queue_t wait ;
if ( count < = 0 )
return 0 ;
/* Reading returns if the pretimeout has gone off, and it only does
it once per pretimeout . */
spin_lock ( & ipmi_read_lock ) ;
if ( ! data_to_read ) {
if ( file - > f_flags & O_NONBLOCK ) {
rv = - EAGAIN ;
goto out ;
}
init_waitqueue_entry ( & wait , current ) ;
add_wait_queue ( & read_q , & wait ) ;
while ( ! data_to_read ) {
set_current_state ( TASK_INTERRUPTIBLE ) ;
spin_unlock ( & ipmi_read_lock ) ;
schedule ( ) ;
spin_lock ( & ipmi_read_lock ) ;
}
remove_wait_queue ( & read_q , & wait ) ;
if ( signal_pending ( current ) ) {
rv = - ERESTARTSYS ;
goto out ;
}
}
data_to_read = 0 ;
out :
spin_unlock ( & ipmi_read_lock ) ;
if ( rv = = 0 ) {
if ( copy_to_user ( buf , & data_to_read , 1 ) )
rv = - EFAULT ;
else
rv = 1 ;
}
return rv ;
}
static int ipmi_open ( struct inode * ino , struct file * filep )
{
2005-09-06 15:18:45 -07:00
switch ( iminor ( ino ) ) {
case WATCHDOG_MINOR :
if ( test_and_set_bit ( 0 , & ipmi_wdog_open ) )
2005-04-16 15:20:36 -07:00
return - EBUSY ;
2005-09-06 15:18:45 -07:00
/* Don't start the timer now, let it start on the
first heartbeat . */
ipmi_start_timer_on_heartbeat = 1 ;
return nonseekable_open ( ino , filep ) ;
2005-04-16 15:20:36 -07:00
2005-09-06 15:18:45 -07:00
default :
return ( - ENODEV ) ;
2005-04-16 15:20:36 -07:00
}
}
static unsigned int ipmi_poll ( struct file * file , poll_table * wait )
{
unsigned int mask = 0 ;
poll_wait ( file , & read_q , wait ) ;
spin_lock ( & ipmi_read_lock ) ;
if ( data_to_read )
mask | = ( POLLIN | POLLRDNORM ) ;
spin_unlock ( & ipmi_read_lock ) ;
return mask ;
}
static int ipmi_fasync ( int fd , struct file * file , int on )
{
int result ;
result = fasync_helper ( fd , file , on , & fasync_q ) ;
return ( result ) ;
}
static int ipmi_close ( struct inode * ino , struct file * filep )
{
if ( iminor ( ino ) = = WATCHDOG_MINOR )
{
if ( expect_close = = 42 ) {
ipmi_watchdog_state = WDOG_TIMEOUT_NONE ;
ipmi_set_timeout ( IPMI_SET_TIMEOUT_NO_HB ) ;
} else {
printk ( KERN_CRIT PFX " Unexpected close, not stopping watchdog! \n " ) ;
ipmi_heartbeat ( ) ;
}
2005-05-01 08:59:11 -07:00
clear_bit ( 0 , & ipmi_wdog_open ) ;
2005-04-16 15:20:36 -07:00
}
ipmi_fasync ( - 1 , filep , 0 ) ;
expect_close = 0 ;
return 0 ;
}
static struct file_operations ipmi_wdog_fops = {
. owner = THIS_MODULE ,
. read = ipmi_read ,
. poll = ipmi_poll ,
. write = ipmi_write ,
. ioctl = ipmi_ioctl ,
. open = ipmi_open ,
. release = ipmi_close ,
. fasync = ipmi_fasync ,
} ;
static struct miscdevice ipmi_wdog_miscdev = {
. minor = WATCHDOG_MINOR ,
. name = " watchdog " ,
. fops = & ipmi_wdog_fops
} ;
static void ipmi_wdog_msg_handler ( struct ipmi_recv_msg * msg ,
void * handler_data )
{
if ( msg - > msg . data [ 0 ] ! = 0 ) {
printk ( KERN_ERR PFX " response: Error %x on cmd %x \n " ,
msg - > msg . data [ 0 ] ,
msg - > msg . cmd ) ;
}
ipmi_free_recv_msg ( msg ) ;
}
static void ipmi_wdog_pretimeout_handler ( void * handler_data )
{
if ( preaction_val ! = WDOG_PRETIMEOUT_NONE ) {
2005-11-07 01:00:05 -08:00
if ( preop_val = = WDOG_PREOP_PANIC ) {
if ( atomic_inc_and_test ( & preop_panic_excl ) )
panic ( " Watchdog pre-timeout " ) ;
} else if ( preop_val = = WDOG_PREOP_GIVE_DATA ) {
2005-04-16 15:20:36 -07:00
spin_lock ( & ipmi_read_lock ) ;
data_to_read = 1 ;
wake_up_interruptible ( & read_q ) ;
kill_fasync ( & fasync_q , SIGIO , POLL_IN ) ;
spin_unlock ( & ipmi_read_lock ) ;
}
}
/* On some machines, the heartbeat will give
an error and not work unless we re - enable
the timer . So do so . */
pretimeout_since_last_heartbeat = 1 ;
}
static struct ipmi_user_hndl ipmi_hndlrs =
{
. ipmi_recv_hndl = ipmi_wdog_msg_handler ,
. ipmi_watchdog_pretimeout = ipmi_wdog_pretimeout_handler
} ;
static void ipmi_register_watchdog ( int ipmi_intf )
{
int rv = - EBUSY ;
down_write ( & register_sem ) ;
if ( watchdog_user )
goto out ;
rv = ipmi_create_user ( ipmi_intf , & ipmi_hndlrs , NULL , & watchdog_user ) ;
if ( rv < 0 ) {
printk ( KERN_CRIT PFX " Unable to register with ipmi \n " ) ;
goto out ;
}
ipmi_get_version ( watchdog_user ,
& ipmi_version_major ,
& ipmi_version_minor ) ;
rv = misc_register ( & ipmi_wdog_miscdev ) ;
if ( rv < 0 ) {
ipmi_destroy_user ( watchdog_user ) ;
watchdog_user = NULL ;
printk ( KERN_CRIT PFX " Unable to register misc device \n " ) ;
}
out :
up_write ( & register_sem ) ;
if ( ( start_now ) & & ( rv = = 0 ) ) {
/* Run from startup, so start the timer now. */
start_now = 0 ; /* Disable this function after first startup. */
ipmi_watchdog_state = action_val ;
ipmi_set_timeout ( IPMI_SET_TIMEOUT_FORCE_HB ) ;
printk ( KERN_INFO PFX " Starting now! \n " ) ;
}
}
# ifdef HAVE_NMI_HANDLER
static int
ipmi_nmi ( void * dev_id , struct pt_regs * regs , int cpu , int handled )
{
2005-09-06 15:18:39 -07:00
/* If we are not expecting a timeout, ignore it. */
if ( ipmi_watchdog_state = = WDOG_TIMEOUT_NONE )
return NOTIFY_DONE ;
2005-04-16 15:20:36 -07:00
/* If no one else handled the NMI, we assume it was the IPMI
watchdog . */
2005-09-06 15:18:39 -07:00
if ( ( ! handled ) & & ( preop_val = = WDOG_PREOP_PANIC ) ) {
/* On some machines, the heartbeat will give
an error and not work unless we re - enable
the timer . So do so . */
pretimeout_since_last_heartbeat = 1 ;
2005-11-07 01:00:05 -08:00
if ( atomic_inc_and_test ( & preop_panic_excl ) )
panic ( PFX " pre-timeout " ) ;
2005-09-06 15:18:39 -07:00
}
2005-04-16 15:20:36 -07:00
return NOTIFY_DONE ;
}
static struct nmi_handler ipmi_nmi_handler =
{
. link = LIST_HEAD_INIT ( ipmi_nmi_handler . link ) ,
. dev_name = " ipmi_watchdog " ,
. dev_id = NULL ,
. handler = ipmi_nmi ,
. priority = 0 , /* Call us last. */
} ;
2005-11-07 00:59:57 -08:00
int nmi_handler_registered ;
2005-04-16 15:20:36 -07:00
# endif
static int wdog_reboot_handler ( struct notifier_block * this ,
unsigned long code ,
void * unused )
{
static int reboot_event_handled = 0 ;
if ( ( watchdog_user ) & & ( ! reboot_event_handled ) ) {
/* Make sure we only do this once. */
reboot_event_handled = 1 ;
if ( code = = SYS_DOWN | | code = = SYS_HALT ) {
/* Disable the WDT if we are shutting down. */
ipmi_watchdog_state = WDOG_TIMEOUT_NONE ;
panic_halt_ipmi_set_timeout ( ) ;
} else {
/* Set a long timer to let the reboot happens, but
reboot if it hangs . */
timeout = 120 ;
pretimeout = 0 ;
ipmi_watchdog_state = WDOG_TIMEOUT_RESET ;
panic_halt_ipmi_set_timeout ( ) ;
}
}
return NOTIFY_OK ;
}
static struct notifier_block wdog_reboot_notifier = {
. notifier_call = wdog_reboot_handler ,
. next = NULL ,
. priority = 0
} ;
static int wdog_panic_handler ( struct notifier_block * this ,
unsigned long event ,
void * unused )
{
static int panic_event_handled = 0 ;
/* On a panic, if we have a panic timeout, make sure that the thing
reboots , even if it hangs during that panic . */
if ( watchdog_user & & ! panic_event_handled ) {
/* Make sure the panic doesn't hang, and make sure we
do this only once . */
panic_event_handled = 1 ;
timeout = 255 ;
pretimeout = 0 ;
ipmi_watchdog_state = WDOG_TIMEOUT_RESET ;
panic_halt_ipmi_set_timeout ( ) ;
}
return NOTIFY_OK ;
}
static struct notifier_block wdog_panic_notifier = {
. notifier_call = wdog_panic_handler ,
. next = NULL ,
. priority = 150 /* priority: INT_MAX >= x >= 0 */
} ;
static void ipmi_new_smi ( int if_num )
{
ipmi_register_watchdog ( if_num ) ;
}
static void ipmi_smi_gone ( int if_num )
{
/* This can never be called, because once the watchdog is
registered , the interface can ' t go away until the watchdog
is unregistered . */
}
static struct ipmi_smi_watcher smi_watcher =
{
. owner = THIS_MODULE ,
. new_smi = ipmi_new_smi ,
. smi_gone = ipmi_smi_gone
} ;
2005-11-07 00:59:57 -08:00
static int action_op ( const char * inval , char * outval )
2005-04-16 15:20:36 -07:00
{
2005-11-07 00:59:57 -08:00
if ( outval )
strcpy ( outval , action ) ;
if ( ! inval )
return 0 ;
2005-04-16 15:20:36 -07:00
2005-11-07 00:59:57 -08:00
if ( strcmp ( inval , " reset " ) = = 0 )
2005-04-16 15:20:36 -07:00
action_val = WDOG_TIMEOUT_RESET ;
2005-11-07 00:59:57 -08:00
else if ( strcmp ( inval , " none " ) = = 0 )
2005-04-16 15:20:36 -07:00
action_val = WDOG_TIMEOUT_NONE ;
2005-11-07 00:59:57 -08:00
else if ( strcmp ( inval , " power_cycle " ) = = 0 )
2005-04-16 15:20:36 -07:00
action_val = WDOG_TIMEOUT_POWER_CYCLE ;
2005-11-07 00:59:57 -08:00
else if ( strcmp ( inval , " power_off " ) = = 0 )
2005-04-16 15:20:36 -07:00
action_val = WDOG_TIMEOUT_POWER_DOWN ;
2005-11-07 00:59:57 -08:00
else
return - EINVAL ;
strcpy ( action , inval ) ;
return 0 ;
}
static int preaction_op ( const char * inval , char * outval )
{
if ( outval )
strcpy ( outval , preaction ) ;
2005-04-16 15:20:36 -07:00
2005-11-07 00:59:57 -08:00
if ( ! inval )
return 0 ;
if ( strcmp ( inval , " pre_none " ) = = 0 )
2005-04-16 15:20:36 -07:00
preaction_val = WDOG_PRETIMEOUT_NONE ;
2005-11-07 00:59:57 -08:00
else if ( strcmp ( inval , " pre_smi " ) = = 0 )
2005-04-16 15:20:36 -07:00
preaction_val = WDOG_PRETIMEOUT_SMI ;
# ifdef HAVE_NMI_HANDLER
2005-11-07 00:59:57 -08:00
else if ( strcmp ( inval , " pre_nmi " ) = = 0 )
2005-04-16 15:20:36 -07:00
preaction_val = WDOG_PRETIMEOUT_NMI ;
# endif
2005-11-07 00:59:57 -08:00
else if ( strcmp ( inval , " pre_int " ) = = 0 )
2005-04-16 15:20:36 -07:00
preaction_val = WDOG_PRETIMEOUT_MSG_INT ;
2005-11-07 00:59:57 -08:00
else
return - EINVAL ;
strcpy ( preaction , inval ) ;
return 0 ;
}
static int preop_op ( const char * inval , char * outval )
{
if ( outval )
strcpy ( outval , preop ) ;
2005-04-16 15:20:36 -07:00
2005-11-07 00:59:57 -08:00
if ( ! inval )
return 0 ;
if ( strcmp ( inval , " preop_none " ) = = 0 )
2005-04-16 15:20:36 -07:00
preop_val = WDOG_PREOP_NONE ;
2005-11-07 00:59:57 -08:00
else if ( strcmp ( inval , " preop_panic " ) = = 0 )
2005-04-16 15:20:36 -07:00
preop_val = WDOG_PREOP_PANIC ;
2005-11-07 00:59:57 -08:00
else if ( strcmp ( inval , " preop_give_data " ) = = 0 )
2005-04-16 15:20:36 -07:00
preop_val = WDOG_PREOP_GIVE_DATA ;
2005-11-07 00:59:57 -08:00
else
return - EINVAL ;
strcpy ( preop , inval ) ;
return 0 ;
}
2005-04-16 15:20:36 -07:00
2005-11-07 00:59:57 -08:00
static void check_parms ( void )
{
2005-04-16 15:20:36 -07:00
# ifdef HAVE_NMI_HANDLER
2005-11-07 00:59:57 -08:00
int do_nmi = 0 ;
int rv ;
2005-04-16 15:20:36 -07:00
if ( preaction_val = = WDOG_PRETIMEOUT_NMI ) {
2005-11-07 00:59:57 -08:00
do_nmi = 1 ;
2005-04-16 15:20:36 -07:00
if ( preop_val = = WDOG_PREOP_GIVE_DATA ) {
printk ( KERN_WARNING PFX " Pretimeout op is to give data "
" but NMI pretimeout is enabled, setting "
" pretimeout op to none \n " ) ;
2005-11-07 00:59:57 -08:00
preop_op ( " preop_none " , NULL ) ;
do_nmi = 0 ;
2005-04-16 15:20:36 -07:00
}
# ifdef CONFIG_X86_LOCAL_APIC
if ( nmi_watchdog = = NMI_IO_APIC ) {
printk ( KERN_WARNING PFX " nmi_watchdog is set to IO APIC "
" mode (value is %d), that is incompatible "
" with using NMI in the IPMI watchdog. "
" Disabling IPMI nmi pretimeout. \n " ,
nmi_watchdog ) ;
preaction_val = WDOG_PRETIMEOUT_NONE ;
2005-11-07 00:59:57 -08:00
do_nmi = 0 ;
}
2005-04-16 15:20:36 -07:00
# endif
2005-11-07 00:59:57 -08:00
}
if ( do_nmi & & ! nmi_handler_registered ) {
2005-04-16 15:20:36 -07:00
rv = request_nmi ( & ipmi_nmi_handler ) ;
if ( rv ) {
2005-11-07 00:59:57 -08:00
printk ( KERN_WARNING PFX
" Can't register nmi handler \n " ) ;
return ;
} else
nmi_handler_registered = 1 ;
} else if ( ! do_nmi & & nmi_handler_registered ) {
release_nmi ( & ipmi_nmi_handler ) ;
nmi_handler_registered = 0 ;
2005-04-16 15:20:36 -07:00
}
# endif
2005-11-07 00:59:57 -08:00
}
static int __init ipmi_wdog_init ( void )
{
int rv ;
if ( action_op ( action , NULL ) ) {
action_op ( " reset " , NULL ) ;
printk ( KERN_INFO PFX " Unknown action '%s', defaulting to "
" reset \n " , action ) ;
}
if ( preaction_op ( preaction , NULL ) ) {
preaction_op ( " pre_none " , NULL ) ;
printk ( KERN_INFO PFX " Unknown preaction '%s', defaulting to "
" none \n " , preaction ) ;
}
if ( preop_op ( preop , NULL ) ) {
preop_op ( " preop_none " , NULL ) ;
printk ( KERN_INFO PFX " Unknown preop '%s', defaulting to "
" none \n " , preop ) ;
}
check_parms ( ) ;
2005-04-16 15:20:36 -07:00
rv = ipmi_smi_watcher_register ( & smi_watcher ) ;
if ( rv ) {
# ifdef HAVE_NMI_HANDLER
if ( preaction_val = = WDOG_PRETIMEOUT_NMI )
release_nmi ( & ipmi_nmi_handler ) ;
# endif
printk ( KERN_WARNING PFX " can't register smi watcher \n " ) ;
return rv ;
}
register_reboot_notifier ( & wdog_reboot_notifier ) ;
notifier_chain_register ( & panic_notifier_list , & wdog_panic_notifier ) ;
2005-09-06 15:18:42 -07:00
printk ( KERN_INFO PFX " driver initialized \n " ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
static __exit void ipmi_unregister_watchdog ( void )
{
int rv ;
down_write ( & register_sem ) ;
# ifdef HAVE_NMI_HANDLER
2005-11-07 00:59:57 -08:00
if ( nmi_handler_registered )
2005-04-16 15:20:36 -07:00
release_nmi ( & ipmi_nmi_handler ) ;
# endif
notifier_chain_unregister ( & panic_notifier_list , & wdog_panic_notifier ) ;
unregister_reboot_notifier ( & wdog_reboot_notifier ) ;
if ( ! watchdog_user )
goto out ;
/* Make sure no one can call us any more. */
misc_deregister ( & ipmi_wdog_miscdev ) ;
/* Wait to make sure the message makes it out. The lower layer has
pointers to our buffers , we want to make sure they are done before
we release our memory . */
2005-09-10 00:27:30 -07:00
while ( atomic_read ( & set_timeout_tofree ) )
schedule_timeout_uninterruptible ( 1 ) ;
2005-04-16 15:20:36 -07:00
/* Disconnect from IPMI. */
rv = ipmi_destroy_user ( watchdog_user ) ;
if ( rv ) {
printk ( KERN_WARNING PFX " error unlinking from IPMI: %d \n " ,
rv ) ;
}
watchdog_user = NULL ;
out :
up_write ( & register_sem ) ;
}
static void __exit ipmi_wdog_exit ( void )
{
ipmi_smi_watcher_unregister ( & smi_watcher ) ;
ipmi_unregister_watchdog ( ) ;
}
module_exit ( ipmi_wdog_exit ) ;
module_init ( ipmi_wdog_init ) ;
MODULE_LICENSE ( " GPL " ) ;
2005-09-06 15:18:42 -07:00
MODULE_AUTHOR ( " Corey Minyard <minyard@mvista.com> " ) ;
MODULE_DESCRIPTION ( " watchdog timer based upon the IPMI interface. " ) ;