2005-03-24 05:50:00 +03:00
/*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*
2008-04-22 23:46:56 +04:00
* Copyright ( c ) 2004 - 2008 Silicon Graphics , Inc . All Rights Reserved .
2005-03-24 05:50:00 +03:00
*/
/*
* Cross Partition Communication ( XPC ) support - standard version .
*
* XPC provides a message passing capability that crosses partition
* boundaries . This module is made up of two parts :
*
* partition This part detects the presence / absence of other
* partitions . It provides a heartbeat and monitors
* the heartbeats of other partitions .
*
* channel This part manages the channels and sends / receives
* messages across them to / from other partitions .
*
* There are a couple of additional functions residing in XP , which
* provide an interface to XPC for its users .
*
*
* Caveats :
*
* . We currently have no way to determine which nasid an IPI came
* from . Thus , xpc_IPI_send ( ) does a remote AMO write followed by
* an IPI . The AMO indicates where data is to be pulled from , so
* after the IPI arrives , the remote partition checks the AMO word .
* The IPI can actually arrive before the AMO however , so other code
* must periodically check for this case . Also , remote AMO operations
* do not reliably time out . Thus we do a remote PIO read solely to
* know whether the remote partition is down and whether we should
* stop sending IPIs to it . This remote PIO read operation is set up
* in a special nofault region so SAL knows to ignore ( and cleanup )
* any errors due to the remote AMO write , PIO read , and / or PIO
* write operations .
*
* If / when new hardware solves this IPI problem , we should abandon
* the current approach .
*
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/sched.h>
# include <linux/syscalls.h>
# include <linux/cache.h>
# include <linux/interrupt.h>
2005-07-09 04:10:00 +04:00
# include <linux/delay.h>
2005-09-01 23:01:37 +04:00
# include <linux/reboot.h>
2006-01-17 20:52:21 +03:00
# include <linux/completion.h>
2007-05-08 11:27:03 +04:00
# include <linux/kdebug.h>
2005-03-24 05:50:00 +03:00
# include <asm/sn/intr.h>
# include <asm/sn/sn_sal.h>
# include <asm/uaccess.h>
2008-04-22 23:46:56 +04:00
# include "xpc.h"
2005-03-24 05:50:00 +03:00
/* define two XPC debug device structures to be used with dev_dbg() et al */
struct device_driver xpc_dbg_name = {
. name = " xpc "
} ;
struct device xpc_part_dbg_subname = {
. bus_id = { 0 } , /* set to "part" at xpc_init() time */
. driver = & xpc_dbg_name
} ;
struct device xpc_chan_dbg_subname = {
. bus_id = { 0 } , /* set to "chan" at xpc_init() time */
. driver = & xpc_dbg_name
} ;
struct device * xpc_part = & xpc_part_dbg_subname ;
struct device * xpc_chan = & xpc_chan_dbg_subname ;
2006-01-10 20:08:00 +03:00
static int xpc_kdebug_ignore ;
2005-03-24 05:50:00 +03:00
/* systune related variables for /proc/sys directories */
2005-09-01 23:01:37 +04:00
static int xpc_hb_interval = XPC_HB_DEFAULT_INTERVAL ;
static int xpc_hb_min_interval = 1 ;
static int xpc_hb_max_interval = 10 ;
2005-03-24 05:50:00 +03:00
2005-09-01 23:01:37 +04:00
static int xpc_hb_check_interval = XPC_HB_CHECK_DEFAULT_INTERVAL ;
static int xpc_hb_check_min_interval = 10 ;
static int xpc_hb_check_max_interval = 120 ;
2005-03-24 05:50:00 +03:00
2005-10-25 23:07:43 +04:00
int xpc_disengage_request_timelimit = XPC_DISENGAGE_REQUEST_DEFAULT_TIMELIMIT ;
static int xpc_disengage_request_min_timelimit = 0 ;
static int xpc_disengage_request_max_timelimit = 120 ;
2005-03-24 05:50:00 +03:00
static ctl_table xpc_sys_xpc_hb_dir [ ] = {
{
2007-02-14 11:33:41 +03:00
. ctl_name = CTL_UNNUMBERED ,
. procname = " hb_interval " ,
. data = & xpc_hb_interval ,
. maxlen = sizeof ( int ) ,
. mode = 0644 ,
. proc_handler = & proc_dointvec_minmax ,
. strategy = & sysctl_intvec ,
. extra1 = & xpc_hb_min_interval ,
. extra2 = & xpc_hb_max_interval
2005-03-24 05:50:00 +03:00
} ,
{
2007-02-14 11:33:41 +03:00
. ctl_name = CTL_UNNUMBERED ,
. procname = " hb_check_interval " ,
. data = & xpc_hb_check_interval ,
. maxlen = sizeof ( int ) ,
. mode = 0644 ,
. proc_handler = & proc_dointvec_minmax ,
. strategy = & sysctl_intvec ,
. extra1 = & xpc_hb_check_min_interval ,
. extra2 = & xpc_hb_check_max_interval
2005-03-24 05:50:00 +03:00
} ,
2007-02-14 11:33:41 +03:00
{ }
2005-03-24 05:50:00 +03:00
} ;
static ctl_table xpc_sys_xpc_dir [ ] = {
{
2007-02-14 11:33:41 +03:00
. ctl_name = CTL_UNNUMBERED ,
. procname = " hb " ,
. mode = 0555 ,
. child = xpc_sys_xpc_hb_dir
2005-03-24 05:50:00 +03:00
} ,
2005-10-25 23:07:43 +04:00
{
2007-02-14 11:33:41 +03:00
. ctl_name = CTL_UNNUMBERED ,
. procname = " disengage_request_timelimit " ,
. data = & xpc_disengage_request_timelimit ,
. maxlen = sizeof ( int ) ,
. mode = 0644 ,
. proc_handler = & proc_dointvec_minmax ,
. strategy = & sysctl_intvec ,
. extra1 = & xpc_disengage_request_min_timelimit ,
. extra2 = & xpc_disengage_request_max_timelimit
2005-10-25 23:07:43 +04:00
} ,
2007-02-14 11:33:41 +03:00
{ }
2005-03-24 05:50:00 +03:00
} ;
static ctl_table xpc_sys_dir [ ] = {
{
2007-02-14 11:33:41 +03:00
. ctl_name = CTL_UNNUMBERED ,
. procname = " xpc " ,
. mode = 0555 ,
. child = xpc_sys_xpc_dir
2005-03-24 05:50:00 +03:00
} ,
2007-02-14 11:33:41 +03:00
{ }
2005-03-24 05:50:00 +03:00
} ;
static struct ctl_table_header * xpc_sysctl ;
2006-01-06 18:48:21 +03:00
/* non-zero if any remote partition disengage request was timed out */
int xpc_disengage_request_timedout ;
2005-03-24 05:50:00 +03:00
/* #of IRQs received */
static atomic_t xpc_act_IRQ_rcvd ;
/* IRQ handler notifies this wait queue on receipt of an IRQ */
static DECLARE_WAIT_QUEUE_HEAD ( xpc_act_IRQ_wq ) ;
static unsigned long xpc_hb_check_timeout ;
2005-10-25 23:07:43 +04:00
/* notification that the xpc_hb_checker thread has exited */
2006-01-17 20:52:21 +03:00
static DECLARE_COMPLETION ( xpc_hb_checker_exited ) ;
2005-03-24 05:50:00 +03:00
2005-10-25 23:07:43 +04:00
/* notification that the xpc_discovery thread has exited */
2006-01-17 20:52:21 +03:00
static DECLARE_COMPLETION ( xpc_discovery_exited ) ;
2005-03-24 05:50:00 +03:00
static struct timer_list xpc_hb_timer ;
static void xpc_kthread_waitmsgs ( struct xpc_partition * , struct xpc_channel * ) ;
2005-09-01 23:01:37 +04:00
static int xpc_system_reboot ( struct notifier_block * , unsigned long , void * ) ;
static struct notifier_block xpc_reboot_notifier = {
. notifier_call = xpc_system_reboot ,
} ;
2005-11-09 23:41:57 +03:00
static int xpc_system_die ( struct notifier_block * , unsigned long , void * ) ;
static struct notifier_block xpc_die_notifier = {
. notifier_call = xpc_system_die ,
} ;
2005-09-01 23:01:37 +04:00
/*
* Timer function to enforce the timelimit on the partition disengage request .
*/
static void
xpc_timeout_partition_disengage_request ( unsigned long data )
{
struct xpc_partition * part = ( struct xpc_partition * ) data ;
2008-03-29 17:05:30 +03:00
DBUG_ON ( time_before ( jiffies , part - > disengage_request_timeout ) ) ;
2005-09-01 23:01:37 +04:00
( void ) xpc_partition_disengaged ( part ) ;
DBUG_ON ( part - > disengage_request_timeout ! = 0 ) ;
DBUG_ON ( xpc_partition_engaged ( 1UL < < XPC_PARTID ( part ) ) ! = 0 ) ;
}
2005-03-24 05:50:00 +03:00
/*
* Notify the heartbeat check thread that an IRQ has been received .
*/
static irqreturn_t
2006-10-08 17:59:19 +04:00
xpc_act_IRQ_handler ( int irq , void * dev_id )
2005-03-24 05:50:00 +03:00
{
atomic_inc ( & xpc_act_IRQ_rcvd ) ;
wake_up_interruptible ( & xpc_act_IRQ_wq ) ;
return IRQ_HANDLED ;
}
/*
* Timer to produce the heartbeat . The timer structures function is
* already set when this is initially called . A tunable is used to
* specify when the next timeout should occur .
*/
static void
xpc_hb_beater ( unsigned long dummy )
{
xpc_vars - > heartbeat + + ;
2008-03-29 17:05:30 +03:00
if ( time_after_eq ( jiffies , xpc_hb_check_timeout ) ) {
2005-03-24 05:50:00 +03:00
wake_up_interruptible ( & xpc_act_IRQ_wq ) ;
}
xpc_hb_timer . expires = jiffies + ( xpc_hb_interval * HZ ) ;
add_timer ( & xpc_hb_timer ) ;
}
/*
* This thread is responsible for nearly all of the partition
* activation / deactivation .
*/
static int
xpc_hb_checker ( void * ignore )
{
int last_IRQ_count = 0 ;
int new_IRQ_count ;
int force_IRQ = 0 ;
/* this thread was marked active by xpc_hb_init() */
daemonize ( XPC_HB_CHECK_THREAD_NAME ) ;
set_cpus_allowed ( current , cpumask_of_cpu ( XPC_HB_CHECK_CPU ) ) ;
2007-11-07 16:53:06 +03:00
/* set our heartbeating to other partitions into motion */
2005-03-24 05:50:00 +03:00
xpc_hb_check_timeout = jiffies + ( xpc_hb_check_interval * HZ ) ;
2007-11-07 16:53:06 +03:00
xpc_hb_beater ( 0 ) ;
2005-03-24 05:50:00 +03:00
while ( ! ( volatile int ) xpc_exiting ) {
dev_dbg ( xpc_part , " woke up with %d ticks rem; %d IRQs have "
" been received \n " ,
( int ) ( xpc_hb_check_timeout - jiffies ) ,
atomic_read ( & xpc_act_IRQ_rcvd ) - last_IRQ_count ) ;
/* checking of remote heartbeats is skewed by IRQ handling */
2008-03-29 17:05:30 +03:00
if ( time_after_eq ( jiffies , xpc_hb_check_timeout ) ) {
2005-03-24 05:50:00 +03:00
dev_dbg ( xpc_part , " checking remote heartbeats \n " ) ;
xpc_check_remote_hb ( ) ;
/*
* We need to periodically recheck to ensure no
* IPI / AMO pairs have been missed . That check
* must always reset xpc_hb_check_timeout .
*/
force_IRQ = 1 ;
}
2005-09-01 23:01:37 +04:00
/* check for outstanding IRQs */
2005-03-24 05:50:00 +03:00
new_IRQ_count = atomic_read ( & xpc_act_IRQ_rcvd ) ;
if ( last_IRQ_count < new_IRQ_count | | force_IRQ ! = 0 ) {
force_IRQ = 0 ;
dev_dbg ( xpc_part , " found an IRQ to process; will be "
" resetting xpc_hb_check_timeout \n " ) ;
last_IRQ_count + = xpc_identify_act_IRQ_sender ( ) ;
if ( last_IRQ_count < new_IRQ_count ) {
/* retry once to help avoid missing AMO */
( void ) xpc_identify_act_IRQ_sender ( ) ;
}
last_IRQ_count = new_IRQ_count ;
xpc_hb_check_timeout = jiffies +
( xpc_hb_check_interval * HZ ) ;
}
2005-09-01 23:01:37 +04:00
/* wait for IRQ or timeout */
( void ) wait_event_interruptible ( xpc_act_IRQ_wq ,
( last_IRQ_count < atomic_read ( & xpc_act_IRQ_rcvd ) | |
2008-03-29 17:05:30 +03:00
time_after_eq ( jiffies , xpc_hb_check_timeout ) | |
2005-09-01 23:01:37 +04:00
( volatile int ) xpc_exiting ) ) ;
2005-03-24 05:50:00 +03:00
}
dev_dbg ( xpc_part , " heartbeat checker is exiting \n " ) ;
2005-10-25 23:07:43 +04:00
/* mark this thread as having exited */
2006-01-17 20:52:21 +03:00
complete ( & xpc_hb_checker_exited ) ;
2005-03-24 05:50:00 +03:00
return 0 ;
}
/*
* This thread will attempt to discover other partitions to activate
* based on info provided by SAL . This new thread is short lived and
* will exit once discovery is complete .
*/
static int
xpc_initiate_discovery ( void * ignore )
{
daemonize ( XPC_DISCOVERY_THREAD_NAME ) ;
xpc_discovery ( ) ;
dev_dbg ( xpc_part , " discovery thread is exiting \n " ) ;
2005-10-25 23:07:43 +04:00
/* mark this thread as having exited */
2006-01-17 20:52:21 +03:00
complete ( & xpc_discovery_exited ) ;
2005-03-24 05:50:00 +03:00
return 0 ;
}
/*
* Establish first contact with the remote partititon . This involves pulling
* the XPC per partition variables from the remote partition and waiting for
* the remote partition to pull ours .
*/
static enum xpc_retval
xpc_make_first_contact ( struct xpc_partition * part )
{
enum xpc_retval ret ;
while ( ( ret = xpc_pull_remote_vars_part ( part ) ) ! = xpcSuccess ) {
if ( ret ! = xpcRetry ) {
XPC_DEACTIVATE_PARTITION ( part , ret ) ;
return ret ;
}
dev_dbg ( xpc_chan , " waiting to make first contact with "
" partition %d \n " , XPC_PARTID ( part ) ) ;
/* wait a 1/4 of a second or so */
2005-09-01 23:01:37 +04:00
( void ) msleep_interruptible ( 250 ) ;
2005-03-24 05:50:00 +03:00
if ( part - > act_state = = XPC_P_DEACTIVATING ) {
return part - > reason ;
}
}
return xpc_mark_partition_active ( part ) ;
}
/*
* The first kthread assigned to a newly activated partition is the one
* created by XPC HB with which it calls xpc_partition_up ( ) . XPC hangs on to
* that kthread until the partition is brought down , at which time that kthread
* returns back to XPC HB . ( The return of that kthread will signify to XPC HB
* that XPC has dismantled all communication infrastructure for the associated
* partition . ) This kthread becomes the channel manager for that partition .
*
* Each active partition has a channel manager , who , besides connecting and
* disconnecting channels , will ensure that each of the partition ' s connected
* channels has the required number of assigned kthreads to get the work done .
*/
static void
xpc_channel_mgr ( struct xpc_partition * part )
{
while ( part - > act_state ! = XPC_P_DEACTIVATING | |
2005-09-01 23:01:37 +04:00
atomic_read ( & part - > nchannels_active ) > 0 | |
! xpc_partition_disengaged ( part ) ) {
2005-03-24 05:50:00 +03:00
xpc_process_channel_activity ( part ) ;
/*
* Wait until we ' ve been requested to activate kthreads or
* all of the channel ' s message queues have been torn down or
* a signal is pending .
*
* The channel_mgr_requests is set to 1 after being awakened ,
* This is done to prevent the channel mgr from making one pass
* through the loop for each request , since he will
* be servicing all the requests in one pass . The reason it ' s
* set to 1 instead of 0 is so that other kthreads will know
* that the channel mgr is running and won ' t bother trying to
* wake him up .
*/
atomic_dec ( & part - > channel_mgr_requests ) ;
( void ) wait_event_interruptible ( part - > channel_mgr_wq ,
( atomic_read ( & part - > channel_mgr_requests ) > 0 | |
( volatile u64 ) part - > local_IPI_amo ! = 0 | |
( ( volatile u8 ) part - > act_state = =
XPC_P_DEACTIVATING & &
2005-09-01 23:01:37 +04:00
atomic_read ( & part - > nchannels_active ) = = 0 & &
xpc_partition_disengaged ( part ) ) ) ) ;
2005-03-24 05:50:00 +03:00
atomic_set ( & part - > channel_mgr_requests , 1 ) ;
// >>> Does it need to wakeup periodically as well? In case we
// >>> miscalculated the #of kthreads to wakeup or create?
}
}
/*
* When XPC HB determines that a partition has come up , it will create a new
* kthread and that kthread will call this function to attempt to set up the
* basic infrastructure used for Cross Partition Communication with the newly
* upped partition .
*
* The kthread that was created by XPC HB and which setup the XPC
* infrastructure will remain assigned to the partition until the partition
* goes down . At which time the kthread will teardown the XPC infrastructure
* and then exit .
*
* XPC HB will put the remote partition ' s XPC per partition specific variables
* physical address into xpc_partitions [ partid ] . remote_vars_part_pa prior to
* calling xpc_partition_up ( ) .
*/
static void
xpc_partition_up ( struct xpc_partition * part )
{
DBUG_ON ( part - > channels ! = NULL ) ;
dev_dbg ( xpc_chan , " activating partition %d \n " , XPC_PARTID ( part ) ) ;
if ( xpc_setup_infrastructure ( part ) ! = xpcSuccess ) {
return ;
}
/*
* The kthread that XPC HB called us with will become the
* channel manager for this partition . It will not return
* back to XPC HB until the partition ' s XPC infrastructure
* has been dismantled .
*/
( void ) xpc_part_ref ( part ) ; /* this will always succeed */
if ( xpc_make_first_contact ( part ) = = xpcSuccess ) {
xpc_channel_mgr ( part ) ;
}
xpc_part_deref ( part ) ;
xpc_teardown_infrastructure ( part ) ;
}
static int
xpc_activating ( void * __partid )
{
partid_t partid = ( u64 ) __partid ;
struct xpc_partition * part = & xpc_partitions [ partid ] ;
unsigned long irq_flags ;
2006-07-17 09:41:59 +04:00
struct sched_param param = { . sched_priority = MAX_RT_PRIO - 1 } ;
2005-03-24 05:50:00 +03:00
int ret ;
DBUG_ON ( partid < = 0 | | partid > = XP_MAX_PARTITIONS ) ;
spin_lock_irqsave ( & part - > act_lock , irq_flags ) ;
if ( part - > act_state = = XPC_P_DEACTIVATING ) {
part - > act_state = XPC_P_INACTIVE ;
spin_unlock_irqrestore ( & part - > act_lock , irq_flags ) ;
part - > remote_rp_pa = 0 ;
return 0 ;
}
/* indicate the thread is activating */
DBUG_ON ( part - > act_state ! = XPC_P_ACTIVATION_REQ ) ;
part - > act_state = XPC_P_ACTIVATING ;
XPC_SET_REASON ( part , 0 , 0 ) ;
spin_unlock_irqrestore ( & part - > act_lock , irq_flags ) ;
dev_dbg ( xpc_part , " bringing partition %d up \n " , partid ) ;
daemonize ( " xpc%02d " , partid ) ;
/*
* This thread needs to run at a realtime priority to prevent a
* significant performance degradation .
*/
ret = sched_setscheduler ( current , SCHED_FIFO , & param ) ;
if ( ret ! = 0 ) {
dev_warn ( xpc_part , " unable to set pid %d to a realtime "
" priority, ret=%d \n " , current - > pid , ret ) ;
}
/* allow this thread and its children to run on any CPU */
set_cpus_allowed ( current , CPU_MASK_ALL ) ;
/*
* Register the remote partition ' s AMOs with SAL so it can handle
* and cleanup errors within that address range should the remote
* partition go down . We don ' t unregister this range because it is
* difficult to tell when outstanding writes to the remote partition
* are finished and thus when it is safe to unregister . This should
* not result in wasted space in the SAL xp_addr_region table because
* we should get the same page for remote_amos_page_pa after module
* reloads and system reboots .
*/
if ( sn_register_xp_addr_region ( part - > remote_amos_page_pa ,
PAGE_SIZE , 1 ) < 0 ) {
dev_warn ( xpc_part , " xpc_partition_up(%d) failed to register "
" xp_addr region \n " , partid ) ;
spin_lock_irqsave ( & part - > act_lock , irq_flags ) ;
part - > act_state = XPC_P_INACTIVE ;
XPC_SET_REASON ( part , xpcPhysAddrRegFailed , __LINE__ ) ;
spin_unlock_irqrestore ( & part - > act_lock , irq_flags ) ;
part - > remote_rp_pa = 0 ;
return 0 ;
}
2005-09-01 23:01:37 +04:00
xpc_allow_hb ( partid , xpc_vars ) ;
2005-03-24 05:50:00 +03:00
xpc_IPI_send_activated ( part ) ;
/*
* xpc_partition_up ( ) holds this thread and marks this partition as
* XPC_P_ACTIVE by calling xpc_hb_mark_active ( ) .
*/
( void ) xpc_partition_up ( part ) ;
2005-09-01 23:01:37 +04:00
xpc_disallow_hb ( partid , xpc_vars ) ;
2005-03-24 05:50:00 +03:00
xpc_mark_partition_inactive ( part ) ;
if ( part - > reason = = xpcReactivating ) {
/* interrupting ourselves results in activating partition */
xpc_IPI_send_reactivate ( part ) ;
}
return 0 ;
}
void
xpc_activate_partition ( struct xpc_partition * part )
{
partid_t partid = XPC_PARTID ( part ) ;
unsigned long irq_flags ;
pid_t pid ;
spin_lock_irqsave ( & part - > act_lock , irq_flags ) ;
DBUG_ON ( part - > act_state ! = XPC_P_INACTIVE ) ;
2006-02-02 21:30:21 +03:00
part - > act_state = XPC_P_ACTIVATION_REQ ;
XPC_SET_REASON ( part , xpcCloneKThread , __LINE__ ) ;
2005-03-24 05:50:00 +03:00
spin_unlock_irqrestore ( & part - > act_lock , irq_flags ) ;
2006-02-02 21:30:21 +03:00
pid = kernel_thread ( xpc_activating , ( void * ) ( ( u64 ) partid ) , 0 ) ;
if ( unlikely ( pid < = 0 ) ) {
spin_lock_irqsave ( & part - > act_lock , irq_flags ) ;
part - > act_state = XPC_P_INACTIVE ;
XPC_SET_REASON ( part , xpcCloneKThreadFailed , __LINE__ ) ;
spin_unlock_irqrestore ( & part - > act_lock , irq_flags ) ;
}
2005-03-24 05:50:00 +03:00
}
/*
* Handle the receipt of a SGI_XPC_NOTIFY IRQ by seeing whether the specified
* partition actually sent it . Since SGI_XPC_NOTIFY IRQs may be shared by more
* than one partition , we use an AMO_t structure per partition to indicate
* whether a partition has sent an IPI or not . > > > If it has , then wake up the
* associated kthread to handle it .
*
* All SGI_XPC_NOTIFY IRQs received by XPC are the result of IPIs sent by XPC
* running on other partitions .
*
* Noteworthy Arguments :
*
* irq - Interrupt ReQuest number . NOT USED .
*
* dev_id - partid of IPI ' s potential sender .
*/
irqreturn_t
2006-10-08 17:59:19 +04:00
xpc_notify_IRQ_handler ( int irq , void * dev_id )
2005-03-24 05:50:00 +03:00
{
partid_t partid = ( partid_t ) ( u64 ) dev_id ;
struct xpc_partition * part = & xpc_partitions [ partid ] ;
DBUG_ON ( partid < = 0 | | partid > = XP_MAX_PARTITIONS ) ;
if ( xpc_part_ref ( part ) ) {
xpc_check_for_channel_activity ( part ) ;
xpc_part_deref ( part ) ;
}
return IRQ_HANDLED ;
}
/*
* Check to see if xpc_notify_IRQ_handler ( ) dropped any IPIs on the floor
* because the write to their associated IPI amo completed after the IRQ / IPI
* was received .
*/
void
xpc_dropped_IPI_check ( struct xpc_partition * part )
{
if ( xpc_part_ref ( part ) ) {
xpc_check_for_channel_activity ( part ) ;
part - > dropped_IPI_timer . expires = jiffies +
XPC_P_DROPPED_IPI_WAIT ;
add_timer ( & part - > dropped_IPI_timer ) ;
xpc_part_deref ( part ) ;
}
}
void
xpc_activate_kthreads ( struct xpc_channel * ch , int needed )
{
int idle = atomic_read ( & ch - > kthreads_idle ) ;
int assigned = atomic_read ( & ch - > kthreads_assigned ) ;
int wakeup ;
DBUG_ON ( needed < = 0 ) ;
if ( idle > 0 ) {
wakeup = ( needed > idle ) ? idle : needed ;
needed - = wakeup ;
dev_dbg ( xpc_chan , " wakeup %d idle kthreads, partid=%d, "
" channel=%d \n " , wakeup , ch - > partid , ch - > number ) ;
/* only wakeup the requested number of kthreads */
wake_up_nr ( & ch - > idle_wq , wakeup ) ;
}
if ( needed < = 0 ) {
return ;
}
if ( needed + assigned > ch - > kthreads_assigned_limit ) {
needed = ch - > kthreads_assigned_limit - assigned ;
// >>>should never be less than 0
if ( needed < = 0 ) {
return ;
}
}
dev_dbg ( xpc_chan , " create %d new kthreads, partid=%d, channel=%d \n " ,
needed , ch - > partid , ch - > number ) ;
2006-11-22 17:25:00 +03:00
xpc_create_kthreads ( ch , needed , 0 ) ;
2005-03-24 05:50:00 +03:00
}
/*
* This function is where XPC ' s kthreads wait for messages to deliver .
*/
static void
xpc_kthread_waitmsgs ( struct xpc_partition * part , struct xpc_channel * ch )
{
do {
/* deliver messages to their intended recipients */
while ( ( volatile s64 ) ch - > w_local_GP . get <
( volatile s64 ) ch - > w_remote_GP . put & &
! ( ( volatile u32 ) ch - > flags &
XPC_C_DISCONNECTING ) ) {
xpc_deliver_msg ( ch ) ;
}
if ( atomic_inc_return ( & ch - > kthreads_idle ) >
ch - > kthreads_idle_limit ) {
/* too many idle kthreads on this channel */
atomic_dec ( & ch - > kthreads_idle ) ;
break ;
}
dev_dbg ( xpc_chan , " idle kthread calling "
" wait_event_interruptible_exclusive() \n " ) ;
( void ) wait_event_interruptible_exclusive ( ch - > idle_wq ,
( ( volatile s64 ) ch - > w_local_GP . get <
( volatile s64 ) ch - > w_remote_GP . put | |
( ( volatile u32 ) ch - > flags &
XPC_C_DISCONNECTING ) ) ) ;
atomic_dec ( & ch - > kthreads_idle ) ;
} while ( ! ( ( volatile u32 ) ch - > flags & XPC_C_DISCONNECTING ) ) ;
}
static int
xpc_daemonize_kthread ( void * args )
{
partid_t partid = XPC_UNPACK_ARG1 ( args ) ;
u16 ch_number = XPC_UNPACK_ARG2 ( args ) ;
struct xpc_partition * part = & xpc_partitions [ partid ] ;
struct xpc_channel * ch ;
int n_needed ;
2005-10-25 23:07:43 +04:00
unsigned long irq_flags ;
2005-03-24 05:50:00 +03:00
daemonize ( " xpc%02dc%d " , partid , ch_number ) ;
dev_dbg ( xpc_chan , " kthread starting, partid=%d, channel=%d \n " ,
partid , ch_number ) ;
ch = & part - > channels [ ch_number ] ;
if ( ! ( ch - > flags & XPC_C_DISCONNECTING ) ) {
/* let registerer know that connection has been established */
2005-10-25 23:07:43 +04:00
spin_lock_irqsave ( & ch - > lock , irq_flags ) ;
2006-02-15 17:02:21 +03:00
if ( ! ( ch - > flags & XPC_C_CONNECTEDCALLOUT ) ) {
ch - > flags | = XPC_C_CONNECTEDCALLOUT ;
2005-10-25 23:07:43 +04:00
spin_unlock_irqrestore ( & ch - > lock , irq_flags ) ;
2005-03-24 05:50:00 +03:00
xpc_connected_callout ( ch ) ;
2006-02-15 17:02:21 +03:00
spin_lock_irqsave ( & ch - > lock , irq_flags ) ;
ch - > flags | = XPC_C_CONNECTEDCALLOUT_MADE ;
spin_unlock_irqrestore ( & ch - > lock , irq_flags ) ;
2005-03-24 05:50:00 +03:00
/*
* It is possible that while the callout was being
* made that the remote partition sent some messages .
* If that is the case , we may need to activate
* additional kthreads to help deliver them . We only
* need one less than total # of messages to deliver .
*/
n_needed = ch - > w_remote_GP . put - ch - > w_local_GP . get - 1 ;
if ( n_needed > 0 & &
! ( ch - > flags & XPC_C_DISCONNECTING ) ) {
xpc_activate_kthreads ( ch , n_needed ) ;
}
2005-10-25 23:07:43 +04:00
} else {
spin_unlock_irqrestore ( & ch - > lock , irq_flags ) ;
2005-03-24 05:50:00 +03:00
}
xpc_kthread_waitmsgs ( part , ch ) ;
}
2006-11-22 17:25:00 +03:00
/* let registerer know that connection is disconnecting */
2005-10-25 23:07:43 +04:00
2006-11-22 17:25:00 +03:00
spin_lock_irqsave ( & ch - > lock , irq_flags ) ;
if ( ( ch - > flags & XPC_C_CONNECTEDCALLOUT_MADE ) & &
! ( ch - > flags & XPC_C_DISCONNECTINGCALLOUT ) ) {
ch - > flags | = XPC_C_DISCONNECTINGCALLOUT ;
2006-02-15 17:02:21 +03:00
spin_unlock_irqrestore ( & ch - > lock , irq_flags ) ;
2006-11-22 17:25:00 +03:00
xpc_disconnect_callout ( ch , xpcDisconnecting ) ;
spin_lock_irqsave ( & ch - > lock , irq_flags ) ;
ch - > flags | = XPC_C_DISCONNECTINGCALLOUT_MADE ;
}
spin_unlock_irqrestore ( & ch - > lock , irq_flags ) ;
if ( atomic_dec_return ( & ch - > kthreads_assigned ) = = 0 ) {
2005-09-01 23:01:37 +04:00
if ( atomic_dec_return ( & part - > nchannels_engaged ) = = 0 ) {
xpc_mark_partition_disengaged ( part ) ;
xpc_IPI_send_disengage ( part ) ;
}
2005-03-24 05:50:00 +03:00
}
xpc_msgqueue_deref ( ch ) ;
dev_dbg ( xpc_chan , " kthread exiting, partid=%d, channel=%d \n " ,
partid , ch_number ) ;
xpc_part_deref ( part ) ;
return 0 ;
}
/*
* For each partition that XPC has established communications with , there is
* a minimum of one kernel thread assigned to perform any operation that
* may potentially sleep or block ( basically the callouts to the asynchronous
* functions registered via xpc_connect ( ) ) .
*
* Additional kthreads are created and destroyed by XPC as the workload
* demands .
*
* A kthread is assigned to one of the active channels that exists for a given
* partition .
*/
void
2006-11-22 17:25:00 +03:00
xpc_create_kthreads ( struct xpc_channel * ch , int needed ,
int ignore_disconnecting )
2005-03-24 05:50:00 +03:00
{
unsigned long irq_flags ;
pid_t pid ;
u64 args = XPC_PACK_ARGS ( ch - > partid , ch - > number ) ;
2005-09-01 23:01:37 +04:00
struct xpc_partition * part = & xpc_partitions [ ch - > partid ] ;
2005-03-24 05:50:00 +03:00
while ( needed - - > 0 ) {
2005-10-25 23:07:43 +04:00
/*
* The following is done on behalf of the newly created
* kthread . That kthread is responsible for doing the
* counterpart to the following before it exits .
*/
2006-11-22 17:25:00 +03:00
if ( ignore_disconnecting ) {
if ( ! atomic_inc_not_zero ( & ch - > kthreads_assigned ) ) {
/* kthreads assigned had gone to zero */
BUG_ON ( ! ( ch - > flags &
XPC_C_DISCONNECTINGCALLOUT_MADE ) ) ;
break ;
}
} else if ( ch - > flags & XPC_C_DISCONNECTING ) {
break ;
} else if ( atomic_inc_return ( & ch - > kthreads_assigned ) = = 1 ) {
if ( atomic_inc_return ( & part - > nchannels_engaged ) = = 1 )
xpc_mark_partition_engaged ( part ) ;
}
2005-10-25 23:07:43 +04:00
( void ) xpc_part_ref ( part ) ;
xpc_msgqueue_ref ( ch ) ;
2005-03-24 05:50:00 +03:00
pid = kernel_thread ( xpc_daemonize_kthread , ( void * ) args , 0 ) ;
if ( pid < 0 ) {
/* the fork failed */
2006-11-22 17:25:00 +03:00
/*
* NOTE : if ( ignore_disconnecting & &
* ! ( ch - > flags & XPC_C_DISCONNECTINGCALLOUT ) ) is true ,
* then we ' ll deadlock if all other kthreads assigned
* to this channel are blocked in the channel ' s
* registerer , because the only thing that will unblock
* them is the xpcDisconnecting callout that this
* failed kernel_thread would have made .
*/
2005-10-25 23:07:43 +04:00
if ( atomic_dec_return ( & ch - > kthreads_assigned ) = = 0 & &
atomic_dec_return ( & part - > nchannels_engaged ) = = 0 ) {
xpc_mark_partition_disengaged ( part ) ;
xpc_IPI_send_disengage ( part ) ;
}
xpc_msgqueue_deref ( ch ) ;
xpc_part_deref ( part ) ;
2005-03-24 05:50:00 +03:00
if ( atomic_read ( & ch - > kthreads_assigned ) <
ch - > kthreads_idle_limit ) {
/*
* Flag this as an error only if we have an
* insufficient # of kthreads for the channel
* to function .
*/
spin_lock_irqsave ( & ch - > lock , irq_flags ) ;
XPC_DISCONNECT_CHANNEL ( ch , xpcLackOfResources ,
& irq_flags ) ;
spin_unlock_irqrestore ( & ch - > lock , irq_flags ) ;
}
break ;
}
ch - > kthreads_created + + ; // >>> temporary debug only!!!
}
}
void
xpc_disconnect_wait ( int ch_number )
{
2005-09-01 23:01:37 +04:00
unsigned long irq_flags ;
2005-03-24 05:50:00 +03:00
partid_t partid ;
struct xpc_partition * part ;
struct xpc_channel * ch ;
2005-10-25 23:07:43 +04:00
int wakeup_channel_mgr ;
2005-03-24 05:50:00 +03:00
/* now wait for all callouts to the caller's function to cease */
for ( partid = 1 ; partid < XP_MAX_PARTITIONS ; partid + + ) {
part = & xpc_partitions [ partid ] ;
2005-10-25 23:07:43 +04:00
if ( ! xpc_part_ref ( part ) ) {
continue ;
}
2005-03-24 05:50:00 +03:00
2005-10-25 23:07:43 +04:00
ch = & part - > channels [ ch_number ] ;
2005-03-24 05:50:00 +03:00
2005-10-25 23:07:43 +04:00
if ( ! ( ch - > flags & XPC_C_WDISCONNECT ) ) {
2005-03-24 05:50:00 +03:00
xpc_part_deref ( part ) ;
2005-10-25 23:07:43 +04:00
continue ;
2005-03-24 05:50:00 +03:00
}
2005-10-25 23:07:43 +04:00
2006-01-17 20:52:21 +03:00
wait_for_completion ( & ch - > wdisconnect_wait ) ;
2005-10-25 23:07:43 +04:00
spin_lock_irqsave ( & ch - > lock , irq_flags ) ;
DBUG_ON ( ! ( ch - > flags & XPC_C_DISCONNECTED ) ) ;
wakeup_channel_mgr = 0 ;
if ( ch - > delayed_IPI_flags ) {
if ( part - > act_state ! = XPC_P_DEACTIVATING ) {
spin_lock ( & part - > IPI_lock ) ;
XPC_SET_IPI_FLAGS ( part - > local_IPI_amo ,
ch - > number , ch - > delayed_IPI_flags ) ;
spin_unlock ( & part - > IPI_lock ) ;
wakeup_channel_mgr = 1 ;
}
ch - > delayed_IPI_flags = 0 ;
2005-03-24 05:50:00 +03:00
}
2005-10-25 23:07:43 +04:00
ch - > flags & = ~ XPC_C_WDISCONNECT ;
spin_unlock_irqrestore ( & ch - > lock , irq_flags ) ;
if ( wakeup_channel_mgr ) {
xpc_wakeup_channel_mgr ( part ) ;
}
xpc_part_deref ( part ) ;
2005-03-24 05:50:00 +03:00
}
}
static void
2005-09-01 23:01:37 +04:00
xpc_do_exit ( enum xpc_retval reason )
2005-03-24 05:50:00 +03:00
{
partid_t partid ;
2006-01-06 18:48:21 +03:00
int active_part_count , printed_waiting_msg = 0 ;
2005-03-24 05:50:00 +03:00
struct xpc_partition * part ;
2006-01-06 18:48:21 +03:00
unsigned long printmsg_time , disengage_request_timeout = 0 ;
2005-03-24 05:50:00 +03:00
2005-09-01 23:01:37 +04:00
/* a 'rmmod XPC' and a 'reboot' cannot both end up here together */
DBUG_ON ( xpc_exiting = = 1 ) ;
2005-03-24 05:50:00 +03:00
/*
2005-09-01 23:01:37 +04:00
* Let the heartbeat checker thread and the discovery thread
* ( if one is running ) know that they should exit . Also wake up
* the heartbeat checker thread in case it ' s sleeping .
2005-03-24 05:50:00 +03:00
*/
xpc_exiting = 1 ;
wake_up_interruptible ( & xpc_act_IRQ_wq ) ;
2005-09-01 23:01:37 +04:00
/* ignore all incoming interrupts */
free_irq ( SGI_XPC_ACTIVATE , NULL ) ;
2005-03-24 05:50:00 +03:00
2005-10-25 23:07:43 +04:00
/* wait for the discovery thread to exit */
2006-01-17 20:52:21 +03:00
wait_for_completion ( & xpc_discovery_exited ) ;
2005-03-24 05:50:00 +03:00
2005-10-25 23:07:43 +04:00
/* wait for the heartbeat checker thread to exit */
2006-01-17 20:52:21 +03:00
wait_for_completion ( & xpc_hb_checker_exited ) ;
2005-03-24 05:50:00 +03:00
2005-09-01 23:01:37 +04:00
/* sleep for a 1/3 of a second or so */
( void ) msleep_interruptible ( 300 ) ;
2005-03-24 05:50:00 +03:00
/* wait for all partitions to become inactive */
2006-01-06 18:48:21 +03:00
printmsg_time = jiffies + ( XPC_DISENGAGE_PRINTMSG_INTERVAL * HZ ) ;
xpc_disengage_request_timedout = 0 ;
2005-09-01 23:01:37 +04:00
2005-03-24 05:50:00 +03:00
do {
active_part_count = 0 ;
for ( partid = 1 ; partid < XP_MAX_PARTITIONS ; partid + + ) {
part = & xpc_partitions [ partid ] ;
2005-09-01 23:01:37 +04:00
if ( xpc_partition_disengaged ( part ) & &
part - > act_state = = XPC_P_INACTIVE ) {
continue ;
2005-03-24 05:50:00 +03:00
}
2005-09-01 23:01:37 +04:00
active_part_count + + ;
XPC_DEACTIVATE_PARTITION ( part , reason ) ;
2005-03-24 05:50:00 +03:00
2006-01-06 18:48:21 +03:00
if ( part - > disengage_request_timeout >
disengage_request_timeout ) {
disengage_request_timeout =
part - > disengage_request_timeout ;
}
2005-09-01 23:01:37 +04:00
}
2005-03-24 05:50:00 +03:00
2006-01-06 18:48:21 +03:00
if ( xpc_partition_engaged ( - 1UL ) ) {
if ( time_after ( jiffies , printmsg_time ) ) {
dev_info ( xpc_part , " waiting for remote "
" partitions to disengage, timeout in "
" %ld seconds \n " ,
( disengage_request_timeout - jiffies )
/ HZ ) ;
printmsg_time = jiffies +
2005-09-01 23:01:37 +04:00
( XPC_DISENGAGE_PRINTMSG_INTERVAL * HZ ) ;
2006-01-06 18:48:21 +03:00
printed_waiting_msg = 1 ;
}
} else if ( active_part_count > 0 ) {
if ( printed_waiting_msg ) {
dev_info ( xpc_part , " waiting for local partition "
" to disengage \n " ) ;
printed_waiting_msg = 0 ;
}
} else {
if ( ! xpc_disengage_request_timedout ) {
dev_info ( xpc_part , " all partitions have "
" disengaged \n " ) ;
}
break ;
2005-03-24 05:50:00 +03:00
}
2005-09-01 23:01:37 +04:00
/* sleep for a 1/3 of a second or so */
( void ) msleep_interruptible ( 300 ) ;
} while ( 1 ) ;
DBUG_ON ( xpc_partition_engaged ( - 1UL ) ) ;
/* indicate to others that our reserved page is uninitialized */
xpc_rsvd_page - > vars_pa = 0 ;
/* now it's time to eliminate our heartbeat */
del_timer_sync ( & xpc_hb_timer ) ;
2005-10-25 23:07:43 +04:00
DBUG_ON ( xpc_vars - > heartbeating_to_mask ! = 0 ) ;
2005-03-24 05:50:00 +03:00
2006-01-10 20:07:19 +03:00
if ( reason = = xpcUnloading ) {
/* take ourselves off of the reboot_notifier_list */
( void ) unregister_reboot_notifier ( & xpc_reboot_notifier ) ;
2005-03-24 05:50:00 +03:00
2006-01-10 20:07:19 +03:00
/* take ourselves off of the die_notifier list */
( void ) unregister_die_notifier ( & xpc_die_notifier ) ;
}
2005-11-09 23:41:57 +03:00
2005-03-24 05:50:00 +03:00
/* close down protections for IPI operations */
xpc_restrict_IPI_ops ( ) ;
/* clear the interface to XPC's functions */
xpc_clear_interface ( ) ;
if ( xpc_sysctl ) {
unregister_sysctl_table ( xpc_sysctl ) ;
}
2006-08-09 00:03:29 +04:00
kfree ( xpc_remote_copy_buffer_base ) ;
2005-03-24 05:50:00 +03:00
}
2005-11-09 23:41:57 +03:00
/*
2006-01-10 20:08:55 +03:00
* This function is called when the system is being rebooted .
*/
static int
xpc_system_reboot ( struct notifier_block * nb , unsigned long event , void * unused )
{
enum xpc_retval reason ;
switch ( event ) {
case SYS_RESTART :
reason = xpcSystemReboot ;
break ;
case SYS_HALT :
reason = xpcSystemHalt ;
break ;
case SYS_POWER_OFF :
reason = xpcSystemPoweroff ;
break ;
default :
reason = xpcSystemGoingDown ;
}
xpc_do_exit ( reason ) ;
return NOTIFY_DONE ;
}
/*
* Notify other partitions to disengage from all references to our memory .
2005-11-09 23:41:57 +03:00
*/
static void
xpc_die_disengage ( void )
{
struct xpc_partition * part ;
partid_t partid ;
unsigned long engaged ;
2006-01-06 18:48:21 +03:00
long time , printmsg_time , disengage_request_timeout ;
2005-11-09 23:41:57 +03:00
/* keep xpc_hb_checker thread from doing anything (just in case) */
xpc_exiting = 1 ;
xpc_vars - > heartbeating_to_mask = 0 ; /* indicate we're deactivated */
for ( partid = 1 ; partid < XP_MAX_PARTITIONS ; partid + + ) {
part = & xpc_partitions [ partid ] ;
if ( ! XPC_SUPPORTS_DISENGAGE_REQUEST ( part - >
remote_vars_version ) ) {
/* just in case it was left set by an earlier XPC */
xpc_clear_partition_engaged ( 1UL < < partid ) ;
continue ;
}
if ( xpc_partition_engaged ( 1UL < < partid ) | |
part - > act_state ! = XPC_P_INACTIVE ) {
xpc_request_partition_disengage ( part ) ;
xpc_mark_partition_disengaged ( part ) ;
xpc_IPI_send_disengage ( part ) ;
}
}
2006-01-06 18:48:21 +03:00
time = rtc_time ( ) ;
printmsg_time = time +
( XPC_DISENGAGE_PRINTMSG_INTERVAL * sn_rtc_cycles_per_second ) ;
disengage_request_timeout = time +
2005-11-09 23:41:57 +03:00
( xpc_disengage_request_timelimit * sn_rtc_cycles_per_second ) ;
/* wait for all other partitions to disengage from us */
2006-01-06 18:48:21 +03:00
while ( 1 ) {
engaged = xpc_partition_engaged ( - 1UL ) ;
if ( ! engaged ) {
dev_info ( xpc_part , " all partitions have disengaged \n " ) ;
break ;
}
2005-11-09 23:41:57 +03:00
2006-01-06 18:48:21 +03:00
time = rtc_time ( ) ;
if ( time > = disengage_request_timeout ) {
for ( partid = 1 ; partid < XP_MAX_PARTITIONS ; partid + + ) {
if ( engaged & ( 1UL < < partid ) ) {
dev_info ( xpc_part , " disengage from "
" remote partition %d timed "
" out \n " , partid ) ;
}
}
break ;
}
if ( time > = printmsg_time ) {
2005-11-09 23:41:57 +03:00
dev_info ( xpc_part , " waiting for remote partitions to "
2006-01-06 18:48:21 +03:00
" disengage, timeout in %ld seconds \n " ,
( disengage_request_timeout - time ) /
sn_rtc_cycles_per_second ) ;
printmsg_time = time +
( XPC_DISENGAGE_PRINTMSG_INTERVAL *
2005-11-09 23:41:57 +03:00
sn_rtc_cycles_per_second ) ;
}
}
}
/*
2006-01-10 20:08:00 +03:00
* This function is called when the system is being restarted or halted due
* to some sort of system failure . If this is the case we need to notify the
* other partitions to disengage from all references to our memory .
* This function can also be called when our heartbeater could be offlined
* for a time . In this case we need to notify other partitions to not worry
* about the lack of a heartbeat .
2005-11-09 23:41:57 +03:00
*/
static int
xpc_system_die ( struct notifier_block * nb , unsigned long event , void * unused )
{
switch ( event ) {
case DIE_MACHINE_RESTART :
case DIE_MACHINE_HALT :
xpc_die_disengage ( ) ;
break ;
2006-01-10 20:08:00 +03:00
case DIE_KDEBUG_ENTER :
/* Should lack of heartbeat be ignored by other partitions? */
if ( ! xpc_kdebug_ignore ) {
break ;
}
/* fall through */
2005-11-09 23:41:57 +03:00
case DIE_MCA_MONARCH_ENTER :
case DIE_INIT_MONARCH_ENTER :
xpc_vars - > heartbeat + + ;
xpc_vars - > heartbeat_offline = 1 ;
break ;
2006-01-10 20:08:00 +03:00
case DIE_KDEBUG_LEAVE :
/* Is lack of heartbeat being ignored by other partitions? */
if ( ! xpc_kdebug_ignore ) {
break ;
}
/* fall through */
2005-11-09 23:41:57 +03:00
case DIE_MCA_MONARCH_LEAVE :
case DIE_INIT_MONARCH_LEAVE :
xpc_vars - > heartbeat + + ;
xpc_vars - > heartbeat_offline = 0 ;
break ;
}
return NOTIFY_DONE ;
}
2005-03-24 05:50:00 +03:00
int __init
xpc_init ( void )
{
int ret ;
partid_t partid ;
struct xpc_partition * part ;
pid_t pid ;
2006-08-09 00:03:29 +04:00
size_t buf_size ;
2005-03-24 05:50:00 +03:00
2005-09-08 19:46:58 +04:00
if ( ! ia64_platform_is ( " sn2 " ) ) {
return - ENODEV ;
}
2006-08-09 00:03:29 +04:00
buf_size = max ( XPC_RP_VARS_SIZE ,
XPC_RP_HEADER_SIZE + XP_NASID_MASK_BYTES ) ;
xpc_remote_copy_buffer = xpc_kmalloc_cacheline_aligned ( buf_size ,
GFP_KERNEL , & xpc_remote_copy_buffer_base ) ;
if ( xpc_remote_copy_buffer = = NULL )
return - ENOMEM ;
2005-03-24 05:50:00 +03:00
snprintf ( xpc_part - > bus_id , BUS_ID_SIZE , " part " ) ;
snprintf ( xpc_chan - > bus_id , BUS_ID_SIZE , " chan " ) ;
2007-02-14 11:34:09 +03:00
xpc_sysctl = register_sysctl_table ( xpc_sys_dir ) ;
2005-03-24 05:50:00 +03:00
/*
* The first few fields of each entry of xpc_partitions [ ] need to
* be initialized now so that calls to xpc_connect ( ) and
* xpc_disconnect ( ) can be made prior to the activation of any remote
* partition . NOTE THAT NONE OF THE OTHER FIELDS BELONGING TO THESE
* ENTRIES ARE MEANINGFUL UNTIL AFTER AN ENTRY ' S CORRESPONDING
* PARTITION HAS BEEN ACTIVATED .
*/
for ( partid = 1 ; partid < XP_MAX_PARTITIONS ; partid + + ) {
part = & xpc_partitions [ partid ] ;
DBUG_ON ( ( u64 ) part ! = L1_CACHE_ALIGN ( ( u64 ) part ) ) ;
part - > act_IRQ_rcvd = 0 ;
spin_lock_init ( & part - > act_lock ) ;
part - > act_state = XPC_P_INACTIVE ;
XPC_SET_REASON ( part , 0 , 0 ) ;
2005-09-01 23:01:37 +04:00
init_timer ( & part - > disengage_request_timer ) ;
part - > disengage_request_timer . function =
xpc_timeout_partition_disengage_request ;
part - > disengage_request_timer . data = ( unsigned long ) part ;
2005-03-24 05:50:00 +03:00
part - > setup_state = XPC_P_UNSET ;
init_waitqueue_head ( & part - > teardown_wq ) ;
atomic_set ( & part - > references , 0 ) ;
}
/*
* Open up protections for IPI operations ( and AMO operations on
* Shub 1.1 systems ) .
*/
xpc_allow_IPI_ops ( ) ;
/*
* Interrupts being processed will increment this atomic variable and
* awaken the heartbeat thread which will process the interrupts .
*/
atomic_set ( & xpc_act_IRQ_rcvd , 0 ) ;
/*
* This is safe to do before the xpc_hb_checker thread has started
* because the handler releases a wait queue . If an interrupt is
* received before the thread is waiting , it will not go to sleep ,
* but rather immediately process the interrupt .
*/
ret = request_irq ( SGI_XPC_ACTIVATE , xpc_act_IRQ_handler , 0 ,
" xpc hb " , NULL ) ;
if ( ret ! = 0 ) {
dev_err ( xpc_part , " can't register ACTIVATE IRQ handler, "
" errno=%d \n " , - ret ) ;
xpc_restrict_IPI_ops ( ) ;
if ( xpc_sysctl ) {
unregister_sysctl_table ( xpc_sysctl ) ;
}
2006-08-09 00:03:29 +04:00
kfree ( xpc_remote_copy_buffer_base ) ;
2005-03-24 05:50:00 +03:00
return - EBUSY ;
}
/*
* Fill the partition reserved page with the information needed by
* other partitions to discover we are alive and establish initial
* communications .
*/
xpc_rsvd_page = xpc_rsvd_page_init ( ) ;
if ( xpc_rsvd_page = = NULL ) {
dev_err ( xpc_part , " could not setup our reserved page \n " ) ;
free_irq ( SGI_XPC_ACTIVATE , NULL ) ;
xpc_restrict_IPI_ops ( ) ;
if ( xpc_sysctl ) {
unregister_sysctl_table ( xpc_sysctl ) ;
}
2006-08-09 00:03:29 +04:00
kfree ( xpc_remote_copy_buffer_base ) ;
2005-03-24 05:50:00 +03:00
return - EBUSY ;
}
2005-09-01 23:01:37 +04:00
/* add ourselves to the reboot_notifier_list */
ret = register_reboot_notifier ( & xpc_reboot_notifier ) ;
if ( ret ! = 0 ) {
dev_warn ( xpc_part , " can't register reboot notifier \n " ) ;
}
2007-05-08 11:27:03 +04:00
/* add ourselves to the die_notifier list */
2005-11-09 23:41:57 +03:00
ret = register_die_notifier ( & xpc_die_notifier ) ;
if ( ret ! = 0 ) {
dev_warn ( xpc_part , " can't register die notifier \n " ) ;
}
2005-03-24 05:50:00 +03:00
init_timer ( & xpc_hb_timer ) ;
xpc_hb_timer . function = xpc_hb_beater ;
/*
* The real work - horse behind xpc . This processes incoming
* interrupts and monitors remote heartbeats .
*/
pid = kernel_thread ( xpc_hb_checker , NULL , 0 ) ;
if ( pid < 0 ) {
dev_err ( xpc_part , " failed while forking hb check thread \n " ) ;
/* indicate to others that our reserved page is uninitialized */
xpc_rsvd_page - > vars_pa = 0 ;
2005-09-01 23:01:37 +04:00
/* take ourselves off of the reboot_notifier_list */
( void ) unregister_reboot_notifier ( & xpc_reboot_notifier ) ;
2005-11-09 23:41:57 +03:00
/* take ourselves off of the die_notifier list */
( void ) unregister_die_notifier ( & xpc_die_notifier ) ;
2005-03-24 05:50:00 +03:00
del_timer_sync ( & xpc_hb_timer ) ;
free_irq ( SGI_XPC_ACTIVATE , NULL ) ;
xpc_restrict_IPI_ops ( ) ;
if ( xpc_sysctl ) {
unregister_sysctl_table ( xpc_sysctl ) ;
}
2006-08-09 00:03:29 +04:00
kfree ( xpc_remote_copy_buffer_base ) ;
2005-03-24 05:50:00 +03:00
return - EBUSY ;
}
/*
* Startup a thread that will attempt to discover other partitions to
* activate based on info provided by SAL . This new thread is short
* lived and will exit once discovery is complete .
*/
pid = kernel_thread ( xpc_initiate_discovery , NULL , 0 ) ;
if ( pid < 0 ) {
dev_err ( xpc_part , " failed while forking discovery thread \n " ) ;
/* mark this new thread as a non-starter */
2006-01-17 20:52:21 +03:00
complete ( & xpc_discovery_exited ) ;
2005-03-24 05:50:00 +03:00
2005-09-01 23:01:37 +04:00
xpc_do_exit ( xpcUnloading ) ;
2005-03-24 05:50:00 +03:00
return - EBUSY ;
}
/* set the interface to point at XPC's functions */
xpc_set_interface ( xpc_initiate_connect , xpc_initiate_disconnect ,
xpc_initiate_allocate , xpc_initiate_send ,
xpc_initiate_send_notify , xpc_initiate_received ,
xpc_initiate_partid_to_nasids ) ;
return 0 ;
}
module_init ( xpc_init ) ;
void __exit
xpc_exit ( void )
{
2005-09-01 23:01:37 +04:00
xpc_do_exit ( xpcUnloading ) ;
2005-03-24 05:50:00 +03:00
}
module_exit ( xpc_exit ) ;
MODULE_AUTHOR ( " Silicon Graphics, Inc. " ) ;
MODULE_DESCRIPTION ( " Cross Partition Communication (XPC) support " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_param ( xpc_hb_interval , int , 0 ) ;
MODULE_PARM_DESC ( xpc_hb_interval , " Number of seconds between "
" heartbeat increments. " ) ;
module_param ( xpc_hb_check_interval , int , 0 ) ;
MODULE_PARM_DESC ( xpc_hb_check_interval , " Number of seconds between "
" heartbeat checks. " ) ;
2005-10-25 23:07:43 +04:00
module_param ( xpc_disengage_request_timelimit , int , 0 ) ;
MODULE_PARM_DESC ( xpc_disengage_request_timelimit , " Number of seconds to wait "
" for disengage request to complete. " ) ;
2006-01-10 20:08:00 +03:00
module_param ( xpc_kdebug_ignore , int , 0 ) ;
MODULE_PARM_DESC ( xpc_kdebug_ignore , " Should lack of heartbeat be ignored by "
" other partitions when dropping into kdebug. " ) ;