2007-10-19 10:39:16 +04:00
# include <linux/kdebug.h>
# include <linux/kprobes.h>
# include <linux/module.h>
# include <linux/notifier.h>
# include <linux/rcupdate.h>
# include <linux/vmalloc.h>
2008-02-06 12:36:46 +03:00
# include <linux/reboot.h>
2007-10-19 10:39:16 +04:00
/*
* Notifier list for kernel code which wants to be called
* at shutdown . This is used to stop any idling DMA operations
* and the like .
*/
BLOCKING_NOTIFIER_HEAD ( reboot_notifier_list ) ;
/*
* Notifier chain core routines . The exported routines below
* are layered on top of these , with appropriate locking added .
*/
static int notifier_chain_register ( struct notifier_block * * nl ,
struct notifier_block * n )
{
while ( ( * nl ) ! = NULL ) {
if ( n - > priority > ( * nl ) - > priority )
break ;
nl = & ( ( * nl ) - > next ) ;
}
n - > next = * nl ;
rcu_assign_pointer ( * nl , n ) ;
return 0 ;
}
2008-04-29 12:00:45 +04:00
static int notifier_chain_cond_register ( struct notifier_block * * nl ,
struct notifier_block * n )
{
while ( ( * nl ) ! = NULL ) {
if ( ( * nl ) = = n )
return 0 ;
if ( n - > priority > ( * nl ) - > priority )
break ;
nl = & ( ( * nl ) - > next ) ;
}
n - > next = * nl ;
rcu_assign_pointer ( * nl , n ) ;
return 0 ;
}
2007-10-19 10:39:16 +04:00
static int notifier_chain_unregister ( struct notifier_block * * nl ,
struct notifier_block * n )
{
while ( ( * nl ) ! = NULL ) {
if ( ( * nl ) = = n ) {
rcu_assign_pointer ( * nl , n - > next ) ;
return 0 ;
}
nl = & ( ( * nl ) - > next ) ;
}
return - ENOENT ;
}
/**
* notifier_call_chain - Informs the registered notifiers about an event .
* @ nl : Pointer to head of the blocking notifier chain
* @ val : Value passed unmodified to notifier function
* @ v : Pointer passed unmodified to notifier function
* @ nr_to_call : Number of notifier functions to be called . Don ' t care
* value of this parameter is - 1.
* @ nr_calls : Records the number of notifications sent . Don ' t care
* value of this field is NULL .
* @ returns : notifier_call_chain returns the value returned by the
* last notifier function called .
*/
static int __kprobes notifier_call_chain ( struct notifier_block * * nl ,
unsigned long val , void * v ,
int nr_to_call , int * nr_calls )
{
int ret = NOTIFY_DONE ;
struct notifier_block * nb , * next_nb ;
nb = rcu_dereference ( * nl ) ;
while ( nb & & nr_to_call ) {
next_nb = rcu_dereference ( nb - > next ) ;
2008-08-16 02:29:38 +04:00
# ifdef CONFIG_DEBUG_NOTIFIERS
2008-08-16 02:29:38 +04:00
if ( unlikely ( ! func_ptr_is_kernel_text ( nb - > notifier_call ) ) ) {
2008-08-16 02:29:38 +04:00
WARN ( 1 , " Invalid notifier called! " ) ;
nb = next_nb ;
continue ;
}
# endif
2007-10-19 10:39:16 +04:00
ret = nb - > notifier_call ( nb , val , v ) ;
if ( nr_calls )
( * nr_calls ) + + ;
if ( ( ret & NOTIFY_STOP_MASK ) = = NOTIFY_STOP_MASK )
break ;
nb = next_nb ;
nr_to_call - - ;
}
return ret ;
}
/*
* Atomic notifier chain routines . Registration and unregistration
* use a spinlock , and call_chain is synchronized by RCU ( no locks ) .
*/
/**
* atomic_notifier_chain_register - Add notifier to an atomic notifier chain
* @ nh : Pointer to head of the atomic notifier chain
* @ n : New entry in notifier chain
*
* Adds a notifier to an atomic notifier chain .
*
* Currently always returns zero .
*/
int atomic_notifier_chain_register ( struct atomic_notifier_head * nh ,
struct notifier_block * n )
{
unsigned long flags ;
int ret ;
spin_lock_irqsave ( & nh - > lock , flags ) ;
ret = notifier_chain_register ( & nh - > head , n ) ;
spin_unlock_irqrestore ( & nh - > lock , flags ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( atomic_notifier_chain_register ) ;
/**
* atomic_notifier_chain_unregister - Remove notifier from an atomic notifier chain
* @ nh : Pointer to head of the atomic notifier chain
* @ n : Entry to remove from notifier chain
*
* Removes a notifier from an atomic notifier chain .
*
* Returns zero on success or % - ENOENT on failure .
*/
int atomic_notifier_chain_unregister ( struct atomic_notifier_head * nh ,
struct notifier_block * n )
{
unsigned long flags ;
int ret ;
spin_lock_irqsave ( & nh - > lock , flags ) ;
ret = notifier_chain_unregister ( & nh - > head , n ) ;
spin_unlock_irqrestore ( & nh - > lock , flags ) ;
synchronize_rcu ( ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( atomic_notifier_chain_unregister ) ;
/**
* __atomic_notifier_call_chain - Call functions in an atomic notifier chain
* @ nh : Pointer to head of the atomic notifier chain
* @ val : Value passed unmodified to notifier function
* @ v : Pointer passed unmodified to notifier function
* @ nr_to_call : See the comment for notifier_call_chain .
* @ nr_calls : See the comment for notifier_call_chain .
*
* Calls each function in a notifier chain in turn . The functions
* run in an atomic context , so they must not block .
* This routine uses RCU to synchronize with changes to the chain .
*
* If the return value of the notifier can be and ' ed
* with % NOTIFY_STOP_MASK then atomic_notifier_call_chain ( )
* will return immediately , with the return value of
* the notifier function which halted execution .
* Otherwise the return value is the return value
* of the last notifier function called .
*/
int __kprobes __atomic_notifier_call_chain ( struct atomic_notifier_head * nh ,
unsigned long val , void * v ,
int nr_to_call , int * nr_calls )
{
int ret ;
rcu_read_lock ( ) ;
ret = notifier_call_chain ( & nh - > head , val , v , nr_to_call , nr_calls ) ;
rcu_read_unlock ( ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( __atomic_notifier_call_chain ) ;
int __kprobes atomic_notifier_call_chain ( struct atomic_notifier_head * nh ,
unsigned long val , void * v )
{
return __atomic_notifier_call_chain ( nh , val , v , - 1 , NULL ) ;
}
EXPORT_SYMBOL_GPL ( atomic_notifier_call_chain ) ;
/*
* Blocking notifier chain routines . All access to the chain is
* synchronized by an rwsem .
*/
/**
* blocking_notifier_chain_register - Add notifier to a blocking notifier chain
* @ nh : Pointer to head of the blocking notifier chain
* @ n : New entry in notifier chain
*
* Adds a notifier to a blocking notifier chain .
* Must be called in process context .
*
* Currently always returns zero .
*/
int blocking_notifier_chain_register ( struct blocking_notifier_head * nh ,
struct notifier_block * n )
{
int ret ;
/*
* This code gets used during boot - up , when task switching is
* not yet working and interrupts must remain disabled . At
* such times we must not call down_write ( ) .
*/
if ( unlikely ( system_state = = SYSTEM_BOOTING ) )
return notifier_chain_register ( & nh - > head , n ) ;
down_write ( & nh - > rwsem ) ;
ret = notifier_chain_register ( & nh - > head , n ) ;
up_write ( & nh - > rwsem ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( blocking_notifier_chain_register ) ;
2008-04-29 12:00:45 +04:00
/**
* blocking_notifier_chain_cond_register - Cond add notifier to a blocking notifier chain
* @ nh : Pointer to head of the blocking notifier chain
* @ n : New entry in notifier chain
*
* Adds a notifier to a blocking notifier chain , only if not already
* present in the chain .
* Must be called in process context .
*
* Currently always returns zero .
*/
int blocking_notifier_chain_cond_register ( struct blocking_notifier_head * nh ,
struct notifier_block * n )
{
int ret ;
down_write ( & nh - > rwsem ) ;
ret = notifier_chain_cond_register ( & nh - > head , n ) ;
up_write ( & nh - > rwsem ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( blocking_notifier_chain_cond_register ) ;
2007-10-19 10:39:16 +04:00
/**
* blocking_notifier_chain_unregister - Remove notifier from a blocking notifier chain
* @ nh : Pointer to head of the blocking notifier chain
* @ n : Entry to remove from notifier chain
*
* Removes a notifier from a blocking notifier chain .
* Must be called from process context .
*
* Returns zero on success or % - ENOENT on failure .
*/
int blocking_notifier_chain_unregister ( struct blocking_notifier_head * nh ,
struct notifier_block * n )
{
int ret ;
/*
* This code gets used during boot - up , when task switching is
* not yet working and interrupts must remain disabled . At
* such times we must not call down_write ( ) .
*/
if ( unlikely ( system_state = = SYSTEM_BOOTING ) )
return notifier_chain_unregister ( & nh - > head , n ) ;
down_write ( & nh - > rwsem ) ;
ret = notifier_chain_unregister ( & nh - > head , n ) ;
up_write ( & nh - > rwsem ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( blocking_notifier_chain_unregister ) ;
/**
* __blocking_notifier_call_chain - Call functions in a blocking notifier chain
* @ nh : Pointer to head of the blocking notifier chain
* @ val : Value passed unmodified to notifier function
* @ v : Pointer passed unmodified to notifier function
* @ nr_to_call : See comment for notifier_call_chain .
* @ nr_calls : See comment for notifier_call_chain .
*
* Calls each function in a notifier chain in turn . The functions
* run in a process context , so they are allowed to block .
*
* If the return value of the notifier can be and ' ed
* with % NOTIFY_STOP_MASK then blocking_notifier_call_chain ( )
* will return immediately , with the return value of
* the notifier function which halted execution .
* Otherwise the return value is the return value
* of the last notifier function called .
*/
int __blocking_notifier_call_chain ( struct blocking_notifier_head * nh ,
unsigned long val , void * v ,
int nr_to_call , int * nr_calls )
{
int ret = NOTIFY_DONE ;
/*
* We check the head outside the lock , but if this access is
* racy then it does not matter what the result of the test
* is , we re - check the list after having taken the lock anyway :
*/
if ( rcu_dereference ( nh - > head ) ) {
down_read ( & nh - > rwsem ) ;
ret = notifier_call_chain ( & nh - > head , val , v , nr_to_call ,
nr_calls ) ;
up_read ( & nh - > rwsem ) ;
}
return ret ;
}
EXPORT_SYMBOL_GPL ( __blocking_notifier_call_chain ) ;
int blocking_notifier_call_chain ( struct blocking_notifier_head * nh ,
unsigned long val , void * v )
{
return __blocking_notifier_call_chain ( nh , val , v , - 1 , NULL ) ;
}
EXPORT_SYMBOL_GPL ( blocking_notifier_call_chain ) ;
/*
* Raw notifier chain routines . There is no protection ;
* the caller must provide it . Use at your own risk !
*/
/**
* raw_notifier_chain_register - Add notifier to a raw notifier chain
* @ nh : Pointer to head of the raw notifier chain
* @ n : New entry in notifier chain
*
* Adds a notifier to a raw notifier chain .
* All locking must be provided by the caller .
*
* Currently always returns zero .
*/
int raw_notifier_chain_register ( struct raw_notifier_head * nh ,
struct notifier_block * n )
{
return notifier_chain_register ( & nh - > head , n ) ;
}
EXPORT_SYMBOL_GPL ( raw_notifier_chain_register ) ;
/**
* raw_notifier_chain_unregister - Remove notifier from a raw notifier chain
* @ nh : Pointer to head of the raw notifier chain
* @ n : Entry to remove from notifier chain
*
* Removes a notifier from a raw notifier chain .
* All locking must be provided by the caller .
*
* Returns zero on success or % - ENOENT on failure .
*/
int raw_notifier_chain_unregister ( struct raw_notifier_head * nh ,
struct notifier_block * n )
{
return notifier_chain_unregister ( & nh - > head , n ) ;
}
EXPORT_SYMBOL_GPL ( raw_notifier_chain_unregister ) ;
/**
* __raw_notifier_call_chain - Call functions in a raw notifier chain
* @ nh : Pointer to head of the raw notifier chain
* @ val : Value passed unmodified to notifier function
* @ v : Pointer passed unmodified to notifier function
* @ nr_to_call : See comment for notifier_call_chain .
* @ nr_calls : See comment for notifier_call_chain
*
* Calls each function in a notifier chain in turn . The functions
* run in an undefined context .
* All locking must be provided by the caller .
*
* If the return value of the notifier can be and ' ed
* with % NOTIFY_STOP_MASK then raw_notifier_call_chain ( )
* will return immediately , with the return value of
* the notifier function which halted execution .
* Otherwise the return value is the return value
* of the last notifier function called .
*/
int __raw_notifier_call_chain ( struct raw_notifier_head * nh ,
unsigned long val , void * v ,
int nr_to_call , int * nr_calls )
{
return notifier_call_chain ( & nh - > head , val , v , nr_to_call , nr_calls ) ;
}
EXPORT_SYMBOL_GPL ( __raw_notifier_call_chain ) ;
int raw_notifier_call_chain ( struct raw_notifier_head * nh ,
unsigned long val , void * v )
{
return __raw_notifier_call_chain ( nh , val , v , - 1 , NULL ) ;
}
EXPORT_SYMBOL_GPL ( raw_notifier_call_chain ) ;
/*
* SRCU notifier chain routines . Registration and unregistration
* use a mutex , and call_chain is synchronized by SRCU ( no locks ) .
*/
/**
* srcu_notifier_chain_register - Add notifier to an SRCU notifier chain
* @ nh : Pointer to head of the SRCU notifier chain
* @ n : New entry in notifier chain
*
* Adds a notifier to an SRCU notifier chain .
* Must be called in process context .
*
* Currently always returns zero .
*/
int srcu_notifier_chain_register ( struct srcu_notifier_head * nh ,
struct notifier_block * n )
{
int ret ;
/*
* This code gets used during boot - up , when task switching is
* not yet working and interrupts must remain disabled . At
* such times we must not call mutex_lock ( ) .
*/
if ( unlikely ( system_state = = SYSTEM_BOOTING ) )
return notifier_chain_register ( & nh - > head , n ) ;
mutex_lock ( & nh - > mutex ) ;
ret = notifier_chain_register ( & nh - > head , n ) ;
mutex_unlock ( & nh - > mutex ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( srcu_notifier_chain_register ) ;
/**
* srcu_notifier_chain_unregister - Remove notifier from an SRCU notifier chain
* @ nh : Pointer to head of the SRCU notifier chain
* @ n : Entry to remove from notifier chain
*
* Removes a notifier from an SRCU notifier chain .
* Must be called from process context .
*
* Returns zero on success or % - ENOENT on failure .
*/
int srcu_notifier_chain_unregister ( struct srcu_notifier_head * nh ,
struct notifier_block * n )
{
int ret ;
/*
* This code gets used during boot - up , when task switching is
* not yet working and interrupts must remain disabled . At
* such times we must not call mutex_lock ( ) .
*/
if ( unlikely ( system_state = = SYSTEM_BOOTING ) )
return notifier_chain_unregister ( & nh - > head , n ) ;
mutex_lock ( & nh - > mutex ) ;
ret = notifier_chain_unregister ( & nh - > head , n ) ;
mutex_unlock ( & nh - > mutex ) ;
synchronize_srcu ( & nh - > srcu ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( srcu_notifier_chain_unregister ) ;
/**
* __srcu_notifier_call_chain - Call functions in an SRCU notifier chain
* @ nh : Pointer to head of the SRCU notifier chain
* @ val : Value passed unmodified to notifier function
* @ v : Pointer passed unmodified to notifier function
* @ nr_to_call : See comment for notifier_call_chain .
* @ nr_calls : See comment for notifier_call_chain
*
* Calls each function in a notifier chain in turn . The functions
* run in a process context , so they are allowed to block .
*
* If the return value of the notifier can be and ' ed
* with % NOTIFY_STOP_MASK then srcu_notifier_call_chain ( )
* will return immediately , with the return value of
* the notifier function which halted execution .
* Otherwise the return value is the return value
* of the last notifier function called .
*/
int __srcu_notifier_call_chain ( struct srcu_notifier_head * nh ,
unsigned long val , void * v ,
int nr_to_call , int * nr_calls )
{
int ret ;
int idx ;
idx = srcu_read_lock ( & nh - > srcu ) ;
ret = notifier_call_chain ( & nh - > head , val , v , nr_to_call , nr_calls ) ;
srcu_read_unlock ( & nh - > srcu , idx ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( __srcu_notifier_call_chain ) ;
int srcu_notifier_call_chain ( struct srcu_notifier_head * nh ,
unsigned long val , void * v )
{
return __srcu_notifier_call_chain ( nh , val , v , - 1 , NULL ) ;
}
EXPORT_SYMBOL_GPL ( srcu_notifier_call_chain ) ;
/**
* srcu_init_notifier_head - Initialize an SRCU notifier head
* @ nh : Pointer to head of the srcu notifier chain
*
* Unlike other sorts of notifier heads , SRCU notifier heads require
* dynamic initialization . Be sure to call this routine before
* calling any of the other SRCU notifier routines for this head .
*
* If an SRCU notifier head is deallocated , it must first be cleaned
* up by calling srcu_cleanup_notifier_head ( ) . Otherwise the head ' s
* per - cpu data ( used by the SRCU mechanism ) will leak .
*/
void srcu_init_notifier_head ( struct srcu_notifier_head * nh )
{
mutex_init ( & nh - > mutex ) ;
if ( init_srcu_struct ( & nh - > srcu ) < 0 )
BUG ( ) ;
nh - > head = NULL ;
}
EXPORT_SYMBOL_GPL ( srcu_init_notifier_head ) ;
/**
* register_reboot_notifier - Register function to be called at reboot time
* @ nb : Info about notifier function to be called
*
* Registers a function with the list of functions
* to be called at reboot time .
*
* Currently always returns zero , as blocking_notifier_chain_register ( )
* always returns zero .
*/
int register_reboot_notifier ( struct notifier_block * nb )
{
return blocking_notifier_chain_register ( & reboot_notifier_list , nb ) ;
}
EXPORT_SYMBOL ( register_reboot_notifier ) ;
/**
* unregister_reboot_notifier - Unregister previously registered reboot notifier
* @ nb : Hook to be unregistered
*
* Unregisters a previously registered reboot
* notifier function .
*
* Returns zero on success , or % - ENOENT on failure .
*/
int unregister_reboot_notifier ( struct notifier_block * nb )
{
return blocking_notifier_chain_unregister ( & reboot_notifier_list , nb ) ;
}
EXPORT_SYMBOL ( unregister_reboot_notifier ) ;
static ATOMIC_NOTIFIER_HEAD ( die_chain ) ;
int notify_die ( enum die_val val , const char * str ,
struct pt_regs * regs , long err , int trap , int sig )
{
struct die_args args = {
. regs = regs ,
. str = str ,
. err = err ,
. trapnr = trap ,
. signr = sig ,
} ;
return atomic_notifier_call_chain ( & die_chain , val , & args ) ;
}
int register_die_notifier ( struct notifier_block * nb )
{
vmalloc_sync_all ( ) ;
return atomic_notifier_chain_register ( & die_chain , nb ) ;
}
EXPORT_SYMBOL_GPL ( register_die_notifier ) ;
int unregister_die_notifier ( struct notifier_block * nb )
{
return atomic_notifier_chain_unregister ( & die_chain , nb ) ;
}
EXPORT_SYMBOL_GPL ( unregister_die_notifier ) ;