Index: linux/arch/ia64/sn/kernel/Makefile =================================================================== --- linux.orig/arch/ia64/sn/kernel/Makefile 2004-04-23 13:42:47.000000000 -0500 +++ linux/arch/ia64/sn/kernel/Makefile 2004-04-23 13:43:07.000000000 -0500 @@ -11,3 +11,5 @@ obj-$(CONFIG_IA64_GENERIC) += machvec.o obj-$(CONFIG_IA64_SGI_SN_XPC) += xp.o xp-y := xp_main.o xp_nofault.o +obj-$(CONFIG_IA64_SGI_SN_XPC) += xpc.o +xpc-y := xpc_main.o xpc_kdb.o xpc_channel.o xpc_partition.o Index: linux/arch/ia64/sn/kernel/xpc.h =================================================================== --- linux.orig/arch/ia64/sn/kernel/xpc.h 1969-12-31 18:00:00.000000000 -0600 +++ linux/arch/ia64/sn/kernel/xpc.h 2004-04-23 13:43:07.000000000 -0500 @@ -0,0 +1,1005 @@ +/* + * 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. + * + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + */ + + +/* + * Cross Partition Communication (XPC) structures and macros. + */ + +#ifndef _IA64_SN_KERNEL_XPC_H +#define _IA64_SN_KERNEL_XPC_H + +#define __XPC_MAIN__ + +#ifndef SN_PROM +#include +#include +#include +#include +#include +#include +#include +#include +#include "xpc_dbgtk.h" +#else /* ! SN_PROM */ +#include "libc/libc.h" +#include "chipset/bte.h" +#include "chipset/fetchop.h" +#include +#include +#include +#include "xp.h" +#endif /* ! SN_PROM */ +#include "xpc_stubs.h" + + +// >>> Where does the following two macros belong? +/* + * Define a macro that does exactly what wait_event_interruptible() does except + * that it adds the current task to the wait queue as an exclusive task (i.e., + * sets the WQ_FLAG_EXCLUSIVE flag) rather than as a non-exclusive task as + * wait_event_interruptible() does. (Linux doesn't seem to provide this option + * so I've written my own.) + */ +#define __wait_event_exclusive_interruptible(wq, condition, ret) \ +do { \ + wait_queue_t __wait; \ + init_waitqueue_entry(&__wait, current); \ + \ + add_wait_queue_exclusive(&wq, &__wait); \ + for (;;) { \ + set_current_state(TASK_INTERRUPTIBLE); \ + if (condition) \ + break; \ + if (!signal_pending(current)) { \ + schedule(); \ + continue; \ + } \ + ret = -ERESTARTSYS; \ + break; \ + } \ + current->state = TASK_RUNNING; \ + remove_wait_queue(&wq, &__wait); \ +} while (0) + +#define wait_event_exclusive_interruptible(wq, condition) \ +({ \ + int __ret = 0; \ + if (!(condition)) \ + __wait_event_exclusive_interruptible(wq, condition, __ret);\ + __ret; \ +}) + + +/* + * XPC Version numbers consist of a major and minor number. XPC can always + * talk to versions with same major #, and never talk to versions with a + * different major #. + */ +#define _XPC_VERSION(_maj, _min) (((_maj) << 4) | ((_min) & 0xf)) +#define XPC_VERSION_MAJOR(_v) ((_v) >> 4) +#define XPC_VERSION_MINOR(_v) ((_v) & 0xf) + + +/* + * The next macros define word or bit representations for given + * C-brick nasid in either the SAL provided bit array representing + * nasids in the partition/machine or the AMO_t array used for + * inter-partition initiation communications. + * + * For SN2 machines, C-Bricks are alway even numbered NASIDs. As + * such, some space will be saved by insisting that nasid information + * passed from SAL always be packed for C-Bricks and the + * cross-partition interrupts use the same packing scheme. + */ +#define XPC_NASID_W_INDEX(_n) (((_n) / 64) / 2) +#define XPC_NASID_B_INDEX(_n) (((_n) / 2) & (64 - 1)) +#define XPC_NASID_IN_ARRAY(_n, _p) ((_p)[XPC_NASID_W_INDEX(_n)] & \ + (1UL << XPC_NASID_B_INDEX(_n))) +#define XPC_NASID_FROM_W_B(_w, _b) (((_w) * 64 + (_b)) * 2) + +#define XPC_HB_DEFAULT_INTERVAL 5 /* incr HB every x secs */ +#define XPC_HB_CHECK_DEFAULT_TIMEOUT 20 /* check HB every x secs */ + +/* define the process name of HB checker and the CPU it is pinned to */ +#define XPC_HB_CHECK_THREAD_NAME "xpc_hb" +#define XPC_HB_CHECK_CPU 0 + +/* define the process name of the discovery thread */ +#define XPC_DISCOVERY_THREAD_NAME "xpc_discovery" + + +#define XPC_HB_ALLOWED(_p, _v) ((_v)->heartbeating_to_mask & (1UL << (_p))) +#define XPC_ALLOW_HB(_p, _v) (_v)->heartbeating_to_mask |= (1UL << (_p)) +#define XPC_DISALLOW_HB(_p, _v) (_v)->heartbeating_to_mask &= (~(1UL << (_p))) + + +/* + * Reserved Page provided by SAL. + * + * SAL provides one page per partition of reserved memory. When SAL + * initialization is complete, SAL_signature, SAL_version, partid, + * part_nasids, and mach_nasids are set. + * + * Note: Until vars_pa is set, the partition XPC code has not been initialized. + */ +typedef struct { + u64 SAL_signature; /* SAL unique signature */ + u64 SAL_version; /* SAL specified version */ + u8 partid; /* partition ID from SAL */ + u8 version; + u8 pad[6]; /* pad to u64 align */ + volatile u64 vars_pa; + u64 part_nasids[XP_NUM_NASID_WORDS] ____cacheline_aligned; + u64 mach_nasids[XP_NUM_NASID_WORDS] ____cacheline_aligned; +} xpc_rsvd_page_t; +#define XPC_RP_VERSION _XPC_VERSION(1,0) /* version 1.0 of the reserved page */ + +#define XPC_RSVD_PAGE_ALIGNED_SIZE (L1_CACHE_ALIGN(sizeof(xpc_rsvd_page_t))) + + +/* + * Define the structures by which XPC variables can be exported to other + * partitions. (There are two: xpc_vars_t and xpc_vars_part_t) + */ + +/* + * The following structure describes the partition generic variables + * needed by other partitions in order to properly initialize. + * + * xpc_vars_t version number also applies to xpc_vars_part_t. Changes to either + * structure and/or related functionality should be reflected by incrementing + * either the major or minor version numbers of xpc_vars_t. + */ +typedef struct { + u8 version; + partid_t partid; /* REMOVE when lbs2.3 disappears */ + u64 heartbeat; + u64 heartbeating_to_mask; + u64 kdb_status; /* 0 = machine running */ + u64 vars_part_pa; + u64 act_cpuid; + u64 amos_page_pa; /* paddr of page of AMOs from fetchop driver */ + AMO_t *amos_page; /* vaddr of page of AMOs from fetchop driver */ + AMO_t *act_amos; /* pointer to the first activation AMO */ +} xpc_vars_t; +#define XPC_V_VERSION _XPC_VERSION(2,0) /* version 2.0 of the cross vars */ + +#define XPC_VARS_ALIGNED_SIZE (L1_CACHE_ALIGN(sizeof(xpc_vars_t))) + +/* + * The following structure describes the per partition specific variables. + * + * An array of these structures, one per partition, will be defined. As a + * partition becomes active XPC will copy the array entry corresponding to + * itself from that partition. It is desirable that the size of this + * structure evenly divide into a cacheline, such that none of the entries + * in this array crosses a cacheline boundary. As it is now, each entry + * occupies half a cacheline. + */ +typedef struct { + volatile u64 magic; + + u64 openclose_args_pa; /* physical address of open and close args */ + u64 GPs_pa; /* physical address of Get/Put values */ + + u64 IPI_amo_pa; /* physical address of IPI AMO_t structure */ + u32 IPI_cpuid; /* physical CPU ID of where to send IPIs */ + + u8 nchannels; /* #of defined channels supported */ + + u8 reserved[27]; /* pad to a full 64 bytes */ +} xpc_vars_part_t; + +/* + * The vars_part MAGIC numbers play a part in the first contact protocol. + * + * MAGIC1 indicates that the per partition specific variables for a remote + * partition have been initialized by this partition. + * + * MAGIC2 indicates that this partition has pulled the remote partititions + * per partition variables that pertain to this partition. + */ +#define XPC_VP_MAGIC1 0x0053524156435058L /* 'XPCVARS\0'L (little endian) */ +#define XPC_VP_MAGIC2 0x0073726176435058L /* 'XPCvars\0'L (little endian) */ + + + +/* + * Functions registered by add_timer() or called by kernel_thread() only + * allow for a single 64-bit argument. The following macros can be used to + * pack and unpack two (32-bit, 16-bit or 8-bit) arguments into or out from + * the passed argument. + */ +typedef u64 xpc_args_t; + +#define XPC_PACK_ARGS(_arg1, _arg2) \ + ((((xpc_args_t) _arg1) & 0xffffffff) | \ + ((((xpc_args_t) _arg2) & 0xffffffff) << 32)) + +#define XPC_UNPACK_ARG1(_args) (((xpc_args_t) _args) & 0xffffffff) +#define XPC_UNPACK_ARG2(_args) ((((xpc_args_t) _args) >> 32) & 0xffffffff) + + + +/* + * Define a Get/Put value pair (pointers) used with a message queue. + */ +typedef struct { + volatile s64 get; /* Get value */ + volatile s64 put; /* Put value */ +} xpc_gp_t; + +#define XPC_GP_SIZE \ + L1_CACHE_ALIGN(sizeof(xpc_gp_t) * XPC_NCHANNELS) + + + +/* + * Define a structure that contains arguments associated with opening and + * closing a channel. + */ +typedef struct { + u16 reason; /* reason why channel is closing */ + u16 msg_size; /* sizeof each message entry */ + u16 remote_nentries; /* #of message entries in remote msg queue */ + u16 local_nentries; /* #of message entries in local msg queue */ + u64 local_msgqueue_pa; /* physical address of local message queue */ +} xpc_openclose_args_t; + +#define XPC_OPENCLOSE_ARGS_SIZE \ + L1_CACHE_ALIGN(sizeof(xpc_openclose_args_t) * XPC_NCHANNELS) + + + +/* xpc_msg_t flags */ + +#define XPC_M_DONE 0x01 /* msg has been received/consumed */ +#define XPC_M_READY 0x02 /* msg is ready to be sent */ +#define XPC_M_INTERRUPT 0x04 /* send interrupt when msg consumed */ + + +#define XPC_MSG_ADDRESS(_payload) \ + ((xpc_msg_t *)((u8 *)(_payload) - XPC_MSG_PAYLOAD_OFFSET)) + + + +/* + * Defines notify entry. + * + * This is used to notify a message's sender that their message was received + * and consumed by the intended recipient. + */ +typedef struct { + struct semaphore sema; /* notify semaphore */ + volatile u8 type; /* type of notification */ + + /* the following two fields are only used if type == XPC_N_CALL */ + xpc_notify_func_t func; /* user's notify function */ + void *key; /* pointer to user's key */ +} xpc_notify_t; + +/* xpc_notify_t type of notification */ + +#define XPC_N_CALL 0x01 /* notify function provided by user */ + + + +/* + * Define the structure that manages all the stuff required by a channel. In + * particular, they are used to manage the messages sent across the channel. + * + * This structure is private to a partition, and is NOT shared across the + * partition boundary. + * + * There is an array of these structures for each remote partition. It is + * allocated at the time a partition becomes active. The array contains one + * of these structures for each potential channel connection to that partition. + * + * Each of these structures manages two message queues (circular buffers). + * They are allocated at the time a channel connection is made. One of + * these message queues (local_msgqueue) holds the locally created messages + * that are destined for the remote partition. The other of these message + * queues (remote_msgqueue) is a locally cached copy of the remote partition's + * own local_msgqueue. + * + * The following is a description of the Get/Put pointers used to manage these + * two message queues. Consider the local_msgqueue to be on one partition + * and the remote_msgqueue to be its cached copy on another partition. A + * description of what each of the lettered areas contains is included. + * + * + * local_msgqueue remote_msgqueue + * + * |/////////| |/////////| + * w_remote_GP.get --> +---------+ |/////////| + * | F | |/////////| + * remote_GP.get --> +---------+ +---------+ <-- local_GP->get + * | | | | + * | | | E | + * | | | | + * | | +---------+ <-- w_local_GP.get + * | B | |/////////| + * | | |////D////| + * | | |/////////| + * | | +---------+ <-- w_remote_GP.put + * | | |////C////| + * local_GP->put --> +---------+ +---------+ <-- remote_GP.put + * | | |/////////| + * | A | |/////////| + * | | |/////////| + * w_local_GP.put --> +---------+ |/////////| + * |/////////| |/////////| + * + * + * ( remote_GP.[get|put] are cached copies of the remote + * partition's local_GP->[get|put], and thus their values can + * lag behind their counterparts on the remote partition. ) + * + * + * A - Messages that have been allocated, but have not yet been sent to the + * remote partition. + * + * B - Messages that have been sent, but have not yet been acknowledged by the + * remote partition as having been received. + * + * C - Area that needs to be prepared for the copying of sent messages, by + * the clearing of the message flags of any previously received messages. + * + * D - Area into which sent messages are to be copied from the remote + * partition's local_msgqueue and then delivered to their intended + * recipients. [ To allow for a multi-message copy, another pointer + * (next_msg_to_pull) has been added to keep track of the next message + * number needing to be copied (pulled). It chases after w_remote_GP.put. + * Any messages lying between w_local_GP.get and next_msg_to_pull have + * been copied and are ready to be delivered. ] + * + * E - Messages that have been copied and delivered, but have not yet been + * acknowledged by the recipient as having been received. + * + * F - Messages that have been acknowledged, but XPC has not yet notified the + * sender that the message was received by its intended recipient. + * This is also an area that needs to be prepared for the allocating of + * new messages, by the clearing of the message flags of the acknowledged + * messages. + */ +typedef struct xpc_channel_s { + partid_t partid; /* ID of remote partition connected */ + spinlock_t lock; /* lock for updating this structure */ + volatile u32 flags; /* general flags */ + + xpc_t reason; /* reason why channel is disconnect'g */ + int reason_line; /* line# disconnect initiated from */ + + u16 number; /* channel # */ + + u16 msg_size; /* sizeof each msg entry */ + u16 local_nentries; /* #of msg entries in local msg queue */ + u16 remote_nentries; /* #of msg entries in remote msg queue*/ + + xpc_msg_t *local_msgqueue; /* local message queue */ + xpc_msg_t *remote_msgqueue; /* cached copy of remote partition's */ + /* local message queue */ + u64 remote_msgqueue_pa; /* phys addr of remote partition's */ + /* local message queue */ + + atomic_t references; /* #of external references to queues */ + + atomic_t n_on_msg_allocate_wq; /* #on msg allocation wait queue */ + wait_queue_head_t msg_allocate_wq; /* msg allocation wait queue */ + + /* queue of msg senders who want to be notified when msg received */ + + atomic_t n_to_notify; /* #of msg senders to notify */ + xpc_notify_t *notify_queue; /* notify queue for messages sent */ + + xpc_channel_func_t func; /* user's channel function */ + void *key; /* pointer to user's key */ + + struct semaphore msg_to_pull_sema; /* next msg to pull serialization */ + struct semaphore teardown_sema; /* wait for teardown completion */ + + xpc_openclose_args_t *local_openclose_args; /* args passed on opening */ + /* or closing of channel */ + + /* various flavors of local and remote Get/Put values */ + + xpc_gp_t *local_GP; /* local Get/Put values */ + xpc_gp_t remote_GP; /* remote Get/Put values */ + xpc_gp_t w_local_GP; /* working local Get/Put values */ + xpc_gp_t w_remote_GP; /* working remote Get/Put values */ + s64 next_msg_to_pull; /* Put value of next msg to pull */ + + /* kthread management related fields */ + +// >>> rethink having kthreads_assigned_limit and kthreads_idle_limit; perhaps +// >>> allow the assigned limit be unbounded and let the idle limit be dynamic +// >>> dependent on activity over the last interval of time + atomic_t kthreads_assigned; /* #of kthreads assigned to channel */ + u32 kthreads_assigned_limit; /* limit on #of kthreads assigned */ + atomic_t kthreads_idle; /* #of kthreads idle waiting for work */ + u32 kthreads_idle_limit; /* limit on #of kthreads idle */ + atomic_t kthreads_active; /* #of kthreads actively working */ + // >>> following field is temporary + u32 kthreads_created; /* total #of kthreads created */ + + wait_queue_head_t idle_wq; /* idle kthread wait queue */ + +} ____cacheline_aligned xpc_channel_t; + + +/* xpc_channel_t flags */ + +#define XPC_C_WASCONNECTED 0x00000001 /* channel was connected */ + +#define XPC_C_ROPENREPLY 0x00000002 /* remote open channel reply */ +#define XPC_C_OPENREPLY 0x00000004 /* local open channel reply */ +#define XPC_C_ROPENREQUEST 0x00000008 /* remote open channel request */ +#define XPC_C_OPENREQUEST 0x00000010 /* local open channel request */ + +#define XPC_C_SETUP 0x00000020 /* channel's msgqueues are alloc'd */ +#define XPC_C_CONNECTCALLOUT 0x00000040 /* channel connected callout made */ +#define XPC_C_CONNECTED 0x00000080 /* local channel is connected */ +#define XPC_C_CONNECTING 0x00000100 /* channel is being connected */ + +#define XPC_C_RCLOSEREPLY 0x00000200 /* remote close channel reply */ +#define XPC_C_CLOSEREPLY 0x00000400 /* local close channel reply */ +#define XPC_C_RCLOSEREQUEST 0x00000800 /* remote close channel request */ +#define XPC_C_CLOSEREQUEST 0x00001000 /* local close channel request */ + +#define XPC_C_DISCONNECTED 0x00002000 /* channel is disconnected */ +#define XPC_C_DISCONNECTING 0x00004000 /* channel is being disconnected */ + + +/* + * These two macros are used to keep us from tearing down a channel's msg + * queues while a thread may be referencing them. + */ +#define XPC_MSGQUEUE_REF(_ch) atomic_inc(&(_ch)->references) +#define XPC_MSGQUEUE_DEREF(_ch) \ + { \ + xpc_partition_t *_p; \ + s32 _refs; \ + _refs = atomic_dec_return(&(_ch)->references); \ + XP_ASSERT(_refs >= 0); \ + if (_refs == 0) { \ + _p = &xpc_partitions[(_ch)->partid]; \ + xpc_wakeup_channel_mgr(_p); \ + } \ + } + + +#define XPC_CALL_CHANNEL_FUNC(_ch, _reason, _data) \ + { \ + if ((_ch)->func != NULL) { \ + (_ch)->func(_reason, (_ch)->partid, \ + (_ch)->number, _data, (_ch)->key); \ + } \ + } + + +#define XPC_CALL_NOTIFY_FUNC(_ch, _n, _reason) \ + { \ + if ((_n)->func != NULL) { \ + (_n)->func(_reason, (_ch)->partid, \ + (_ch)->number, (_n)->key); \ + } \ + } + + +/* clear some of the msg flags in the local message queue */ +#define XPC_CLEAR_LOCAL_MSGQUEUE_FLAGS(_ch) \ + { \ + xpc_msg_t *_m; \ + s64 _g; \ + _g = (_ch)->w_remote_GP.get; \ + do { \ + _m = (xpc_msg_t *) ((u64) (_ch)->local_msgqueue \ + + (_g % (_ch)->local_nentries) \ + * (_ch)->msg_size); \ + _m->flags = 0; \ + } while (++_g < (_ch)->remote_GP.get); \ + } + +/* clear some of the msg flags in the remote message queue */ +#define XPC_CLEAR_REMOTE_MSGQUEUE_FLAGS(_ch) \ + { \ + xpc_msg_t *_m; \ + s64 _p; \ + _p = (_ch)->w_remote_GP.put; \ + do { \ + _m = (xpc_msg_t *) ((u64) (_ch)->remote_msgqueue\ + + (_p % (_ch)->remote_nentries) \ + * (_ch)->msg_size); \ + _m->flags = 0; \ + } while (++_p < (_ch)->remote_GP.put); \ + } + + + +#define XPC_DISCONNECT_CHANNEL(_ch, _reason, _irqflgs) \ + xpc_disconnect_channel(__LINE__, _ch, _reason, _irqflgs) + + + +/* + * Manages channels on a partition basis. There is one of these structures + * for each partition (a partition will never utilize the structure that + * represents itself). + */ +typedef struct xpc_partition_s { + + /* XPC HB infrastructure */ + + u64 remote_rp_pa; /* phys addr of partition's rsvd pg */ + u64 remote_vars_pa; /* phys addr of partition's vars */ + u64 remote_vars_part_pa; /* phys addr of partition's vars part */ + u64 last_heartbeat; /* HB at last read */ + u64 remote_amos_page_pa; /* phys addr of partition's amos page */ + u32 remote_act_cpuid; /* active part's act/deact cpuid */ + u32 act_IRQ_rcvd; /* IRQs since activation */ + int act_cpu; /* CPU act thread pinned to */ + spinlock_t act_lock; /* protect updating of act_state */ + u8 act_state; /* from XPC HB viewpoint */ + xpc_t reason; /* reason partition is deactivating */ + int reason_line; /* line# deactivation initiated from */ + nasid_t reactivate_nasid; /* nasid in partition to reactivate */ + + + /* XPC infrastructure referencing and teardown control */ + + // >>> does setup_state need to be volatile? + volatile u8 setup_state; /* infrastructure setup state */ + wait_queue_head_t teardown_wq; /* kthread waiting to teardown infra */ + atomic_t references; /* #of references to infrastructure */ + + + /* + * NONE OF THE PRECEDING FIELDS OF THIS STRUCTURE WILL BE CLEARED WHEN + * XPC SETS UP THE NECESSARY INFRASTRUCTURE TO SUPPORT CROSS PARTITION + * COMMUNICATION. ALL OF THE FOLLOWING FIELDS WILL BE CLEARED. + */ + + + u8 nchannels; /* #of defined channels supported */ + atomic_t nchannels_active; /* #of channels that are not DISCONNECTED */ + xpc_channel_t *channels; /* array of channel structures */ + + xpc_gp_t *local_GPs; /* local Get/Put values */ + xpc_gp_t *remote_GPs; /* copy of remote partition's local Get/Put */ + /* values */ + u64 remote_GPs_pa; /* phys address of remote partition's local */ + /* Get/Put values */ + + + /* fields used to pass args when opening or closing a channel */ + + xpc_openclose_args_t *local_openclose_args; /* local's args */ + xpc_openclose_args_t *remote_openclose_args; /* copy of remote's args */ + u64 remote_openclose_args_pa; /* phys addr of remote's args */ + + + /* IPI sending, receiving and handling related fields */ + + u32 remote_IPI_cpuid; /* phys CPU ID of where to send IPIs */ + AMO_t *remote_IPI_amo_va; /* address of remote IPI AMO_t structure */ + + AMO_t *local_IPI_amo_va; /* address of IPI AMO_t structure */ + volatile u64 local_IPI_amo; /* IPI amo flags yet to be handled */ + char IPI_owner[8]; /* IPI owner's name */ + struct timer_list dropped_IPI_timer; /* dropped IPI timer */ + + spinlock_t IPI_lock; /* IPI handler lock */ + + + /* channel manager related fields */ + + atomic_t channel_mgr_requests; /* #of requests to activate chan mgr */ + wait_queue_head_t channel_mgr_wq; /* channel mgr's wait queue */ + +} ____cacheline_aligned xpc_partition_t; + + +/* xpc_partition_t act_state values (for XPC HB) */ + +#define XPC_P_INACTIVE 0x00 /* partition is not active */ +#define XPC_P_ACTIVATION_REQ 0x01 /* created thread to activate */ +#define XPC_P_ACTIVATING 0x02 /* activation thread started */ +#define XPC_P_ACTIVE 0x03 /* xpc_partition_up() was called */ +#define XPC_P_DEACTIVATING 0x04 /* partition deactivation initiated */ + + +#define XPC_DEACTIVATE_PARTITION(_p, _reason) \ + xpc_deactivate_partition(__LINE__, (_p), (_reason)) + + +/* xpc_partition_t setup_state values */ + +#define XPC_P_UNSET 0x00 /* infrastructure was never setup */ +#define XPC_P_SETUP 0x01 /* infrastructure is setup */ +#define XPC_P_WTEARDOWN 0x02 /* waiting to teardown infrastructure */ +#define XPC_P_TORNDOWN 0x03 /* infrastructure is torndown */ + + +/* + * xpc_partition_t IPI_timer #of seconds to wait before checking for + * dropped IPIs. These occur whenever an IPI amo write doesn't complete until + * after the IPI was received. + */ +#define XPC_P_DROPPED_IPI_WAIT (0.25 * XPC_TICKS_PER_SEC) + + +#define XPC_PARTID(_p) ((partid_t) ((_p) - &xpc_partitions[0])) + + +/* + * These two macros are used to keep us from tearing down a partition's + * setup infrastructure while a thread may be referencing it. + */ +#define XPC_PART_DEREF(_p) \ + { \ + s32 refs; \ + refs = atomic_dec_return(&(_p)->references); \ + XP_ASSERT(refs >= 0); \ + if (refs == 0 && (_p)->setup_state == XPC_P_WTEARDOWN) {\ + wake_up(&(_p)->teardown_wq); \ + } \ + } +#define XPC_PART_REF(_p) \ + ({ \ + u8 _setup; \ + atomic_inc(&(_p)->references); \ + _setup = ((_p)->setup_state == XPC_P_SETUP); \ + if (!_setup) { \ + XPC_PART_DEREF(_p); \ + } \ + _setup; \ + }) + + + +/* + * The following macro is to be used for the setting of the reason and + * reason_line fields in both the xpc_channel_t and xpc_partition_t structures. + */ +#define XPC_SET_REASON(_p, _reason, _line) \ + { \ + (_p)->reason = _reason; \ + (_p)->reason_line = _line; \ + } + + + +/* + * The following set of macros and inlines are used for the sending and + * receiving of IPIs (also known as IRQs). There are two flavors of IPIs, + * one that is associated with partition activity (SGI_XPC_ACTIVATE) and + * the other that is associated with channel activity (SGI_XPC_NOTIFY). + */ + +static __inline__ u64 +xpc_IPI_receive(AMO_t *amo) +{ + return FETCHOP_LOAD_OP(TO_MSPEC((u64) &amo->variable), FETCHOP_CLEAR); +} + + +static __inline__ xpc_t +xpc_IPI_send(AMO_t *amo, u64 flag, long phys_cpuid, int vector) +{ + int ret = 0; + unsigned long irq_flags; + + local_irq_save(irq_flags); + + FETCHOP_STORE_OP(TO_MSPEC((u64) &amo->variable), FETCHOP_OR, flag); + sn_send_IPI_phys(phys_cpuid, vector, 0); + + /* + * We must always use the nofault function regardless of whether we + * are on a Shub 1.1 system or a Shub 1.2 slice 0xc processor. If we + * didn't, we'd never know that the other partition is down and would + * keep sending IPIs and AMOs to it until the heartbeat times out. + * There is no special reason for SH_IPI_ACCESS being the target of + * the PIO read. + */ + ret = xp_nofault_PIOR((u64 *) GLOBAL_MMR_ADDR(NASID_GET(&amo->variable), + SH_IPI_ACCESS)); + + local_irq_restore(irq_flags); + + return ((ret == 0) ? xpcSuccess : xpcPioReadError); +} + + +/* + * IPIs associated with SGI_XPC_ACTIVATE IRQ. + */ + +/* + * Flag the appropriate AMO variable and send an IPI to the specified node. + */ +#define XPC_ACTIVATE_IRQ_SEND(_a, _c, _n) \ + { \ + int _w_index = XPC_NASID_W_INDEX(_n); \ + int _b_index = XPC_NASID_B_INDEX(_n); \ + AMO_t *_amos = (AMO_t *) __va((u64) (_a) + \ + (MAX_PARTITIONS * sizeof(AMO_t))); \ + (void) xpc_IPI_send(&_amos[_w_index], (1UL << _b_index),\ + _c, SGI_XPC_ACTIVATE); \ + } + +#define XPC_IPI_SEND_ACTIVATE(_v) \ + XPC_ACTIVATE_IRQ_SEND((_v)->amos_page_pa, \ + (_v)->act_cpuid, \ + cnodeid_to_nasid(0)) + +#define XPC_IPI_SEND_ACTIVATED(_p) \ + XPC_ACTIVATE_IRQ_SEND((_p)->remote_amos_page_pa, \ + (_p)->remote_act_cpuid, \ + cnodeid_to_nasid(0)) + +#define XPC_IPI_SEND_REACTIVATE(_p) \ + XPC_ACTIVATE_IRQ_SEND(xpc_vars->amos_page_pa, \ + xpc_vars->act_cpuid, \ + (_p)->reactivate_nasid) + + +/* + * IPIs associated with SGI_XPC_NOTIFY IRQ. + */ + +/* + * Send an IPI to the remote partition that is associated with the + * specified channel. + */ +#define XPC_NOTIFY_IRQ_SEND(_p, _ch, _f) \ + ({ \ + xpc_t _ret = xpcSuccess; \ + if ((_p)->act_state != XPC_P_DEACTIVATING) { \ + _ret = xpc_IPI_send((_p)->remote_IPI_amo_va, \ + (u64) (_f) << ((_ch)->number * 8), \ + (_p)->remote_IPI_cpuid, \ + SGI_XPC_NOTIFY); \ + DPRINTK(xpc_chan, XPC_DBG_C_IPI, \ + #_f " sent to partid=%d, channel=%d, " \ + "ret=%d\n", (_ch)->partid, \ + (_ch)->number, _ret); \ + } \ + _ret; \ + }) + + +/* + * Make it look like the remote partition, which is associated with the + * specified channel, sent us an IPI. This faked IPI will be handled + * by xpc_dropped_IPI_check(). + */ +#define XPC_NOTIFY_IRQ_SEND_LOCAL(_ch, _f) \ + { \ + xpc_partition_t *_p = &xpc_partitions[(_ch)->partid]; \ + FETCHOP_STORE_OP(TO_MSPEC((u64) &_p->local_IPI_amo_va-> \ + variable), FETCHOP_OR, \ + ((u64) (_f) << ((_ch)->number * 8))); \ + DPRINTK(xpc_chan, XPC_DBG_C_IPI, \ + #_f " sent local from partid=%d, channel=%d\n", \ + (_ch)->partid, (_ch)->number); \ + } + + +/* + * The sending and receiving of IPIs includes the setting of an AMO variable + * to indicate the reason the IPI was sent. The 64-bit variable is divided + * up into eight bytes, ordered from right to left. Byte zero pertains to + * channel 0, byte one to channel 1, and so on. Each byte is described by + * the following IPI flags. + */ + +#define XPC_IPI_CLOSEREQUEST 0x01 +#define XPC_IPI_CLOSEREPLY 0x02 +#define XPC_IPI_OPENREQUEST 0x04 +#define XPC_IPI_OPENREPLY 0x08 +#define XPC_IPI_MSGREQUEST 0x10 + + +/* given an AMO variable and a channel#, get its associated IPI flags */ +#define XPC_GET_IPI_FLAGS(_amo, _c) ((u8) (((_amo) >> ((_c) * 8)) & 0xff)) + +#define XPC_ANY_OPENCLOSE_IPI_FLAGS_SET(_amo) ((_amo) & 0x0f0f0f0f0f0f0f0f) +#define XPC_ANY_MSG_IPI_FLAGS_SET(_amo) ((_amo) & 0x1010101010101010) + + +#define XPC_IPI_SEND_CLOSEREQUEST(_ch, _irqflgs) \ + { \ + xpc_partition_t *_p = &xpc_partitions[(_ch)->partid]; \ + xpc_openclose_args_t *_a = (_ch)->local_openclose_args; \ + xpc_t _ret; \ + _a->reason = (_ch)->reason; \ + _ret = XPC_NOTIFY_IRQ_SEND(_p, _ch, \ + XPC_IPI_CLOSEREQUEST); \ + if (_ret != xpcSuccess) { \ + spin_unlock_irqrestore(&(_ch)->lock, \ + *(_irqflgs)); \ + XPC_DEACTIVATE_PARTITION(_p, _ret); \ + spin_lock_irqsave(&(_ch)->lock, *(_irqflgs)); \ + } \ + } + +#define XPC_IPI_SEND_CLOSEREPLY(_ch, _irqflgs) \ + { \ + xpc_partition_t *_p = &xpc_partitions[(_ch)->partid]; \ + xpc_t _ret; \ + _ret = XPC_NOTIFY_IRQ_SEND(_p, _ch, XPC_IPI_CLOSEREPLY);\ + if (_ret != xpcSuccess) { \ + spin_unlock_irqrestore(&(_ch)->lock, \ + *(_irqflgs)); \ + XPC_DEACTIVATE_PARTITION(_p, _ret); \ + spin_lock_irqsave(&(_ch)->lock, *(_irqflgs)); \ + } \ + } + +#define XPC_IPI_SEND_OPENREQUEST(_ch, _irqflgs) \ + { \ + xpc_partition_t *_p = &xpc_partitions[(_ch)->partid]; \ + xpc_openclose_args_t *_a = (_ch)->local_openclose_args; \ + xpc_t _ret; \ + _a->msg_size = (_ch)->msg_size; \ + _a->local_nentries = (_ch)->local_nentries; \ + _ret = XPC_NOTIFY_IRQ_SEND(_p, _ch, \ + XPC_IPI_OPENREQUEST); \ + if (_ret != xpcSuccess) { \ + spin_unlock_irqrestore(&(_ch)->lock, \ + *(_irqflgs)); \ + XPC_DEACTIVATE_PARTITION(_p, _ret); \ + spin_lock_irqsave(&(_ch)->lock, *(_irqflgs)); \ + } \ + } + +#define XPC_IPI_SEND_OPENREPLY(_ch, _irqflgs) \ + { \ + xpc_partition_t *_p = &xpc_partitions[(_ch)->partid]; \ + xpc_openclose_args_t *_a = (_ch)->local_openclose_args; \ + xpc_t _ret; \ + _a->remote_nentries = (_ch)->remote_nentries; \ + _a->local_nentries = (_ch)->local_nentries; \ + _a->local_msgqueue_pa = __pa((_ch)->local_msgqueue); \ + _ret = XPC_NOTIFY_IRQ_SEND(_p, _ch, XPC_IPI_OPENREPLY); \ + if (_ret != xpcSuccess) { \ + spin_unlock_irqrestore(&(_ch)->lock, \ + *(_irqflgs)); \ + XPC_DEACTIVATE_PARTITION(_p, _ret); \ + spin_lock_irqsave(&(_ch)->lock, *(_irqflgs)); \ + } \ + } + +#define XPC_IPI_SEND_MSGREQUEST(_ch) \ + { \ + xpc_partition_t *_p = &xpc_partitions[(_ch)->partid]; \ + xpc_t _ret; \ + _ret = XPC_NOTIFY_IRQ_SEND(_p, _ch, XPC_IPI_MSGREQUEST);\ + if (_ret != xpcSuccess) { \ + XPC_DEACTIVATE_PARTITION(_p, _ret); \ + } \ + } + +#define XPC_IPI_SEND_LOCAL_MSGREQUEST(_ch) \ + { \ + XPC_NOTIFY_IRQ_SEND_LOCAL(_ch, XPC_IPI_MSGREQUEST); \ + } + + +/* + * Memory for XPC's AMO variables is allocated by the fetchop driver. These + * pages are located in the lowest granule. The lowest granule uses 4k pages + * for cached references and an alternate TLB handler to never provide a + * cacheable mapping for the entire region. This will prevent speculative + * reading of cached copies of our lines from being issued which will cause + * a PI FSB Protocol error to be generated by the SHUB. For XPC, we need 64 + * (MAX_PARTITIONS) AMO variables for message notification (xpc_main.c) and + * an additional 16 AMO variables for partition activation (xpc_hb.c). + */ +static __inline__ AMO_t * +xpc_IPI_init(partid_t partid) +{ + extern xpc_vars_t *xpc_vars; + AMO_t *part_amo = xpc_vars->amos_page + partid; + + + xpc_IPI_receive(part_amo); + return part_amo; +} + + + +static __inline__ void +xpc_wakeup_channel_mgr(xpc_partition_t *part) +{ + if (atomic_inc_return(&part->channel_mgr_requests) == 1) { + wake_up(&part->channel_mgr_wq); + } +} + + + +static __inline__ xpc_t +xpc_map_bte_errors(bte_result_t error) +{ + switch (error) { + case BTE_SUCCESS: return xpcSuccess; + case BTEFAIL_DIR: return xpcBteDirectoryError; + case BTEFAIL_POISON: return xpcBtePoisonError; + case BTEFAIL_WERR: return xpcBteWriteError; + case BTEFAIL_ACCESS: return xpcBteAccessError; + case BTEFAIL_PWERR: return xpcBtePWriteError; + case BTEFAIL_PRERR: return xpcBtePReadError; + case BTEFAIL_TOUT: return xpcBteTimeOutError; + case BTEFAIL_XTERR: return xpcBteXtalkError; + case BTEFAIL_NOTAVAIL: return xpcBteNotAvailable; + default: return xpcBteUnmappedError; + } +} + + + +/* found in xp.c */ +extern xpc_registration_t xpc_registrations[]; + + +/* >>> found in xpc.c only */ +extern irqreturn_t xpc_notify_IRQ_handler(int, void *, struct pt_regs *); +extern void xpc_dropped_IPI_check(xpc_partition_t *); +extern void xpc_activate_kthreads(xpc_channel_t *, int); +extern void xpc_create_kthreads(xpc_channel_t *, int); +extern void xpc_disconnect_wait(int); + + +/* found in xpc.c and efi-xpc.c */ +extern void xpc_activate_partition(xpc_partition_t *); + + +/* found in xpc_partition.c */ +volatile extern int xpc_exiting; +extern int xpc_hb_interval; +extern int xpc_hb_check_interval; +extern xpc_vars_t *xpc_vars; +volatile extern xpc_rsvd_page_t *xpc_rsvd_page; +extern xpc_vars_part_t *xpc_vars_part; +extern xpc_partition_t xpc_partitions[MAX_PARTITIONS + 1]; +extern char xpc_remote_copy_buffer[]; +extern xpc_rsvd_page_t *xpc_rsvd_page_init(void); +extern void xpc_allow_IPI_ops(void); +extern void xpc_restrict_IPI_ops(void); +extern int xpc_identify_act_IRQ_sender(void); +extern xpc_t xpc_mark_partition_active(xpc_partition_t *); +extern void xpc_mark_partition_inactive(xpc_partition_t *); +extern void xpc_discovery(void); +extern void xpc_check_remote_hb(void); +extern void xpc_deactivate_partition(const int, xpc_partition_t *, xpc_t); + + +/* found in xpc_channel.c */ +extern xpc_t xpc_setup_infrastructure(xpc_partition_t *); +extern xpc_t xpc_pull_remote_vars_part(xpc_partition_t *); +extern __inline__ void xpc_check_for_channel_activity(xpc_partition_t *); +extern void xpc_process_channel_activity(xpc_partition_t *); +extern void xpc_connected_callout(xpc_channel_t *); +extern void xpc_deliver_msg(xpc_channel_t *); +extern void xpc_disconnect_channel(const int, xpc_channel_t *, xpc_t, + unsigned long *); +extern void xpc_disconnected_callout(xpc_channel_t *); +extern void xpc_partition_down(xpc_partition_t *, xpc_t); +extern void xpc_teardown_infrastructure(xpc_partition_t *); + + +/* found in xpc_kdb.c */ +extern void xpc_kdb_register(void); +extern void xpc_kdb_unregister(void); + + +#endif /* _IA64_SN_KERNEL_XPC_H */ + Index: linux/arch/ia64/sn/kernel/xpc_channel.c =================================================================== --- linux.orig/arch/ia64/sn/kernel/xpc_channel.c 1969-12-31 18:00:00.000000000 -0600 +++ linux/arch/ia64/sn/kernel/xpc_channel.c 2004-04-23 13:43:07.000000000 -0500 @@ -0,0 +1,2330 @@ +/* + * 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. + * + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + */ + + +/* + * Cross Partition Communication (XPC) channel support. + * + * This is the part of XPC that manages the channels and + * sends/receives messages across them to/from other partitions. + * + */ + + +#ifndef SN_PROM +#include +#include +#include +#include +#include +#include +#include +#else /* ! SN_PROM */ +#include "main/main.h" +#include "libc/libc.h" +#endif /* ! SN_PROM */ +#include "xpc.h" + + +/* + * Set up the initial values for the XPartition Communication channels. + */ +static void +xpc_initialize_channels(xpc_partition_t *part, partid_t partid) +{ + int ch_number; + xpc_channel_t *ch; + + + for (ch_number = 0; ch_number < part->nchannels; ch_number++) { + ch = &part->channels[ch_number]; + + ch->partid = partid; + ch->number = ch_number; + ch->flags = XPC_C_DISCONNECTED; + + ch->local_GP = &part->local_GPs[ch_number]; + ch->local_openclose_args = + &part->local_openclose_args[ch_number]; + + atomic_set(&ch->kthreads_assigned, 0); + atomic_set(&ch->kthreads_idle, 0); + atomic_set(&ch->kthreads_active, 0); + + atomic_set(&ch->references, 0); + atomic_set(&ch->n_to_notify, 0); + + spin_lock_init(&ch->lock); + sema_init(&ch->msg_to_pull_sema, 1); /* mutex */ + + atomic_set(&ch->n_on_msg_allocate_wq, 0); + init_waitqueue_head(&ch->msg_allocate_wq); + init_waitqueue_head(&ch->idle_wq); + } +} + + +/* + * Setup the infrastructure necessary to support XPartition Communication + * between the specified remote partition and the local one. + */ +xpc_t +xpc_setup_infrastructure(xpc_partition_t *part) +{ + int ret; + partid_t partid = XPC_PARTID(part); + + + /* + * Zero out MOST of the entry for this partition. Only the fields + * following `references' will be zeroed. The others must remain + * `viable' across partition ups and downs, since they may be + * referenced during this memset() operation. + */ + memset((u8 *)&part->references + sizeof(part->references), 0, + (u64)(part + 1) - (u64)((u8 *)&part->references + + sizeof(part->references))); + + /* + * Allocate all of the channel structures as a contiguous chunk of + * memory. + */ + part->channels = kmalloc(sizeof(xpc_channel_t) * XPC_NCHANNELS, + GFP_KERNEL); + if (part->channels == NULL) { + DPRINTK_ALWAYS(xpc_chan, (XPC_DBG_C_SETUP | XPC_DBG_C_ERROR), + KERN_ERR "XPC: can't get memory for channels\n"); + return xpcNoMemory; + } + memset(part->channels, 0, sizeof(xpc_channel_t) * XPC_NCHANNELS); + + part->nchannels = XPC_NCHANNELS; + + + /* allocate all the required GET/PUT values */ + + part->local_GPs = kmalloc(XPC_GP_SIZE, GFP_KERNEL); + if (part->local_GPs == NULL) { + kfree(part->channels); + DPRINTK_ALWAYS(xpc_chan, (XPC_DBG_C_SETUP | XPC_DBG_C_ERROR), + KERN_ERR "XPC: can't get memory for local get/put " + "values\n"); + return xpcNoMemory; + } + XP_ASSERT(L1_CACHE_ALIGNED(part->local_GPs)); + memset(part->local_GPs, 0, XPC_GP_SIZE); + + part->remote_GPs = kmalloc(XPC_GP_SIZE, GFP_KERNEL); + if (part->remote_GPs == NULL) { + kfree(part->channels); + kfree(part->local_GPs); + DPRINTK_ALWAYS(xpc_chan, (XPC_DBG_C_SETUP | XPC_DBG_C_ERROR), + KERN_ERR "XPC: can't get memory for remote get/put " + "values\n"); + return xpcNoMemory; + } + XP_ASSERT(L1_CACHE_ALIGNED(part->remote_GPs)); + memset(part->remote_GPs, 0, XPC_GP_SIZE); + + + /* allocate all the required open and close args */ + + part->local_openclose_args = kmalloc(XPC_OPENCLOSE_ARGS_SIZE, + GFP_KERNEL); + if (part->local_openclose_args == NULL) { + kfree(part->channels); + kfree(part->local_GPs); + kfree(part->remote_GPs); + DPRINTK_ALWAYS(xpc_chan, (XPC_DBG_C_SETUP | XPC_DBG_C_ERROR), + KERN_ERR "XPC: can't get memory for local connect " + "args\n"); + return xpcNoMemory; + } + XP_ASSERT(L1_CACHE_ALIGNED(part->local_openclose_args)); + memset(part->local_openclose_args, 0, XPC_OPENCLOSE_ARGS_SIZE); + + part->remote_openclose_args = kmalloc(XPC_OPENCLOSE_ARGS_SIZE, + GFP_KERNEL); + if (part->remote_openclose_args == NULL) { + kfree(part->channels); + kfree(part->local_GPs); + kfree(part->remote_GPs); + kfree(part->local_openclose_args); + DPRINTK_ALWAYS(xpc_chan, (XPC_DBG_C_SETUP | XPC_DBG_C_ERROR), + KERN_ERR "XPC: can't get memory for remote connect " + "args\n"); + return xpcNoMemory; + } + XP_ASSERT(L1_CACHE_ALIGNED(part->remote_openclose_args)); + memset(part->remote_openclose_args, 0, XPC_OPENCLOSE_ARGS_SIZE); + + + xpc_initialize_channels(part, partid); + + atomic_set(&part->nchannels_active, 0); + + + /* local_IPI_amo were set to 0 by an earlier memset() */ + + /* Initialize this partitions AMO_t structure */ + part->local_IPI_amo_va = xpc_IPI_init(partid); + + spin_lock_init(&part->IPI_lock); + + atomic_set(&part->channel_mgr_requests, 1); + init_waitqueue_head(&part->channel_mgr_wq); + + sprintf(part->IPI_owner, "xpc%02d", partid); + ret = XPC_REQUEST_IRQ(SGI_XPC_NOTIFY, xpc_notify_IRQ_handler, SA_SHIRQ, + part->IPI_owner, (void *) (u64) partid); + if (ret != 0) { + kfree(part->channels); + kfree(part->local_GPs); + kfree(part->remote_GPs); + kfree(part->local_openclose_args); + kfree(part->remote_openclose_args); + DPRINTK_ALWAYS(xpc_chan, (XPC_DBG_C_SETUP | XPC_DBG_C_ERROR), + KERN_ERR "XPC: can't register NOTIFY IRQ handler, " + "errno=%d\n", -ret); + return xpcLackOfResources; + } + + /* Setup a timer to check for dropped IPIs */ + XPC_INIT_TIMER(&part->dropped_IPI_timer, xpc_dropped_IPI_check, part, + XPC_TICKS + XPC_P_DROPPED_IPI_WAIT); + + /* + * With the setting of the partition setup_state to XPC_P_SETUP, we're + * declaring that this partition is ready to go. + */ + part->setup_state = XPC_P_SETUP; + + + /* + * Setup the per partition specific variables required by the + * remote partition to establish channel connections with us. + * + * The setting of the magic # indicates that these per partition + * specific variables are ready to be used. + */ + xpc_vars_part[partid].GPs_pa = __pa(part->local_GPs); + xpc_vars_part[partid].openclose_args_pa = + __pa(part->local_openclose_args); + xpc_vars_part[partid].IPI_amo_pa = __pa(part->local_IPI_amo_va); + xpc_vars_part[partid].IPI_cpuid = cpu_physical_id(smp_processor_id()); + xpc_vars_part[partid].nchannels = part->nchannels; + xpc_vars_part[partid].magic = XPC_VP_MAGIC1; + + return xpcSuccess; +} + + +/* + * Create a wrapper that hides the underlying mechanism for pulling a cacheline + * (or multiple cachelines) from a remote partition. + * + * src must be a cacheline aligned physical address on the remote partition. + * dst must be a cacheline aligned virtual address on this partition. + * cnt must be an cacheline sized + */ +static __inline__ xpc_t +xpc_pull_remote_cachelines(xpc_partition_t *part, void *dst, const void *src, + size_t cnt) +{ + bte_result_t bte_ret; + + + XP_ASSERT(L1_CACHE_ALIGNED(src)); + XP_ASSERT(L1_CACHE_ALIGNED(dst)); + XP_ASSERT(L1_CACHE_ALIGNED(cnt)); + + if (part->act_state == XPC_P_DEACTIVATING) { + return part->reason; + } + + bte_ret = xp_bte_copy((u64) src, (u64) ia64_tpa((__u64) dst), + (u64) cnt, (BTE_NORMAL | BTE_WACQUIRE), NULL); + if (bte_ret == BTE_SUCCESS) { + return xpcSuccess; + } + + DPRINTK(xpc_chan, (XPC_DBG_C_IPI | XPC_DBG_C_ERROR), + "xp_bte_copy() from partition %d failed, ret=%d\n", + XPC_PARTID(part), bte_ret); + + return xpc_map_bte_errors(bte_ret); +} + + +/* + * Pull the remote per partititon specific variables from the specified + * partition. + */ +xpc_t +xpc_pull_remote_vars_part(xpc_partition_t *part) +{ + u8 buffer[L1_CACHE_BYTES * 2]; + xpc_vars_part_t *pulled_entry_cacheline = + (xpc_vars_part_t *) L1_CACHE_ALIGN((u64) buffer); + xpc_vars_part_t *pulled_entry; + u64 remote_entry_cacheline_pa, remote_entry_pa; + partid_t partid = XPC_PARTID(part); + xpc_t ret; + + + /* pull the cacheline that contains the variables we're interested in */ + + XP_ASSERT(L1_CACHE_ALIGNED(part->remote_vars_part_pa)); + XP_ASSERT(sizeof(xpc_vars_part_t) == L1_CACHE_BYTES / 2); + + remote_entry_pa = part->remote_vars_part_pa + + sn_local_partid() * sizeof(xpc_vars_part_t); + + remote_entry_cacheline_pa = (remote_entry_pa & ~(L1_CACHE_BYTES - 1)); + + pulled_entry = (xpc_vars_part_t *) ((u64) pulled_entry_cacheline + + (remote_entry_pa & (L1_CACHE_BYTES - 1))); + + ret = xpc_pull_remote_cachelines(part, pulled_entry_cacheline, + (void *) remote_entry_cacheline_pa, + L1_CACHE_BYTES); + if (ret != xpcSuccess) { + DPRINTK(xpc_chan, XPC_DBG_C_SETUP, + "failed to pull XPC vars_part from partition %d, " + "ret=%d\n", partid, ret); + return ret; + } + + + /* see if they've been set up yet */ + + if (pulled_entry->magic != XPC_VP_MAGIC1 && + pulled_entry->magic != XPC_VP_MAGIC2) { + + if (pulled_entry->magic != 0) { + DPRINTK(xpc_chan, XPC_DBG_C_SETUP, + "partition %d's XPC vars_part for partition %d" + " has bad magic value (=0x%lx)\n", partid, + sn_local_partid(), pulled_entry->magic); + return xpcBadMagic; + } + + /* they've not been initialized yet */ + return xpcRetry; + } + + if (xpc_vars_part[partid].magic == XPC_VP_MAGIC1) { + + /* validate the variables */ + + if (pulled_entry->GPs_pa == 0 || + pulled_entry->openclose_args_pa == 0 || + pulled_entry->IPI_amo_pa == 0) { + + DPRINTK_ALWAYS(xpc_chan, (XPC_DBG_C_SETUP | + XPC_DBG_C_ERROR), + KERN_ERR "XPC: partition %d's XPC vars_part " + "for partition %d are not valid\n", partid, + sn_local_partid()); + return xpcInvalidAddress; + } + + /* the variables we imported look to be valid */ + + part->remote_GPs_pa = pulled_entry->GPs_pa; + part->remote_openclose_args_pa = + pulled_entry->openclose_args_pa; + part->remote_IPI_amo_va = + (AMO_t *) __va(pulled_entry->IPI_amo_pa); + part->remote_IPI_cpuid = pulled_entry->IPI_cpuid; + + if (part->nchannels > pulled_entry->nchannels) { + part->nchannels = pulled_entry->nchannels; + } + + /* let the other side know that we've pulled their variables */ + + xpc_vars_part[partid].magic = XPC_VP_MAGIC2; + } + + if (pulled_entry->magic == XPC_VP_MAGIC1) { + return xpcRetry; + } + + return xpcSuccess; +} + + +/* + * Check to see if there is any channel activity to/from the specified + * partition. + */ +__inline__ void +xpc_check_for_channel_activity(xpc_partition_t *part) +{ + u64 IPI_amo; + unsigned long irq_flags; + + + IPI_amo = xpc_IPI_receive(part->local_IPI_amo_va); + if (IPI_amo == 0) { + return; + } + + spin_lock_irqsave(&part->IPI_lock, irq_flags); + part->local_IPI_amo |= IPI_amo; + spin_unlock_irqrestore(&part->IPI_lock, irq_flags); + + DPRINTK(xpc_chan, XPC_DBG_C_IPI, + "received IPI from partid=%d, IPI_amo=0x%lx\n", + XPC_PARTID(part), IPI_amo); + + XPC_PROCESS_CHANNEL_ACTIVITY(part); +} + + +/* + * Get the IPI flags and pull the openclose args and/or remote GPs as needed. + */ +static u64 +xpc_get_IPI_flags(xpc_partition_t *part) +{ + unsigned long irq_flags; + u64 IPI_amo; + xpc_t ret; + + + /* + * See if there are any IPI flags to be handled. + */ + + spin_lock_irqsave(&part->IPI_lock, irq_flags); + if ((IPI_amo = part->local_IPI_amo) != 0) { + part->local_IPI_amo = 0; + } + spin_unlock_irqrestore(&part->IPI_lock, irq_flags); + + + if (XPC_ANY_OPENCLOSE_IPI_FLAGS_SET(IPI_amo)) { + ret = xpc_pull_remote_cachelines(part, + part->remote_openclose_args, + (void *) part->remote_openclose_args_pa, + XPC_OPENCLOSE_ARGS_SIZE); + if (ret != xpcSuccess) { + XPC_DEACTIVATE_PARTITION(part, ret); + + DPRINTK(xpc_chan, XPC_DBG_C_IPI, + "failed to pull openclose args from partition " + "%d, ret=%d\n", XPC_PARTID(part), ret); + + /* don't bother processing IPIs anymore */ + IPI_amo = 0; + } + } + + if (XPC_ANY_MSG_IPI_FLAGS_SET(IPI_amo)) { + ret = xpc_pull_remote_cachelines(part, part->remote_GPs, + (void *) part->remote_GPs_pa, + XPC_GP_SIZE); + if (ret != xpcSuccess) { + XPC_DEACTIVATE_PARTITION(part, ret); + + DPRINTK(xpc_chan, XPC_DBG_C_IPI, + "failed to pull GPs from partition %d, " + "ret=%d\n", XPC_PARTID(part), ret); + + /* don't bother processing IPIs anymore */ + IPI_amo = 0; + } + } + + return IPI_amo; +} + + +/* + * Allocate the local message queue and the notify queue. + */ +static xpc_t +xpc_allocate_local_msgqueue(xpc_channel_t *ch) +{ + unsigned long irq_flags; + int nentries; + size_t nbytes; + + + // >>> may want to check for ch->flags & XPC_C_DISCONNECTING between + // >>> iterations of the for-loop, bail if set? + + // >>> should we impose a minumum #of entries? like 4 or 8? + for (nentries = ch->local_nentries; nentries > 0; nentries--) { + + nbytes = nentries * ch->msg_size; + ch->local_msgqueue = kmalloc(nbytes, GFP_KERNEL | GFP_DMA); + if (ch->local_msgqueue == NULL) { + continue; + } + XP_ASSERT(L1_CACHE_ALIGNED(ch->local_msgqueue)); + memset(ch->local_msgqueue, 0, nbytes); + + nbytes = nentries * sizeof(xpc_notify_t); + ch->notify_queue = kmalloc(nbytes, GFP_KERNEL | GFP_DMA); + if (ch->notify_queue == NULL) { + kfree(ch->local_msgqueue); + continue; + } + + // >>> do these really need to be cache aligned ? + XP_ASSERT(L1_CACHE_ALIGNED(ch->notify_queue)); + memset(ch->notify_queue, 0, nbytes); + + spin_lock_irqsave(&ch->lock, irq_flags); + if (nentries < ch->local_nentries) { + DPRINTK(xpc_chan, XPC_DBG_C_CONNECT, + "nentries=%d local_nentries=%d, partid=%d, " + "channel=%d\n", nentries, ch->local_nentries, + ch->partid, ch->number); + + ch->local_nentries = nentries; + } + spin_unlock_irqrestore(&ch->lock, irq_flags); + return xpcSuccess; + } + + DPRINTK(xpc_chan, XPC_DBG_C_CONNECT, + "can't get memory for local message queue and notify queue, " + "partid=%d, channel=%d\n", ch->partid, ch->number); + return xpcNoMemory; +} + + +/* + * Allocate the cached remote message queue. + */ +static xpc_t +xpc_allocate_remote_msgqueue(xpc_channel_t *ch) +{ + unsigned long irq_flags; + int nentries; + size_t nbytes; + + + XP_ASSERT(ch->remote_nentries > 0); + + // >>> may want to check for ch->flags & XPC_C_DISCONNECTING between + // >>> iterations of the for-loop, bail if set? + + // >>> should we impose a minumum #of entries? like 4 or 8? + for (nentries = ch->remote_nentries; nentries > 0; nentries--) { + + nbytes = nentries * ch->msg_size; + ch->remote_msgqueue = kmalloc(nbytes, GFP_KERNEL | GFP_DMA); + if (ch->remote_msgqueue == NULL) { + continue; + } + + XP_ASSERT(L1_CACHE_ALIGNED(ch->remote_msgqueue)); + memset(ch->remote_msgqueue, 0, nbytes); + + spin_lock_irqsave(&ch->lock, irq_flags); + if (nentries < ch->remote_nentries) { + DPRINTK(xpc_chan, XPC_DBG_C_CONNECT, + "nentries=%d remote_nentries=%d, partid=%d, " + "channel=%d\n", nentries, ch->remote_nentries, + ch->partid, ch->number); + + ch->remote_nentries = nentries; + } + spin_unlock_irqrestore(&ch->lock, irq_flags); + return xpcSuccess; + } + + DPRINTK(xpc_chan, XPC_DBG_C_CONNECT, + "can't get memory for cached remote message queue, partid=%d, " + "channel=%d\n", ch->partid, ch->number); + return xpcNoMemory; +} + + +/* + * Allocate message queues and other stuff associated with a channel. + * + * Note: Assumes all of the channel sizes are filled in. + */ +static xpc_t +xpc_allocate_msgqueues(xpc_channel_t *ch) +{ + unsigned long irq_flags; + int i; + xpc_t ret; + + + XP_ASSERT(!(ch->flags & XPC_C_SETUP)); + + if ((ret = xpc_allocate_local_msgqueue(ch)) != xpcSuccess) { + return ret; + } + + if ((ret = xpc_allocate_remote_msgqueue(ch)) != xpcSuccess) { + kfree(ch->local_msgqueue); + ch->local_msgqueue = NULL; + kfree(ch->notify_queue); + ch->notify_queue = NULL; + return ret; + } + + for (i = 0; i < ch->local_nentries; i++) { + /* use a semaphore as an event wait queue */ + sema_init(&ch->notify_queue[i].sema, 0); + } + + sema_init(&ch->teardown_sema, 0); /* event wait */ + + spin_lock_irqsave(&ch->lock, irq_flags); + ch->flags |= XPC_C_SETUP; + spin_unlock_irqrestore(&ch->lock, irq_flags); + + return xpcSuccess; +} + + +/* + * Process a connect message from a remote partition. + * + * Note: xpc_process_connect() is expecting to be called with the + * spin_lock_irqsave held and will leave it locked upon return. + */ +static void +xpc_process_connect(xpc_channel_t *ch, unsigned long *irq_flags) +{ + xpc_t ret; + + + XP_ASSERT(spin_is_locked(&ch->lock)); + + if (!(ch->flags & XPC_C_OPENREQUEST) || + !(ch->flags & XPC_C_ROPENREQUEST)) { + /* nothing more to do for now */ + return; + } + XP_ASSERT(ch->flags & XPC_C_CONNECTING); + + if (!(ch->flags & XPC_C_SETUP)) { + spin_unlock_irqrestore(&ch->lock, *irq_flags); + ret = xpc_allocate_msgqueues(ch); + spin_lock_irqsave(&ch->lock, *irq_flags); + + if (ret != xpcSuccess) { + XPC_DISCONNECT_CHANNEL(ch, ret, irq_flags); + } + if (ch->flags & (XPC_C_CONNECTED | XPC_C_DISCONNECTING)) { + return; + } + + XP_ASSERT(ch->flags & XPC_C_SETUP); + XP_ASSERT(ch->local_msgqueue != NULL); + XP_ASSERT(ch->remote_msgqueue != NULL); + } + + if (!(ch->flags & XPC_C_OPENREPLY)) { + ch->flags |= XPC_C_OPENREPLY; + XPC_IPI_SEND_OPENREPLY(ch, irq_flags); + } + + if (!(ch->flags & XPC_C_ROPENREPLY)) { + return; + } + + XP_ASSERT(ch->remote_msgqueue_pa != 0); + + ch->flags = (XPC_C_CONNECTED | XPC_C_SETUP); /* clear all else */ + + DPRINTK_ALWAYS(xpc_chan, (XPC_DBG_C_CONNECT | XPC_DBG_C_CONSOLE), + KERN_INFO "XPC: channel %d to partition %d connected\n", + ch->number, ch->partid); + + spin_unlock_irqrestore(&ch->lock, *irq_flags); + XPC_CONNECTED_CALLOUT(ch); + spin_lock_irqsave(&ch->lock, *irq_flags); +} + + +/* + * Free up message queues and other stuff that were allocated for the specified + * channel. + * + * Note: ch->reason and ch->reason_line are left set for debugging purposes, + * they're cleared when XPC_C_DISCONNECTED is cleared. + */ +static void +xpc_free_msgqueues(xpc_channel_t *ch) +{ + XP_ASSERT(spin_is_locked(&ch->lock)); + XP_ASSERT(atomic_read(&ch->n_to_notify) == 0); + + ch->remote_msgqueue_pa = 0; + ch->func = NULL; + ch->key = NULL; + ch->msg_size = 0; + ch->local_nentries = 0; + ch->remote_nentries = 0; + ch->kthreads_assigned_limit = 0; + ch->kthreads_idle_limit = 0; + + ch->local_GP->get = 0; + ch->local_GP->put = 0; + ch->remote_GP.get = 0; + ch->remote_GP.put = 0; + ch->w_local_GP.get = 0; + ch->w_local_GP.put = 0; + ch->w_remote_GP.get = 0; + ch->w_remote_GP.put = 0; + ch->next_msg_to_pull = 0; + + if (ch->flags & XPC_C_SETUP) { + ch->flags &= ~XPC_C_SETUP; + + DPRINTK(xpc_chan, XPC_DBG_C_TEARDOWN, + "ch->flags=0x%x, partid=%d, channel=%d\n", ch->flags, + ch->partid, ch->number); + + kfree(ch->local_msgqueue); + ch->local_msgqueue = NULL; + kfree(ch->remote_msgqueue); + ch->remote_msgqueue = NULL; + kfree(ch->notify_queue); + ch->notify_queue = NULL; + + /* in case someone is waiting for the teardown to complete */ + up(&ch->teardown_sema); + } +} + + +/* + * spin_lock_irqsave() is expected to be held on entry. + */ +static void +xpc_process_disconnect(xpc_channel_t *ch, unsigned long *irq_flags) +{ + xpc_partition_t *part = &xpc_partitions[ch->partid]; + u32 ch_flags = ch->flags; + + + XP_ASSERT(spin_is_locked(&ch->lock)); + + if (!(ch->flags & XPC_C_DISCONNECTING)) { + return; + } + + XP_ASSERT(ch->flags & XPC_C_CLOSEREQUEST); + + /* make sure all activity has settled down first */ + + if (atomic_read(&ch->references) > 0) { + return; + } + XP_ASSERT(atomic_read(&ch->kthreads_assigned) == 0); + + /* it's now safe to free the channel's message queues */ + + xpc_free_msgqueues(ch); + XP_ASSERT(!(ch->flags & XPC_C_SETUP)); + + if (part->act_state != XPC_P_DEACTIVATING) { + + /* as long as the other side is up do the full protocol */ + + if (!(ch->flags & XPC_C_RCLOSEREQUEST)) { + return; + } + + if (!(ch->flags & XPC_C_CLOSEREPLY)) { + ch->flags |= XPC_C_CLOSEREPLY; + XPC_IPI_SEND_CLOSEREPLY(ch, irq_flags); + } + + if (!(ch->flags & XPC_C_RCLOSEREPLY)) { + return; + } + } + + /* both sides are disconnected now */ + + ch->flags = XPC_C_DISCONNECTED; /* clear all flags, but this one */ + + atomic_dec(&part->nchannels_active); + + if (ch_flags & XPC_C_WASCONNECTED) { + DPRINTK_ALWAYS(xpc_chan, (XPC_DBG_C_DISCONNECT | + XPC_DBG_C_CONSOLE), + KERN_INFO "XPC: channel %d to partition %d " + "disconnected, reason=%s\n", ch->number, ch->partid, + xpc_get_ascii_reason_code(ch->reason)); + } + + XPC_DISCONNECTED_CALLOUT(ch, ch_flags, irq_flags); +} + + +/* + * Process a change in the channel's remote connection state. + */ +static void +xpc_process_openclose_IPI(xpc_partition_t *part, int ch_number, u8 IPI_flags) +{ + unsigned long irq_flags; + xpc_openclose_args_t *args = &part->remote_openclose_args[ch_number]; + xpc_channel_t *ch = &part->channels[ch_number]; + xpc_t reason; + + + + spin_lock_irqsave(&ch->lock, irq_flags); + + + if (IPI_flags & XPC_IPI_CLOSEREQUEST) { + + DPRINTK(xpc_chan, XPC_DBG_C_IPI, + "XPC_IPI_CLOSEREQUEST (reason=%d) received from partid" + "=%d, channel=%d\n", args->reason, + ch->partid, ch->number); + + /* + * If RCLOSEREQUEST is set, we're probably waiting for + * RCLOSEREPLY. We should find it and a ROPENREQUEST packed + * with this RCLOSEQREUQEST in the IPI_flags. + */ + + if (ch->flags & XPC_C_RCLOSEREQUEST) { + XP_ASSERT(ch->flags & XPC_C_DISCONNECTING); + XP_ASSERT(ch->flags & XPC_C_CLOSEREQUEST); + XP_ASSERT(ch->flags & XPC_C_CLOSEREPLY); + XP_ASSERT(!(ch->flags & XPC_C_RCLOSEREPLY)); + + XP_ASSERT(IPI_flags & XPC_IPI_CLOSEREPLY); + IPI_flags &= ~XPC_IPI_CLOSEREPLY; + ch->flags |= XPC_C_RCLOSEREPLY; + + /* both sides have finished disconnecting */ + xpc_process_disconnect(ch, &irq_flags); + } + + if (ch->flags & XPC_C_DISCONNECTED) { + // >>> explain this section + + if (!(IPI_flags & XPC_IPI_OPENREQUEST)) { + XP_ASSERT(part->act_state == + XPC_P_DEACTIVATING); + spin_unlock_irqrestore(&ch->lock, irq_flags); + return; + } + + XPC_SET_REASON(ch, 0, 0); + ch->flags &= ~XPC_C_DISCONNECTED; + + atomic_inc(&part->nchannels_active); + ch->flags |= (XPC_C_CONNECTING | XPC_C_ROPENREQUEST); + } + + IPI_flags &= ~(XPC_IPI_OPENREQUEST | XPC_IPI_OPENREPLY); + + /* + * The meaningful CLOSEREQUEST connection state fields are: + * reason = reason connection is to be closed + */ + + ch->flags |= XPC_C_RCLOSEREQUEST; + + if (!(ch->flags & XPC_C_DISCONNECTING)) { + reason = args->reason; + if (reason <= xpcSuccess || reason > xpcUnknownReason) { + reason = xpcUnknownReason; + } else if (reason == xpcUnregistering) { + reason = xpcOtherUnregistering; + } + + XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags); + } else { + xpc_process_disconnect(ch, &irq_flags); + } + } + + + if (IPI_flags & XPC_IPI_CLOSEREPLY) { + + DPRINTK(xpc_chan, XPC_DBG_C_IPI, + "XPC_IPI_CLOSEREPLY received from partid=%d, channel=" + "%d\n", ch->partid, ch->number); + + if (ch->flags & XPC_C_DISCONNECTED) { + XP_ASSERT(part->act_state == XPC_P_DEACTIVATING); + spin_unlock_irqrestore(&ch->lock, irq_flags); + return; + } + + XP_ASSERT(ch->flags & XPC_C_CLOSEREQUEST); + XP_ASSERT(ch->flags & XPC_C_RCLOSEREQUEST); + + ch->flags |= XPC_C_RCLOSEREPLY; + + if (ch->flags & XPC_C_CLOSEREPLY) { + /* both sides have finished disconnecting */ + xpc_process_disconnect(ch, &irq_flags); + } + } + + + if (IPI_flags & XPC_IPI_OPENREQUEST) { + + DPRINTK(xpc_chan, XPC_DBG_C_IPI, + "XPC_IPI_OPENREQUEST (msg_size=%d, local_nentries=%d) " + "received from partid=%d, channel=%d\n", + args->msg_size, args->local_nentries, + ch->partid, ch->number); + + if ((ch->flags & XPC_C_DISCONNECTING) || + part->act_state == XPC_P_DEACTIVATING) { + spin_unlock_irqrestore(&ch->lock, irq_flags); + return; + } + XP_ASSERT(ch->flags & (XPC_C_DISCONNECTED | XPC_C_OPENREQUEST)); + XP_ASSERT(!(ch->flags & (XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY | + XPC_C_OPENREPLY | XPC_C_CONNECTED))); + + /* + * The meaningful OPENREQUEST connection state fields are: + * msg_size = size of channel's messages in bytes + * local_nentries = remote partition's local_nentries + */ + XP_ASSERT(args->msg_size != 0); + XP_ASSERT(args->local_nentries != 0); + + ch->flags |= (XPC_C_ROPENREQUEST | XPC_C_CONNECTING); + ch->remote_nentries = args->local_nentries; + + + if (ch->flags & XPC_C_OPENREQUEST) { + if (args->msg_size != ch->msg_size) { + XPC_DISCONNECT_CHANNEL(ch, xpcUnequalMsgSizes, + &irq_flags); + spin_unlock_irqrestore(&ch->lock, irq_flags); + return; + } + } else { + ch->msg_size = args->msg_size; + + XPC_SET_REASON(ch, 0, 0); + ch->flags &= ~XPC_C_DISCONNECTED; + + atomic_inc(&part->nchannels_active); + } + + xpc_process_connect(ch, &irq_flags); + } + + + if (IPI_flags & XPC_IPI_OPENREPLY) { + + DPRINTK(xpc_chan, XPC_DBG_C_IPI, + "XPC_IPI_OPENREPLY (local_msgqueue_pa=0x%lx, " + "local_nentries=%d, remote_nentries=%d) received from " + "partid=%d, channel=%d\n", args->local_msgqueue_pa, + args->local_nentries, args->remote_nentries, + ch->partid, ch->number); + + if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED)) { + spin_unlock_irqrestore(&ch->lock, irq_flags); + return; + } + XP_ASSERT(ch->flags & XPC_C_OPENREQUEST); + XP_ASSERT(ch->flags & XPC_C_ROPENREQUEST); + XP_ASSERT(!(ch->flags & XPC_C_CONNECTED)); + + /* + * The meaningful OPENREPLY connection state fields are: + * local_msgqueue_pa = physical address of remote + * partition's local_msgqueue + * local_nentries = remote partition's local_nentries + * remote_nentries = remote partition's remote_nentries + */ + XP_ASSERT(args->local_msgqueue_pa != 0); + XP_ASSERT(args->local_nentries != 0); + XP_ASSERT(args->remote_nentries != 0); + + ch->flags |= XPC_C_ROPENREPLY; + ch->remote_msgqueue_pa = args->local_msgqueue_pa; + + if (args->local_nentries < ch->remote_nentries) { + DPRINTK(xpc_chan, XPC_DBG_C_IPI, + "XPC_IPI_OPENREPLY: new remote_nentries=%d, old" + " remote_nentries=%d, partid=%d, channel=%d\n", + args->local_nentries, ch->remote_nentries, + ch->partid, ch->number); + + ch->remote_nentries = args->local_nentries; + } + if (args->remote_nentries < ch->local_nentries) { + DPRINTK(xpc_chan, XPC_DBG_C_IPI, + "XPC_IPI_OPENREPLY: new local_nentries=%d, old " + "local_nentries=%d, partid=%d, channel=%d\n", + args->remote_nentries, ch->local_nentries, + ch->partid, ch->number); + + ch->local_nentries = args->remote_nentries; + } + + xpc_process_connect(ch, &irq_flags); + } + + spin_unlock_irqrestore(&ch->lock, irq_flags); +} + + +/* + * Attempt to establish a channel connection to a remote partition. + */ +static xpc_t +xpc_connect_channel(xpc_channel_t *ch) +{ + unsigned long irq_flags; + xpc_registration_t *registration = &xpc_registrations[ch->number]; + + + if (down_interruptible(®istration->sema) != 0) { + return xpcInterrupted; + } + + if (!XPC_CHANNEL_REGISTERED(ch->number)) { + up(®istration->sema); + return xpcUnregistered; + } + + spin_lock_irqsave(&ch->lock, irq_flags); + + XP_ASSERT(!(ch->flags & XPC_C_CONNECTED)); + XP_ASSERT(!(ch->flags & XPC_C_OPENREQUEST)); + + if (ch->flags & XPC_C_DISCONNECTING) { + spin_unlock_irqrestore(&ch->lock, irq_flags); + up(®istration->sema); + return ch->reason; + } + + + /* add info from the channel connect registration to the channel */ + + ch->kthreads_assigned_limit = registration->assigned_limit; + ch->kthreads_idle_limit = registration->idle_limit; + XP_ASSERT(atomic_read(&ch->kthreads_assigned) == 0); + XP_ASSERT(atomic_read(&ch->kthreads_idle) == 0); + XP_ASSERT(atomic_read(&ch->kthreads_active) == 0); + + ch->func = registration->func; + XP_ASSERT(registration->func != NULL); + ch->key = registration->key; + + ch->local_nentries = registration->nentries; + + if (ch->flags & XPC_C_ROPENREQUEST) { + if (registration->msg_size != ch->msg_size) { + /* the local and remote sides aren't the same */ + + /* + * Because XPC_DISCONNECT_CHANNEL() can block we're + * forced to up the registration sema before we unlock + * the channel lock. But that's okay here because we're + * done with the part that required the registration + * sema. XPC_DISCONNECT_CHANNEL() requires that the + * channel lock be locked and will unlock and relock + * the channel lock as needed. + */ + up(®istration->sema); + XPC_DISCONNECT_CHANNEL(ch, xpcUnequalMsgSizes, + &irq_flags); + spin_unlock_irqrestore(&ch->lock, irq_flags); + return xpcUnequalMsgSizes; + } + } else { + ch->msg_size = registration->msg_size; + + XPC_SET_REASON(ch, 0, 0); + ch->flags &= ~XPC_C_DISCONNECTED; + + atomic_inc(&xpc_partitions[ch->partid].nchannels_active); + } + + up(®istration->sema); + + + /* initiate the connection */ + + ch->flags |= (XPC_C_OPENREQUEST | XPC_C_CONNECTING); + XPC_IPI_SEND_OPENREQUEST(ch, &irq_flags); + + xpc_process_connect(ch, &irq_flags); + + spin_unlock_irqrestore(&ch->lock, irq_flags); + + return xpcSuccess; +} + + +#define XPC_DBG_C_NOTIFY ((reason == xpcMsgDelivered) ? \ + XPC_DBG_C_IPI : XPC_DBG_C_DISCONNECT) + + +/* + * Notify those who wanted to be notified upon delivery of their message. + */ +static void +xpc_notify_senders(xpc_channel_t *ch, xpc_t reason, s64 put) +{ + xpc_notify_t *notify; + u8 notify_type; + s64 get = ch->w_remote_GP.get - 1; + + + while (++get < put && atomic_read(&ch->n_to_notify) > 0) { + + notify = &ch->notify_queue[get % ch->local_nentries]; + + /* + * See if the notify entry indicates it was associated with + * a message who's sender wants to be notified. It is possible + * that it is, but someone else is doing or has done the + * notification. + */ + notify_type = notify->type; + if (notify_type == 0 || + cmpxchg(¬ify->type, notify_type, 0) != + notify_type) { + continue; + } + + XP_ASSERT(notify_type == XPC_N_CALL); + + atomic_dec(&ch->n_to_notify); + + DPRINTK(xpc_chan, XPC_DBG_C_NOTIFY, + "XPC_CALL_NOTIFY_FUNC() called, notify=0x%p, " + "msg_number=%ld, partid=%d, channel=%d\n", + (void *) notify, get, ch->partid, ch->number); + + XPC_CALL_NOTIFY_FUNC(ch, notify, reason); + + DPRINTK(xpc_chan, XPC_DBG_C_NOTIFY, + "XPC_CALL_NOTIFY_FUNC() returned, notify=0x%p, " + "msg_number=%ld, partid=%d, channel=%d\n", + (void *) notify, get, ch->partid, ch->number); + } +} + + +#undef XPC_DBG_C_NOTIFY + + +/* + * + */ +static void +xpc_process_msg_IPI(xpc_partition_t *part, int ch_number) +{ + xpc_channel_t *ch = &part->channels[ch_number]; + int msgs_sent; + + + ch->remote_GP = part->remote_GPs[ch_number]; + + + /* See what, if anything, has changed for each connected channel */ + + XPC_MSGQUEUE_REF(ch); + + if (ch->w_remote_GP.get == ch->remote_GP.get && + ch->w_remote_GP.put == ch->remote_GP.put) { + /* nothing changed since GPs were last pulled */ + XPC_MSGQUEUE_DEREF(ch); + return; + } + + if (!(ch->flags & XPC_C_CONNECTED)){ + XPC_MSGQUEUE_DEREF(ch); + return; + } + + + /* + * First check to see if messages recently sent by us have been + * received by the other side. (The remote GET value will have + * changed since we last looked at it.) + */ + + if (ch->w_remote_GP.get != ch->remote_GP.get) { + + /* + * We need to notify any senders that want to be notified + * that their sent messages have been received by their + * intended recipients. We need to do this before updating + * w_remote_GP.get so that we don't allocate the same message + * queue entries prematurely (see xpc_allocate_msg()). + */ + if (atomic_read(&ch->n_to_notify) > 0) { + /* + * Notify senders that messages sent have been + * received and delivered by the other side. + */ + xpc_notify_senders(ch, xpcMsgDelivered, + ch->remote_GP.get); + } + + /* + * Clear msg->flags in previously sent messages, so that + * they're ready for xpc_allocate_msg(). + */ + XPC_CLEAR_LOCAL_MSGQUEUE_FLAGS(ch); + + ch->w_remote_GP.get = ch->remote_GP.get; + + DPRINTK(xpc_chan, (XPC_DBG_C_IPI | XPC_DBG_C_GP), + "w_remote_GP.get changed to %ld, partid=%d, " + "channel=%d\n", ch->w_remote_GP.get, + ch->partid, ch->number); + + /* + * If anyone was waiting for message queue entries to become + * available, wake them up. + */ + if (atomic_read(&ch->n_on_msg_allocate_wq) > 0) { + wake_up(&ch->msg_allocate_wq); + } + } + + + /* + * Now check for newly sent messages by the other side. (The remote + * PUT value will have changed since we last looked at it.) + */ + + if (ch->w_remote_GP.put != ch->remote_GP.put) { + /* + * Clear msg->flags in previously received messages, so that + * they're ready for xpc_get_deliverable_msg(). + */ + XPC_CLEAR_REMOTE_MSGQUEUE_FLAGS(ch); + + ch->w_remote_GP.put = ch->remote_GP.put; + + DPRINTK(xpc_chan, (XPC_DBG_C_IPI | XPC_DBG_C_GP), + "w_remote_GP.put changed to %ld, partid=%d, " + "channel=%d\n", ch->w_remote_GP.put, + ch->partid, ch->number); + + msgs_sent = ch->w_remote_GP.put - ch->w_local_GP.get; + if (msgs_sent > 0) { + DPRINTK(xpc_chan, XPC_DBG_C_IPI, + "msgs waiting to be copied and delivered=%d, " + "partid=%d, channel=%d\n", msgs_sent, + ch->partid, ch->number); + + XPC_INITIATE_MSG_DELIVERY(ch, msgs_sent); + } + } + + XPC_MSGQUEUE_DEREF(ch); +} + + +void +xpc_process_channel_activity(xpc_partition_t *part) +{ + unsigned long irq_flags; + u64 IPI_amo, IPI_flags; + xpc_channel_t *ch; + int ch_number; + + + IPI_amo = xpc_get_IPI_flags(part); + + /* + * Initiate channel connections for registered channels. + * + * For each connected channel that has pending messages activate idle + * kthreads and/or create new kthreads as needed. + */ + + for (ch_number = 0; ch_number < part->nchannels; ch_number++) { + ch = &part->channels[ch_number]; + + + /* + * Process any open or close related IPI flags, and then deal + * with connecting or disconnecting the channel as required. + */ + + IPI_flags = XPC_GET_IPI_FLAGS(IPI_amo, ch_number); + + if (XPC_ANY_OPENCLOSE_IPI_FLAGS_SET(IPI_flags)) { + xpc_process_openclose_IPI(part, ch_number, IPI_flags); + } + + + if (ch->flags & XPC_C_DISCONNECTING) { + spin_lock_irqsave(&ch->lock, irq_flags); + xpc_process_disconnect(ch, &irq_flags); + spin_unlock_irqrestore(&ch->lock, irq_flags); + continue; + } + + if (part->act_state == XPC_P_DEACTIVATING) { + continue; + } + + if (!(ch->flags & XPC_C_CONNECTED)) { + if (!(ch->flags & XPC_C_OPENREQUEST)) { + XP_ASSERT(!(ch->flags & XPC_C_SETUP)); + (void) xpc_connect_channel(ch); + } else { + spin_lock_irqsave(&ch->lock, irq_flags); + xpc_process_connect(ch, &irq_flags); + spin_unlock_irqrestore(&ch->lock, irq_flags); + } + continue; + } + + + /* + * Process any message related IPI flags, this may involve the + * activation of kthreads to deliver any pending messages sent + * from the other partition. + */ + + if (XPC_ANY_MSG_IPI_FLAGS_SET(IPI_flags)) { + xpc_process_msg_IPI(part, ch_number); + } + } +} + + +/* + * XPC's heartbeat code calls this function to inform XPC that a partition has + * gone down. XPC responds by tearing down the XPartition Communication + * infrastructure used for the just downed partition. + * + * XPC's heartbeat code will never call this function and xpc_partition_up() + * at the same time. Nor will it ever make multiple calls to either function + * at the same time. + */ +void +xpc_partition_down(xpc_partition_t *part, xpc_t reason) +{ + unsigned long irq_flags; + int ch_number; + xpc_channel_t *ch; + + + DPRINTK(xpc_chan, XPC_DBG_C_TEARDOWN, + "deactivating partition %d, reason=%d\n", XPC_PARTID(part), + reason); + + if (!XPC_PART_REF(part)) { + /* infrastructure for this partition isn't currently set up */ + return; + } + + + /* disconnect all channels associated with the downed partition */ + + for (ch_number = 0; ch_number < part->nchannels; ch_number++) { + ch = &part->channels[ch_number]; + + + XPC_MSGQUEUE_REF(ch); + spin_lock_irqsave(&ch->lock, irq_flags); + + XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags); + + spin_unlock_irqrestore(&ch->lock, irq_flags); + XPC_MSGQUEUE_DEREF(ch); + } + + XPC_PROCESS_PARTITION_DOWN(part); + + XPC_PART_DEREF(part); +} + + +/* + * Teardown the infrastructure necessary to support XPartition Communication + * between the specified remote partition and the local one. + */ +void +xpc_teardown_infrastructure(xpc_partition_t *part) +{ + partid_t partid = XPC_PARTID(part); + + + /* + * We start off by making this partition inaccessible to local + * processes by marking it as no longer setup. Then we make it + * inaccessible to remote processes by clearing the XPC per partition + * specific variable's magic # (which indicates that these variables + * are no longer valid) and by ignoring all XPC notify IPIs sent to + * this partition. + */ + + XP_ASSERT(atomic_read(&part->nchannels_active) == 0); + XP_ASSERT(part->setup_state == XPC_P_SETUP); + part->setup_state = XPC_P_WTEARDOWN; + + xpc_vars_part[partid].magic = 0; + + + XPC_FREE_IRQ(SGI_XPC_NOTIFY, (void *) (u64) partid); + + + /* + * Before proceding with the teardown we have to wait until all + * existing references cease. + */ + wait_event(part->teardown_wq, (atomic_read(&part->references) == 0)); + + + /* now we can begin tearing down the infrastructure */ + + part->setup_state = XPC_P_TORNDOWN; + + /* in case we've still got outstanding timers registered... */ + XPC_DEL_TIMER(&part->dropped_IPI_timer); + + kfree(part->remote_openclose_args); + part->remote_openclose_args = NULL; + kfree(part->local_openclose_args); + part->local_openclose_args = NULL; + kfree(part->remote_GPs); + part->remote_GPs = NULL; + kfree(part->local_GPs); + part->local_GPs = NULL; + kfree(part->channels); + part->channels = NULL; + part->local_IPI_amo_va = NULL; +} + + +/* + * Called by XP at the time of channel connection registration to cause + * XPC to establish connections to all currently active partitions. + */ +void +xpc_initiate_connect(int ch_number) +{ + partid_t partid; + xpc_partition_t *part; + xpc_channel_t *ch; + + + XP_ASSERT(ch_number >= 0 && ch_number < XPC_NCHANNELS); + + for (partid = 1; partid < MAX_PARTITIONS; partid++) { + part = &xpc_partitions[partid]; + + if (XPC_PART_REF(part)) { + ch = &part->channels[ch_number]; + + if (!(ch->flags & XPC_C_DISCONNECTING)) { + XP_ASSERT(!(ch->flags & XPC_C_OPENREQUEST)); + XP_ASSERT(!(ch->flags & XPC_C_CONNECTED)); + XP_ASSERT(!(ch->flags & XPC_C_SETUP)); + + /* + * Initiate the establishment of a connection + * on the newly registered channel to the + * remote partition. + */ + XPC_PROCESS_CHANNEL_ACTIVITY(part); + } + + XPC_PART_DEREF(part); + } + } +} + + +void +xpc_connected_callout(xpc_channel_t *ch) +{ + unsigned long irq_flags; + + + /* let the registerer know that a connection has been established */ + + DPRINTK(xpc_chan, XPC_DBG_C_CONNECT, + "XPC_CALL_CHANNEL_FUNC() called, reason=xpcConnected, " + "partid=%d, channel=%d\n", ch->partid, ch->number); + + XPC_CALL_CHANNEL_FUNC(ch, xpcConnected, + (void *) (u64) ch->local_nentries); + + DPRINTK(xpc_chan, XPC_DBG_C_CONNECT, + "XPC_CALL_CHANNEL_FUNC() returned, reason=xpcConnected, " + "partid=%d, channel=%d\n", ch->partid, ch->number); + + spin_lock_irqsave(&ch->lock, irq_flags); + ch->flags |= XPC_C_CONNECTCALLOUT; + spin_unlock_irqrestore(&ch->lock, irq_flags); +} + + +/* + * Called by XP at the time of channel connection unregistration to cause + * XPC to teardown all current connections for the specified channel. + * + * Before returning xpc_initiate_disconnect() will wait until all connections + * on the specified channel have been closed/torndown. So the caller can be + * assured that they will not be receiving any more callouts from XPC to the + * function they registered via xpc_connect(). + * + * Arguments: + * + * ch_number - channel # to unregister. + */ +void +xpc_initiate_disconnect(int ch_number) +{ + unsigned long irq_flags; + partid_t partid; + xpc_partition_t *part; + xpc_channel_t *ch; + + + XP_ASSERT(ch_number >= 0 && ch_number < XPC_NCHANNELS); + + /* initiate the channel disconnect for every active partition */ + for (partid = 1; partid < MAX_PARTITIONS; partid++) { + part = &xpc_partitions[partid]; + + if (XPC_PART_REF(part)) { + ch = &part->channels[ch_number]; + XPC_MSGQUEUE_REF(ch); + + spin_lock_irqsave(&ch->lock, irq_flags); + + XPC_DISCONNECT_CHANNEL(ch, xpcUnregistering, + &irq_flags); + + spin_unlock_irqrestore(&ch->lock, irq_flags); + + XPC_MSGQUEUE_DEREF(ch); + XPC_PART_DEREF(part); + } + } + + XPC_DISCONNECT_WAIT(ch_number); +} + + +/* + * To disconnect a channel, and reflect it back to all who may be waiting. + * + * >>> An OPEN is not allowed until XPC_C_DISCONNECTING is cleared by + * >>> xpc_free_msgqueues(). + * + * THE CHANNEL IS TO BE LOCKED BY THE CALLER AND WILL REMAIN LOCKED UPON RETURN. + */ +void +xpc_disconnect_channel(const int line, xpc_channel_t *ch, xpc_t reason, + unsigned long *irq_flags) +{ + u32 flags; + + + XP_ASSERT(spin_is_locked(&ch->lock)); + + if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED)) { + return; + } + XP_ASSERT(ch->flags & (XPC_C_CONNECTING | XPC_C_CONNECTED)); + + DPRINTK(xpc_chan, XPC_DBG_C_DISCONNECT, + "reason=%d, line=%d, partid=%d, channel=%d\n", reason, line, + ch->partid, ch->number); + + XPC_SET_REASON(ch, reason, line); + + flags = ch->flags; + /* some of these may not have been set */ + ch->flags &= ~(XPC_C_OPENREQUEST | XPC_C_OPENREPLY | + XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY | + XPC_C_CONNECTING | XPC_C_CONNECTED); + + ch->flags |= (XPC_C_CLOSEREQUEST | XPC_C_DISCONNECTING); + XPC_IPI_SEND_CLOSEREQUEST(ch, irq_flags); + + if (flags & XPC_C_CONNECTED) { + ch->flags |= XPC_C_WASCONNECTED; + } + + if (atomic_read(&ch->kthreads_idle) > 0) { + /* wake all idle kthreads so they can exit */ + wake_up_all(&ch->idle_wq); + } + + spin_unlock_irqrestore(&ch->lock, *irq_flags); + + + /* wake those waiting to allocate an entry from the local msg queue */ + + if (atomic_read(&ch->n_on_msg_allocate_wq) > 0) { + wake_up(&ch->msg_allocate_wq); + } + + /* wake those waiting for notify completion */ + + if (atomic_read(&ch->n_to_notify) > 0) { + xpc_notify_senders(ch, reason, ch->w_local_GP.put); + } + + spin_lock_irqsave(&ch->lock, *irq_flags); +} + + +void +xpc_disconnected_callout(xpc_channel_t *ch) +{ + /* + * Let the channel's registerer know that the channel is now + * disconnected. We don't want to do this if the registerer was never + * informed of a connection being made, unless the disconnect was for + * abnormal reasons. + */ + + DPRINTK(xpc_chan, XPC_DBG_C_DISCONNECT, + "XPC_CALL_CHANNEL_FUNC() called, reason=%d, partid=%d, " + "channel=%d\n", ch->reason, ch->partid, ch->number); + + XPC_CALL_CHANNEL_FUNC(ch, ch->reason, NULL); + + DPRINTK(xpc_chan, XPC_DBG_C_DISCONNECT, + "XPC_CALL_CHANNEL_FUNC() returned, reason=%d, partid=%d, " + "channel=%d\n", ch->reason, ch->partid, ch->number); +} + + +/* + * Wait for a message entry to become available for the specified channel, + * but don't wait any longer than 1 jiffy. + */ +static xpc_t +xpc_allocate_msg_wait(xpc_channel_t *ch) +{ + xpc_t ret; + + + if (ch->flags & XPC_C_DISCONNECTING) { + XP_ASSERT(ch->reason != xpcInterrupted); // >>> Is this true? + return ch->reason; + } + + // >>> It may be more cost effective if the normal kthreads simply call + // >>> interruptible_sleep_on() and then a separate timer function is + // >>> registered to wakeup at some interval and see if there are any + // >>> available. If yes, then it wakes up the sleepers; if no, it + // >>> fakes an IPI to ourselves and resets itself to wakeup later. + // >>> xpc_IPI_handler() would also be able to wake sleepers up if it + // >>> sees that there are available messages. + + // >>> In IRIX, XPC_ACQ_TO was set to 40 microseconds, which + // >>> would mean that the first timeout was 80 microseconds. + // >>> Dimitri was seeing differences in performance between + // >>> 20, 40 and 80 microsecond values for the timeout. + // >>> Linux timers are in jiffies which are in milliseconds, + // >>> which is of course a much coarser ganularity. + // >>> For now we're hardcoding the timeout to 1 jiffy. + + atomic_inc(&ch->n_on_msg_allocate_wq); + ret = interruptible_sleep_on_timeout(&ch->msg_allocate_wq, 1); + atomic_dec(&ch->n_on_msg_allocate_wq); + + if (ch->flags & XPC_C_DISCONNECTING) { + ret = ch->reason; + XP_ASSERT(ch->reason != xpcInterrupted); // >>> Is this true? + } else if (ret == 0) { + ret = xpcTimeout; + } else { + ret = xpcInterrupted; + } + + return ret; +} + + +/* + * Allocate an entry for a message from the message queue associated with the + * specified channel. + */ +static xpc_t +xpc_allocate_msg(xpc_channel_t *ch, u32 flags, xpc_msg_t **address_of_msg) +{ + xpc_msg_t *msg; + xpc_t ret; + s64 put; + + + /* this reference will be dropped in xpc_initiate_send() */ + XPC_MSGQUEUE_REF(ch); + + if (ch->flags & XPC_C_DISCONNECTING) { + XPC_MSGQUEUE_DEREF(ch); + return ch->reason; + } + if (!(ch->flags & XPC_C_CONNECTED)) { + XPC_MSGQUEUE_DEREF(ch); + return xpcNotConnected; + } + + + /* + * Get the next available message entry from the local message queue. + * If none are available, we'll make sure that we grab the latest + * GP values. + */ + ret = xpcTimeout; + + while (1) { + + put = ch->w_local_GP.put; + if (put - ch->w_remote_GP.get < ch->local_nentries) { + + /* There are available message entries. We need to try + * to secure one for ourselves. We'll do this by trying + * to increment w_local_GP.put as long as someone else + * doesn't beat us to it. If they do, we'll have to + * try again. + */ + if (cmpxchg(&ch->w_local_GP.put, put, put + 1) == + put) { + /* we got the entry referenced by put */ + break; + } + continue; /* try again */ + } + + + /* + * There aren't any available msg entries at this time. + * + * In waiting for a message entry to become available, + * we set a timeout in case the other side is not + * sending completion IPIs. This lets us fake an IPI + * that will cause the IPI handler to fetch the latest + * GP values as if an IPI was sent by the other side. + */ + if (ret == xpcTimeout) { + XPC_IPI_SEND_LOCAL_MSGREQUEST(ch); + } + + // >>> Note that Dimitri said that he saw a noticeable + // >>> speedup because of the forced fetch of the GPs, + // >>> in my new scheme we do eliminate the speediness + // >>> of the forced approach as in now we have to wait + // >>> for a kthread to be scheduled, whereas before we + // >>> were the thread that did that fetch. We could + // >>> explore creating a kthread (maybe the one that + // >>> creates new kthreads when needed) that does a + // >>> force fetch of the GPs at some time interval + // >>> whenever we have messages sent that we haven't + // >>> yet accounted for (the other side hasn't moved + // >>> the get value). Anyway something to think about. + + if (flags & XPC_NOWAIT) { + XPC_MSGQUEUE_DEREF(ch); + return xpcNoWait; + } + + ret = xpc_allocate_msg_wait(ch); + if (ret != xpcInterrupted && ret != xpcTimeout) { + XPC_MSGQUEUE_DEREF(ch); + return ret; + } + } + + + /* get the message's address and initialize it */ + msg = (xpc_msg_t *) ((u64) ch->local_msgqueue + + (put % ch->local_nentries) * ch->msg_size); + + + XP_ASSERT(msg->flags == 0); + msg->number = put; + + DPRINTK(xpc_chan, (XPC_DBG_C_SEND | XPC_DBG_C_GP), + "w_local_GP.put changed to %ld; msg=0x%p, msg_number=%ld, " + "partid=%d, channel=%d\n", put + 1, (void *) msg, msg->number, + ch->partid, ch->number); + + *address_of_msg = msg; + + return xpcSuccess; +} + + +/* + * Allocate an entry for a message from the message queue associated with the + * specified channel. NOTE that this routine can sleep waiting for a message + * entry to become available. To not sleep, pass in the XPC_NOWAIT flag. + * + * Arguments: + * + * partid - ID of partition to which the channel is connected. + * ch_number - channel #. + * flags - see xpc.h for valid flags. + * payload - address of the allocated payload area pointer (filled in on + * return) in which the user-defined message is constructed. + */ +xpc_t +xpc_allocate(partid_t partid, int ch_number, u32 flags, void **payload) +{ + xpc_partition_t *part = &xpc_partitions[partid]; + xpc_t ret = xpcUnknownReason; + xpc_msg_t *msg; + + + XP_ASSERT(partid > 0 && partid < MAX_PARTITIONS); + XP_ASSERT(ch_number >= 0 && ch_number < part->nchannels); + + *payload = NULL; + + if (XPC_PART_REF(part)) { + ret = xpc_allocate_msg(&part->channels[ch_number], flags, &msg); + XPC_PART_DEREF(part); + + if (msg != NULL) { + *payload = &msg->payload; + } + } + + return ret; +} + + +/* + * Now we actually send the messages that are ready to be sent by advancing + * the local message queue's Put value and then send an IPI to the recipient + * partition. + */ +static void +xpc_send_msgs(xpc_channel_t *ch, s64 initial_put) +{ + xpc_msg_t *msg; + s64 put = initial_put + 1; + int send_IPI = 0; + + + while (1) { + + while (1) { + if (put == ch->w_local_GP.put) { + break; + } + + msg = (xpc_msg_t *) ((u64) ch->local_msgqueue + + (put % ch->local_nentries) * ch->msg_size); + + if (!(msg->flags & XPC_M_READY)) { + break; + } + + put++; + } + + if (put == initial_put) { + /* nothing's changed */ + break; + } + + if (cmpxchg_rel(&ch->local_GP->put, initial_put, put) != + initial_put) { + /* someone else beat us to it */ + XP_ASSERT(ch->local_GP->put > initial_put); + break; + } + + /* we just set the new value of local_GP->put */ + + DPRINTK(xpc_chan, (XPC_DBG_C_SEND | XPC_DBG_C_GP), + "local_GP->put changed to %ld, partid=%d, channel=%d\n", + put, ch->partid, ch->number); + + send_IPI = 1; + + /* + * We need to ensure that the message referenced by + * local_GP->put is not XPC_M_READY or that local_GP->put + * equals w_local_GP.put, so we'll go have a look. + */ + initial_put = put; + } + + if (send_IPI) { + XPC_IPI_SEND_MSGREQUEST(ch); + } +} + + +/* + * Common code that does the actual sending of the message by advancing the + * local message queue's Put value and sends an IPI to the partition the + * message is being sent to. + */ +static xpc_t +xpc_initiate_send(xpc_channel_t *ch, xpc_msg_t *msg, u8 notify_type, + xpc_notify_func_t func, void *key) +{ + xpc_t ret = xpcSuccess; + xpc_notify_t *notify = NULL; // >>> to keep the compiler happy!!!!! + s64 put, msg_number = msg->number; + + + XP_ASSERT(notify_type != XPC_N_CALL || func != NULL); + XP_ASSERT((((u64) msg - (u64) ch->local_msgqueue) / ch->msg_size) == + msg_number % ch->local_nentries); + XP_ASSERT(!(msg->flags & XPC_M_READY)); + + if (ch->flags & XPC_C_DISCONNECTING) { + /* drop the reference grabbed in xpc_allocate_msg() */ + XPC_MSGQUEUE_DEREF(ch); + return ch->reason; + } + + if (notify_type != 0) { + /* + * Tell the remote side to send an ACK interrupt when the + * message has been delivered. + */ + msg->flags |= XPC_M_INTERRUPT; + + atomic_inc(&ch->n_to_notify); + + notify = &ch->notify_queue[msg_number % ch->local_nentries]; + notify->func = func; + notify->key = key; + notify->type = notify_type; + + // >>> is a mb() needed here? + + if (ch->flags & XPC_C_DISCONNECTING) { + /* + * An error occurred between our last error check and + * this one. We will try to clear the type field from + * the notify entry. If we succeed then + * xpc_disconnect_channel() didn't already process + * the notify entry. + */ + if (cmpxchg(¬ify->type, notify_type, 0) == + notify_type) { + atomic_dec(&ch->n_to_notify); + ret = ch->reason; + } + + /* drop the reference grabbed in xpc_allocate_msg() */ + XPC_MSGQUEUE_DEREF(ch); + return ret; + } + } + + msg->flags |= XPC_M_READY; + + /* + * The preceding store of msg->flags must occur before the following + * load of ch->local_GP->put. + */ + mb(); + + /* see if the message is next in line to be sent, if so send it */ + + put = ch->local_GP->put; + if (put == msg_number) { + xpc_send_msgs(ch, put); + } + + /* drop the reference grabbed in xpc_allocate_msg() */ + XPC_MSGQUEUE_DEREF(ch); + return ret; +} + + +/* + * Send a message previously allocated using xpc_allocate() on the specified + * channel connected to the specified partition. + * + * This routine will not wait for the message to be received, nor will + * notification be given when it does happen. Once this routine has returned + * the message entry allocated via xpc_allocate() is no longer accessable to + * the caller. + * + * This routine, although called by users, does not call XPC_PART_REF() to + * ensure that the partition infrastructure is in place. It relies on the + * fact that we called XPC_MSGQUEUE_REF() in xpc_allocate_msg(). + * + * Arguments: + * + * partid - ID of partition to which the channel is connected. + * ch_number - channel # to send message on. + * payload - pointer to the payload area allocated via xpc_allocate(). + */ +xpc_t +xpc_send(partid_t partid, int ch_number, void *payload) +{ + xpc_partition_t *part = &xpc_partitions[partid]; + xpc_msg_t *msg = XPC_MSG_ADDRESS(payload); + xpc_t ret; + + + DPRINTK(xpc_chan, XPC_DBG_C_SEND, + "msg=0x%p, partid=%d, channel=%d\n", (void *) msg, partid, + ch_number); + + XP_ASSERT(partid > 0 && partid < MAX_PARTITIONS); + XP_ASSERT(ch_number >= 0 && ch_number < part->nchannels); + XP_ASSERT(msg != NULL); + + ret = xpc_initiate_send(&part->channels[ch_number], msg, 0, NULL, NULL); + + return ret; +} + + +/* + * Send a message previously allocated using xpc_allocate on the specified + * channel connected to the specified partition. + * + * This routine will not wait for the message to be sent. Once this routine + * has returned the message entry allocated via xpc_allocate() is no longer + * accessable to the caller. + * + * Once the remote end of the channel has received the message, the function + * passed as an argument to xpc_send_notify() will be called. This allows the + * sender to free up or re-use any buffers referenced by the message, but does + * NOT mean the message has been processed at the remote end by a receiver. + * + * If this routine returns an error, the caller's function will NOT be called. + * + * This routine, although called by users, does not call XPC_PART_REF() to + * ensure that the partition infrastructure is in place. It relies on the + * fact that we called XPC_MSGQUEUE_REF() in xpc_allocate_msg(). + * + * Arguments: + * + * partid - ID of partition to which the channel is connected. + * ch_number - channel # to send message on. + * payload - pointer to the payload area allocated via xpc_allocate(). + * func - function to call with asynchronous notification of message + * receipt. THIS FUNCTION MUST BE NON-BLOCKING. + * key - user-defined key to be passed to the function when it's called. + */ +xpc_t +xpc_send_notify(partid_t partid, int ch_number, void *payload, + xpc_notify_func_t func, void *key) +{ + xpc_partition_t *part = &xpc_partitions[partid]; + xpc_msg_t *msg = XPC_MSG_ADDRESS(payload); + xpc_t ret; + + + DPRINTK(xpc_chan, XPC_DBG_C_SEND, + "msg=0x%p, partid=%d, channel=%d\n", (void *) msg, partid, + ch_number); + + XP_ASSERT(partid > 0 && partid < MAX_PARTITIONS); + XP_ASSERT(ch_number >= 0 && ch_number < part->nchannels); + XP_ASSERT(msg != NULL); + XP_ASSERT(func != NULL); + + ret = xpc_initiate_send(&part->channels[ch_number], msg, XPC_N_CALL, + func, key); + return ret; +} + + +static __inline__ xpc_msg_t * +xpc_pull_remote_msg(xpc_channel_t *ch, s64 get) +{ + xpc_partition_t *part = &xpc_partitions[ch->partid]; + xpc_msg_t *remote_msg, *msg; + u32 msg_index, nmsgs; + u64 msg_offset; + xpc_t ret; + + + if (down_interruptible(&ch->msg_to_pull_sema) != 0) { + /* we were interrupted by a signal */ + return NULL; + } + + while (get >= ch->next_msg_to_pull) { + + /* pull as many messages as are ready and able to be pulled */ + + msg_index = ch->next_msg_to_pull % ch->remote_nentries; + + XP_ASSERT(ch->next_msg_to_pull < ch->w_remote_GP.put); + nmsgs = ch->w_remote_GP.put - ch->next_msg_to_pull; + if (msg_index + nmsgs > ch->remote_nentries) { + /* ignore the ones that wrap the msg queue for now */ + nmsgs = ch->remote_nentries - msg_index; + } + + msg_offset = msg_index * ch->msg_size; + msg = (xpc_msg_t *) ((u64) ch->remote_msgqueue + msg_offset); + remote_msg = (xpc_msg_t *) (ch->remote_msgqueue_pa + + msg_offset); + + if ((ret = xpc_pull_remote_cachelines(part, msg, remote_msg, + nmsgs * ch->msg_size)) != xpcSuccess) { + + DPRINTK(xpc_chan, XPC_DBG_C_RECEIVE, + "failed to pull %d msgs starting with msg %ld " + "from partition %d, channel=%d, ret=%d\n", + nmsgs, ch->next_msg_to_pull, ch->partid, + ch->number, ret); + + XPC_DEACTIVATE_PARTITION(part, ret); + + up(&ch->msg_to_pull_sema); + return NULL; + } + + mb(); /* >>> this may not be needed, we're not sure */ + + ch->next_msg_to_pull += nmsgs; + } + + up(&ch->msg_to_pull_sema); + + /* return the message we were looking for */ + msg_offset = (get % ch->remote_nentries) * ch->msg_size; + msg = (xpc_msg_t *) ((u64) ch->remote_msgqueue + msg_offset); + + return msg; +} + + +/* + * Get a message to be delivered. + */ +static xpc_msg_t * +xpc_get_deliverable_msg(xpc_channel_t *ch) +{ + xpc_msg_t *msg = NULL; + s64 get; + + + do { + if (ch->flags & XPC_C_DISCONNECTING) { + break; + } + + get = ch->w_local_GP.get; + if (get == ch->w_remote_GP.put) { + break; + } + + /* There are messages waiting to be pulled and delivered. + * We need to try to secure one for ourselves. We'll do this + * by trying to increment w_local_GP.get and hope that no one + * else beats us to it. If they do, we'll we'll simply have + * to try again for the next one. + */ + + if (cmpxchg(&ch->w_local_GP.get, get, get + 1) == get) { + /* we got the entry referenced by get */ + + DPRINTK(xpc_chan, (XPC_DBG_C_RECEIVE | XPC_DBG_C_GP), + "w_local_GP.get changed to %ld, partid=%d, " + "channel=%d\n", get + 1, + ch->partid, ch->number); + + /* pull the message from the remote partition */ + + msg = xpc_pull_remote_msg(ch, get); + + XP_ASSERT(msg == NULL || msg->number == get); + XP_ASSERT(msg == NULL || !(msg->flags & XPC_M_DONE)); + XP_ASSERT(msg == NULL || msg->flags & XPC_M_READY); + + break; + } + + } while (1); + + return msg; +} + + +/* + * Deliver a message to its intended recipient. + */ +void +xpc_deliver_msg(xpc_channel_t *ch) +{ + xpc_msg_t *msg; + + + if ((msg = xpc_get_deliverable_msg(ch)) != NULL) { + + /* + * This ref is taken to protect the payload itself from being + * freed before the user is finished with it, which the user + * indicates by calling xpc_received(). + */ + XPC_MSGQUEUE_REF(ch); + + atomic_inc(&ch->kthreads_active); + + DPRINTK(xpc_chan, XPC_DBG_C_RECEIVE, + "XPC_CALL_CHANNEL_FUNC() called, msg=0x%p, " + "msg_number=%ld, partid=%d, channel=%d\n", + (void *) msg, msg->number, ch->partid, ch->number); + + /* deliver the message to its intended recipient */ + XPC_CALL_CHANNEL_FUNC(ch, xpcMsgReceived, &msg->payload); + + DPRINTK(xpc_chan, XPC_DBG_C_RECEIVE, + "XPC_CALL_CHANNEL_FUNC() returned, msg=0x%p, " + "msg_number=%ld, partid=%d, channel=%d\n", + (void *) msg, msg->number, ch->partid, ch->number); + + atomic_dec(&ch->kthreads_active); + } +} + + +/* + * Now we actually acknowledge the messages that have been delivered and ack'd + * by advancing the cached remote message queue's Get value and if requested + * send an IPI to the message sender's partition. + */ +static void +xpc_acknowledge_msgs(xpc_channel_t *ch, s64 initial_get, u8 msg_flags) +{ + xpc_msg_t *msg; + s64 get = initial_get + 1; + int send_IPI = 0; + + + while (1) { + + while (1) { + if (get == ch->w_local_GP.get) { + break; + } + + msg = (xpc_msg_t *) ((u64) ch->remote_msgqueue + + (get % ch->remote_nentries) * ch->msg_size); + + if (!(msg->flags & XPC_M_DONE)) { + break; + } + + msg_flags |= msg->flags; + get++; + } + + if (get == initial_get) { + /* nothing's changed */ + break; + } + + if (cmpxchg_rel(&ch->local_GP->get, initial_get, get) != + initial_get) { + /* someone else beat us to it */ + XP_ASSERT(ch->local_GP->get > initial_get); + break; + } + + /* we just set the new value of local_GP->get */ + + DPRINTK(xpc_chan, (XPC_DBG_C_RECEIVE | XPC_DBG_C_GP), + "local_GP->get changed to %ld, partid=%d, channel=%d\n", + get, ch->partid, ch->number); + + send_IPI = (msg_flags & XPC_M_INTERRUPT); + + /* + * We need to ensure that the message referenced by + * local_GP->get is not XPC_M_DONE or that local_GP->get + * equals w_local_GP.get, so we'll go have a look. + */ + initial_get = get; + } + + if (send_IPI) { + XPC_IPI_SEND_MSGREQUEST(ch); + } +} + + +/* + * Acknowledge receipt of a delivered message. + * + * If a message has XPC_M_INTERRUPT set, send an interrupt to the partition + * that sent the message. + * + * This function, although called by users, does not call XPC_PART_REF() to + * ensure that the partition infrastructure is in place. It relies on the + * fact that we called XPC_MSGQUEUE_REF() in xpc_deliver_msg(). + * + * Arguments: + * + * partid - ID of partition to which the channel is connected. + * ch_number - channel # message received on. + * payload - pointer to the payload area allocated via xpc_allocate(). + */ +void +xpc_received(partid_t partid, int ch_number, void *payload) +{ + xpc_partition_t *part = &xpc_partitions[partid]; + xpc_channel_t *ch; + xpc_msg_t *msg = XPC_MSG_ADDRESS(payload); + s64 get, msg_number = msg->number; + + + XP_ASSERT(partid > 0 && partid < MAX_PARTITIONS); + XP_ASSERT(ch_number >= 0 && ch_number < part->nchannels); + + ch = &part->channels[ch_number]; + + DPRINTK(xpc_chan, XPC_DBG_C_RECEIVE, + "msg=0x%p, msg_number=%ld, partid=%d, channel=%d\n", + (void *) msg, msg_number, ch->partid, ch->number); + + XP_ASSERT((((u64) msg - (u64) ch->remote_msgqueue) / ch->msg_size) == + msg_number % ch->remote_nentries); + XP_ASSERT(!(msg->flags & XPC_M_DONE)); + + msg->flags |= XPC_M_DONE; + + /* + * The preceding store of msg->flags must occur before the following + * load of ch->local_GP->get. + */ + mb(); + + /* + * See if this message is next in line to be acknowledged as having + * been delivered. + */ + get = ch->local_GP->get; + if (get == msg_number) { + xpc_acknowledge_msgs(ch, get, msg->flags); + } + + /* the call to XPC_MSGQUEUE_REF() was done by xpc_deliver_msg() */ + XPC_MSGQUEUE_DEREF(ch); +} + Index: linux/arch/ia64/sn/kernel/xpc_dbgtk.h =================================================================== --- linux.orig/arch/ia64/sn/kernel/xpc_dbgtk.h 1969-12-31 18:00:00.000000000 -0600 +++ linux/arch/ia64/sn/kernel/xpc_dbgtk.h 2004-04-23 13:43:07.000000000 -0500 @@ -0,0 +1,115 @@ +/* + * 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. + * + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + */ + + +/* + * Cross Partition Communication (XPC)'s dbgtk related definitions. + */ + + +#ifndef _IA64_SN_KERNEL_XPC_DBGTK_H +#define _IA64_SN_KERNEL_XPC_DBGTK_H + + +//>>> #define DBGTK_USE_DPRINTK +#include + + +/* + * Define the XPC debug sets and other items used with DPRINTK for the + * partition related messages. + */ +#define XPC_DBG_P_CONSOLE 0x0000000000000001 +#define XPC_DBG_P_ERROR 0x0000000000000002 + +#define XPC_DBG_P_INIT 0x0000000000000010 +#define XPC_DBG_P_HEARTBEAT 0x0000000000000020 +#define XPC_DBG_P_DISCOVERY 0x0000000000000040 +#define XPC_DBG_P_ACT 0x0000000000000080 + +#define XPC_DBG_P_INITV 0x0000000000000100 +#define XPC_DBG_P_HEARTBEATV 0x0000000000000200 +#define XPC_DBG_P_DISCOVERYV 0x0000000000000400 +#define XPC_DBG_P_ACTV 0x0000000000000800 + + +#define XPC_DBG_P_SET_DESCRIPTION "\n" \ + "\t0x001 Console\n" \ + "\t0x002 Error\n" \ + "\t0x010 Initialization\n" \ + "\t0x020 Heartbeat related\n" \ + "\t0x040 Discovery related\n" \ + "\t0x080 Activation/Deact\n" \ + "\t0x100 Verbose Initialization\n" \ + "\t0x200 Verbose Heartbeat related\n" \ + "\t0x400 Verbose Discovery related\n" \ + "\t0x800 Verbose Activation/Deact\n" + +#define XPC_DBG_P_DEFCAPTURE_SETS (XPC_DBG_P_CONSOLE | \ + XPC_DBG_P_ERROR | \ + XPC_DBG_P_INIT | \ + XPC_DBG_P_INITV | \ + XPC_DBG_P_HEARTBEAT | \ + XPC_DBG_P_DISCOVERY | \ + XPC_DBG_P_DISCOVERYV | \ + XPC_DBG_P_ACT | \ + XPC_DBG_P_ACTV) + +#define XPC_DBG_P_DEFCONSOLE_SETS (XPC_DBG_P_CONSOLE | \ + XPC_DBG_P_ERROR) + +EXTERN_DPRINTK(xpc_part); + + + +/* + * Define the XPC debug sets and other items used with DPRINTK for the + * channel related messages. + */ + +#define XPC_DBG_C_CONSOLE 0x0000000000000001 /* console */ +#define XPC_DBG_C_ERROR 0x0000000000000002 /* error */ + +#define XPC_DBG_C_INIT 0x0000000000000004 /* XPC module load */ +#define XPC_DBG_C_EXIT 0x0000000000000008 /* XPC module unload */ +#define XPC_DBG_C_SETUP 0x0000000000000010 /* partition setup */ +#define XPC_DBG_C_TEARDOWN 0x0000000000000020 /* partition teardown */ +#define XPC_DBG_C_CONNECT 0x0000000000000040 /* channel connect */ +#define XPC_DBG_C_DISCONNECT 0x0000000000000080 /* channel disconnect */ +#define XPC_DBG_C_SEND 0x0000000000000100 /* msg send */ +#define XPC_DBG_C_RECEIVE 0x0000000000000200 /* msg receive */ +#define XPC_DBG_C_IPI 0x0000000000000400 /* IPI handling */ +#define XPC_DBG_C_KTHREAD 0x0000000000000800 /* kthread management */ + +#define XPC_DBG_C_GP 0x0000000000001000 /* Get/Put changes */ + + +#define XPC_DBG_C_DEFCAPTURE_SETS (-1ul) /* capture all sets */ +#define XPC_DBG_C_DEFCONSOLE_SETS (XPC_DBG_C_CONSOLE | XPC_DBG_C_ERROR) + /* sets sent to console */ + +#define XPC_DBG_C_SET_DESCRIPTION "\n" \ + "\t0x0001 Console\n" \ + "\t0x0002 Error\n" \ + "\t0x0004 Init\n" \ + "\t0x0008 Exit\n" \ + "\t0x0010 Setup\n" \ + "\t0x0020 Teardown\n" \ + "\t0x0040 Connect\n" \ + "\t0x0080 Disconnect\n" \ + "\t0x0100 Send\n" \ + "\t0x0200 Receive\n" \ + "\t0x0400 IPI\n" \ + "\t0x0800 Kthread\n" \ + "\t0x1000 Get/Put\n" + +EXTERN_DPRINTK(xpc_chan); + + +#endif /* _IA64_SN_KERNEL_XPC_DBGTK_H */ + Index: linux/arch/ia64/sn/kernel/xpc_kdb.c =================================================================== --- linux.orig/arch/ia64/sn/kernel/xpc_kdb.c 1969-12-31 18:00:00.000000000 -0600 +++ linux/arch/ia64/sn/kernel/xpc_kdb.c 2004-04-23 13:43:07.000000000 -0500 @@ -0,0 +1,941 @@ +/* + * 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. + * + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + */ + + +/* + * Cross Partition Communication (XPC) kdb support. + * + * This is the part of XPC that provides kdb functions for + * debugging purposes. + * + */ + + +#include +#include +#include +#ifdef CONFIG_KDB +#include +#include +#endif +#include "xpc.h" + + +#ifdef CONFIG_KDB + +static int xpc_kdb_support = 0; + + +static int +xpc_kdb_state_change(struct notifier_block *self, unsigned long command, + void *ptr) +{ + /* + * xpc_kdb_support being non-zero indicates that when our + * partition goes into kdb, it should signal to other + * partitions that they should ignore the fact that this + * partitions heartbeat is not changing. + * When the other partitions see that kdb_status is non-zero, + * they ignore the heartbeating_to_mask and heartbeat + * values of this partition + */ + if (xpc_kdb_support) { + xpc_vars->kdb_status = command; + } else { + xpc_vars->heartbeating_to_mask = 0; + xpc_vars->kdb_status = 0; + } + + if (xpc_vars->heartbeating_to_mask != 0) { + xpc_vars->heartbeat++; + } + return 0; +} + + +static int +xpc_kdb_down(int argc, const char **argv, const char **envp, + struct pt_regs *regs) +{ + xpc_vars->heartbeating_to_mask = 0; + xpc_vars->kdb_status = 0; + return 0; +} + + +/* + * Display the reserved page used by XPC. + * + * xpcrp + */ +static int +xpc_kdb_rsvd_page(int argc, const char **argv, const char **envp, + struct pt_regs *regs) +{ + xpc_rsvd_page_t *rp = (xpc_rsvd_page_t *) xpc_rsvd_page; + + + if (argc > 0) { + return KDB_ARGCOUNT; + } + + if (rp == NULL) { + kdb_printf("Reserved Page has not been initialized.\n"); + return 0; + } + + kdb_printf("xpc_rsvd_page_t @ (0x%p):\n", (void *) rp); + kdb_printf("\tSAL_signature=0x%lx\n", rp->SAL_signature); + kdb_printf("\tSAL_version=0x%lx\n", rp->SAL_version); + kdb_printf("\tpartid=%d\n", rp->partid); + kdb_printf("\tversion=0x%x %d.%d\n", rp->version, + XPC_VERSION_MAJOR(rp->version), + XPC_VERSION_MINOR(rp->version)); + kdb_printf("\tvars_pa=0x%lx\n", rp->vars_pa); + kdb_printf("\t&part_nasids=0x%p\n", (void *) &rp->part_nasids); + kdb_printf("\t&mach_nasids=0x%p\n", (void *) &rp->mach_nasids); + + return 0; +} + + +static void +xpc_kdb_print_vars_part(xpc_vars_part_t *vars_part, partid_t partid) +{ + kdb_printf("xpc_vars_part_t @ (0x%p) [partid=%d]:\n", + (void *) vars_part, partid); + kdb_printf("\tmagic=0x%lx ", vars_part->magic); + if (vars_part->magic != 0) { + kdb_printf("%s", (char *) &vars_part->magic); + } + kdb_printf("\n"); + kdb_printf("\tGPs_pa=0x%lx\n", vars_part->GPs_pa); + kdb_printf("\topenclose_args_pa=0x%lx\n", + vars_part->openclose_args_pa); + kdb_printf("\tIPI_amo_pa=0x%lx\n", vars_part->IPI_amo_pa); + kdb_printf("\tIPI_cpuid=0x%x\n", vars_part->IPI_cpuid); + kdb_printf("\tnchannels=%d\n", vars_part->nchannels); +} + + +/* + * Display XPC variables. + * + * xpcvars [ ] + * + * no partid - displays xpc_vars_t structure + * partid=0 - displays all initialized xpc_vars_part_t structures + * partid=i - displays xpc_vars_part_t structure for specified + * partition, if initialized + */ +static int +xpc_kdb_variables(int argc, const char **argv, const char **envp, + struct pt_regs *regs) +{ + int ret; + unsigned long ulong_partid; + partid_t partid; + xpc_vars_part_t *vars_part; + + + if (xpc_rsvd_page == NULL) { + kdb_printf("Reserved Page has not been initialized.\n"); + return 0; + } + XP_ASSERT(xpc_vars != NULL); + + if (argc == 0) { + + /* just display the xpc_vars_t structure */ + + kdb_printf("xpc_vars_t @ (0x%p):\n", (void *) xpc_vars); + kdb_printf("\tversion=0x%x %d.%d\n", xpc_vars->version, + XPC_VERSION_MAJOR(xpc_vars->version), + XPC_VERSION_MINOR(xpc_vars->version)); + kdb_printf("\theartbeat=%ld\n", xpc_vars->heartbeat); + kdb_printf("\theartbeating_to_mask=0x%lx", + xpc_vars->heartbeating_to_mask); + for (partid = 1; partid < MAX_PARTITIONS; partid++) { + if (XPC_HB_ALLOWED(partid, xpc_vars)) { + kdb_printf(" %d", partid); + } + } + kdb_printf("\n"); + kdb_printf("\tkdb_status=0x%lx\n", xpc_vars->kdb_status); + kdb_printf("\tvars_part_pa=0x%lx\n", xpc_vars->vars_part_pa); + kdb_printf("\tact_cpuid=0x%lx\n", xpc_vars->act_cpuid); + kdb_printf("\tamos_page_pa=0x%lx\n", xpc_vars->amos_page_pa); + kdb_printf("\tamos_page=0x%p\n", (void *) xpc_vars->amos_page); + kdb_printf("\tact_amos=0x%p\n", (void *) xpc_vars->act_amos); + return 0; + + } else if (argc != 1) { + return KDB_ARGCOUNT; + } + + ret = kdbgetularg(argv[1], (unsigned long *) &ulong_partid); + if (ret) { + return ret; + } + partid = (partid_t) ulong_partid; + if (partid < 0 || partid >= MAX_PARTITIONS) { + kdb_printf("invalid partid\n"); + return KDB_BADINT; + } + + vars_part = (xpc_vars_part_t *) __va(xpc_vars->vars_part_pa); + XP_ASSERT(vars_part != NULL); + + if (partid == 0) { + + /* display all initialized xpc_vars_part_t structure */ + + for (partid = 1; partid < MAX_PARTITIONS; partid++) { + if (vars_part[partid].magic == 0) { + continue; + } + xpc_kdb_print_vars_part(&vars_part[partid], partid); + } + + } else { + + /* display specified xpc_vars_part_t structure */ + + if (vars_part[partid].magic != 0) { + xpc_kdb_print_vars_part(&vars_part[partid], partid); + } else { + kdb_printf("xpc_vars_part_t for partid %d not " + "initialized\n", partid); + } + } + + return 0; +} + + +static void +xpc_kdb_print_IPI_flags(u64 IPI_amo) +{ + int ch_number; + u8 flags; + + + for (ch_number = 0; ch_number < XPC_NCHANNELS; ch_number++) { + + /* get the IPI flags for the specific channel */ + flags = XPC_GET_IPI_FLAGS(IPI_amo, ch_number); + if (flags == 0) { + continue; + } + + kdb_printf("\t channel_%d=0x%x", ch_number, flags); + + if (flags & XPC_IPI_MSGREQUEST) kdb_printf(" MSGREQUEST"); + if (flags & XPC_IPI_OPENREPLY) kdb_printf(" OPENREPLY"); + if (flags & XPC_IPI_OPENREQUEST) kdb_printf(" OPENREQUEST"); + if (flags & XPC_IPI_CLOSEREPLY) kdb_printf(" CLOSEREPLY"); + if (flags & XPC_IPI_CLOSEREQUEST) kdb_printf(" CLOSEREQUEST"); + + kdb_printf("\n"); + } +} + + +static void +xpc_kdb_print_part(xpc_partition_t *part, partid_t partid) +{ + kdb_printf("xpc_partitions[partid=%d] (0x%p):\n", partid, + (void *) part); + kdb_printf("\tremote_rp_pa=0x%lx\n", part->remote_rp_pa); + kdb_printf("\tremote_vars_pa=0x%lx\n", part->remote_vars_pa); + kdb_printf("\tremote_vars_part_pa=0x%lx\n", part->remote_vars_part_pa); + kdb_printf("\tlast_heartbeat=%ld\n", part->last_heartbeat); + kdb_printf("\tremote_amos_page_pa=0x%lx\n", part->remote_amos_page_pa); + kdb_printf("\tremote_act_cpuid=0x%x\n", part->remote_act_cpuid); + kdb_printf("\tact_IRQ_rcvd=%d\n", part->act_IRQ_rcvd); + kdb_printf("\tact_cpu=%d\n", part->act_cpu); + kdb_printf("\tact_state=%d", part->act_state); + switch (part->act_state) { + case XPC_P_INACTIVE: kdb_printf(" INACTIVE\n"); break; + case XPC_P_ACTIVATION_REQ: kdb_printf(" ACTIVATION_REQ\n"); break; + case XPC_P_ACTIVATING: kdb_printf(" ACTIVATING\n"); break; + case XPC_P_ACTIVE: kdb_printf(" ACTIVE\n"); break; + case XPC_P_DEACTIVATING: kdb_printf(" DEACTIVATING\n"); break; + default: kdb_printf(" unknown\n"); + } + kdb_printf("\treason=%d %s\n", part->reason, + xpc_get_ascii_reason_code(part->reason)); + kdb_printf("\treason_line=%d\n", part->reason_line); + kdb_printf("\treactivate_nasid=%d\n", part->reactivate_nasid); + + kdb_printf("\tsetup_state=%d", part->setup_state); + switch (part->setup_state) { + case XPC_P_UNSET: kdb_printf(" UNSET\n"); break; + case XPC_P_SETUP: kdb_printf(" SETUP\n"); break; + case XPC_P_WTEARDOWN: kdb_printf(" WTEARDOWN\n"); break; + case XPC_P_TORNDOWN: kdb_printf(" TORNDOWN\n"); break; + default: kdb_printf(" unknown\n"); + } + kdb_printf("\treferences=%d\n", atomic_read(&part->references)); + kdb_printf("\tnchannels=%d\n", part->nchannels); + kdb_printf("\tnchannels_active=%d\n", + atomic_read(&part->nchannels_active)); + kdb_printf("\tchannels=0x%p\n", (void *) part->channels); + kdb_printf("\tlocal_GPs=0x%p\n", (void *) part->local_GPs); + kdb_printf("\tremote_GPs=0x%p\n", (void *) part->remote_GPs); + kdb_printf("\tremote_GPs_pa=0x%lx\n", part->remote_GPs_pa); + kdb_printf("\tlocal_openclose_args=0x%p\n", + (void *) part->local_openclose_args); + kdb_printf("\tremote_openclose_args=0x%p\n", + (void *) part->remote_openclose_args); + kdb_printf("\tremote_openclose_args_pa=0x%lx\n", + part->remote_openclose_args_pa); + kdb_printf("\tremote_IPI_cpuid=0x%x\n", part->remote_IPI_cpuid); + kdb_printf("\tremote_IPI_amo_va=0x%p\n", + (void *) part->remote_IPI_amo_va); + kdb_printf("\tlocal_IPI_amo_va=0x%p\n", + (void *) part->local_IPI_amo_va); + kdb_printf("\tlocal_IPI_amo=0x%lx\n", part->local_IPI_amo); + xpc_kdb_print_IPI_flags(part->local_IPI_amo); + kdb_printf("\tIPI_owner=%s\n", part->IPI_owner); + kdb_printf("\t&dropped_IPI_timer=0x%p\n", + (void *) &part->dropped_IPI_timer); + + kdb_printf("\tchannel_mgr_requests=%d\n", atomic_read(&part-> + channel_mgr_requests)); +} + + +/* + * Display XPC partitions. + * + * xpcpart [ | ] + */ +static int +xpc_kdb_partitions(int argc, const char **argv, const char **envp, + struct pt_regs *regs) +{ + int ret; + int nextarg = 1; + long offset = 0; + unsigned long addr; + xpc_partition_t *part; + partid_t partid; + + + if (argc > 1) { + return KDB_ARGCOUNT; + + } else if (argc == 1) { + ret = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, + NULL, regs); + if (ret) { + return ret; + } + if (addr > 0 && addr < MAX_PARTITIONS) { + partid = (partid_t) addr; + part = &xpc_partitions[partid]; + } else { + part = (xpc_partition_t *) addr; + partid = part - &xpc_partitions[0]; + if (partid <= 0 || partid >= MAX_PARTITIONS || + part != &xpc_partitions[partid]) { + kdb_printf("invalid partition entry address\n"); + return KDB_BADADDR; + } + } + xpc_kdb_print_part(part, partid); + + } else { + for (partid = 1; partid < MAX_PARTITIONS; partid++) { + part = &xpc_partitions[partid]; + if (part->setup_state == XPC_P_UNSET && + part->reason == 0) { + continue; + } + xpc_kdb_print_part(part, partid); + } + } + return 0; +} + + +static void +xpc_kdb_print_channel_flags(u32 flags) +{ + kdb_printf("\tflags=0x%x", flags); + + if (flags & XPC_C_DISCONNECTING) kdb_printf(" DISCONNECTING"); + if (flags & XPC_C_DISCONNECTED) kdb_printf(" DISCONNECTED"); + + if (flags & XPC_C_CLOSEREQUEST) kdb_printf(" CLOSEREQUEST"); + if (flags & XPC_C_RCLOSEREQUEST) kdb_printf(" RCLOSEREQUEST"); + if (flags & XPC_C_CLOSEREPLY) kdb_printf(" CLOSEREPLY"); + if (flags & XPC_C_RCLOSEREPLY) kdb_printf(" RCLOSEREPLY"); + + if (flags & XPC_C_CONNECTING) kdb_printf(" CONNECTING"); + if (flags & XPC_C_CONNECTED) kdb_printf(" CONNECTED"); + if (flags & XPC_C_CONNECTCALLOUT) kdb_printf(" CONNECTCALLOUT"); + if (flags & XPC_C_SETUP) kdb_printf(" SETUP"); + + if (flags & XPC_C_OPENREQUEST) kdb_printf(" OPENREQUEST"); + if (flags & XPC_C_ROPENREQUEST) kdb_printf(" ROPENREQUEST"); + if (flags & XPC_C_OPENREPLY) kdb_printf(" OPENREPLY"); + if (flags & XPC_C_ROPENREPLY) kdb_printf(" ROPENREPLY"); + + if (flags & XPC_C_WASCONNECTED) kdb_printf(" WASCONNECTED"); + + kdb_printf("\n"); +} + + +static void +xpc_kdb_print_channel(xpc_channel_t *ch) +{ + kdb_printf("channel %d (0x%p):\n", ch->number, (void *) ch); + kdb_printf("\tpartid=%d\n", ch->partid); + + xpc_kdb_print_channel_flags(ch->flags); + + kdb_printf("\treason=%d %s\n", ch->reason, + xpc_get_ascii_reason_code(ch->reason)); + kdb_printf("\treason_line=%d\n", ch->reason_line); + kdb_printf("\tnumber=%d\n", ch->number); + kdb_printf("\tmsg_size=%d\n", ch->msg_size); + kdb_printf("\tlocal_nentries=%d\n", ch->local_nentries); + kdb_printf("\tremote_nentries=%d\n", ch->remote_nentries); + kdb_printf("\tlocal_msgqueue=0x%p\n", (void *) ch->local_msgqueue); + kdb_printf("\tremote_msgqueue_pa=0x%lx\n", ch->remote_msgqueue_pa); + kdb_printf("\tremote_msgqueue=0x%p\n", + (void *) ch->remote_msgqueue); + kdb_printf("\treferences=%d\n", atomic_read(&ch->references)); + kdb_printf("\tn_on_msg_allocate_wq=%d\n", + atomic_read(&ch->n_on_msg_allocate_wq)); + kdb_printf("\t&msg_allocate_wq=0x%p\n", + (void *) &ch->msg_allocate_wq); + kdb_printf("\tn_to_notify=%d\n", atomic_read(&ch->n_to_notify)); + kdb_printf("\tnotify_queue=0x%p\n", (void *) ch->notify_queue); + kdb_printf("\tfunc=0x%p\n", (void *) ch->func); + kdb_printf("\tkey=0x%p\n", ch->key); + kdb_printf("\t&msg_to_pull_sema=0x%p\n", + (void *) &ch->msg_to_pull_sema); + kdb_printf("\t&teardown_sema=0x%p\n", + (void *) &ch->teardown_sema); + kdb_printf("\tlocal_GP=0x%p (%ld:%ld)\n", (void *) ch->local_GP, + ch->local_GP->get, + ch->local_GP->put); + kdb_printf("\tremote_GP=%ld:%ld\n", ch->remote_GP.get, + ch->remote_GP.put); + kdb_printf("\tw_local_GP=%ld:%ld\n", ch->w_local_GP.get, + ch->w_local_GP.put); + kdb_printf("\tw_remote_GP=%ld:%ld\n", ch->w_remote_GP.get, + ch->w_remote_GP.put); + kdb_printf("\tnext_msg_to_pull=%ld\n", ch->next_msg_to_pull); + kdb_printf("\tkthreads_assigned=%d\n", + atomic_read(&ch->kthreads_assigned)); + kdb_printf("\tkthreads_assigned_limit=%d\n", + ch->kthreads_assigned_limit); + kdb_printf("\tkthreads_idle=%d\n", + atomic_read(&ch->kthreads_idle)); + kdb_printf("\tkthreads_idle_limit=%d\n", ch->kthreads_idle_limit); + kdb_printf("\tkthreads_active=%d\n", + atomic_read(&ch->kthreads_active)); + kdb_printf("\tkthreads_created=%d\n", ch->kthreads_created); + kdb_printf("\t&idle_wq=0x%p\n", (void *) &ch->idle_wq); + + if (ch->flags & XPC_C_CONNECTED) { + kdb_printf("\n\t#of local msg queue entries available =%ld\n", + ch->local_nentries - (ch->w_local_GP.put - + ch->w_remote_GP.get)); + + kdb_printf("\t#of local msgs allocated !sent =%ld\n", + ch->w_local_GP.put - ch->local_GP->put); + kdb_printf("\t#of local msgs allocated sent !ACK'd =%ld\n", + ch->local_GP->put - ch->remote_GP.get); + kdb_printf("\t#of local msgs allocated sent ACK'd !notified =" + "%ld\n", ch->remote_GP.get - ch->w_remote_GP.get); + + kdb_printf("\t#of remote msgs sent !pulled =%ld\n", + ch->w_remote_GP.put - ch->next_msg_to_pull); + kdb_printf("\t#of remote msgs sent !delivered =%ld\n", + ch->next_msg_to_pull - ch->w_local_GP.get); + kdb_printf("\t#of remote msgs sent delivered !received =%ld\n", + ch->w_local_GP.get - ch->local_GP->get); + } +} + + +/* + * Display a XPC partition's channels. + * + * xpcchan | [ ] + */ +static int +xpc_kdb_channels(int argc, const char **argv, const char **envp, + struct pt_regs *regs) +{ + int ret; + int nextarg = 1; + long offset = 0; + unsigned long addr; + partid_t partid; + xpc_partition_t *part; + int ch_number; + xpc_channel_t *ch; + + + if (argc < 1 || argc > 2) { + return KDB_ARGCOUNT; + } + + ret = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (ret) { + return ret; + } + if (addr > 0 && addr < MAX_PARTITIONS) { + partid = (partid_t) addr; + part = &xpc_partitions[partid]; + if (part->setup_state == XPC_P_UNSET) { + kdb_printf("partition is UNSET\n"); + return 0; + } + if (part->setup_state == XPC_P_TORNDOWN) { + kdb_printf("partition is TORNDOWN\n"); + return 0; + } + + if (argc == 2) { + ret = kdbgetularg(argv[2], + (unsigned long *) &ch_number); + if (ret) { + return ret; + } + if (ch_number < 0 || ch_number >= part->nchannels) { + kdb_printf("invalid channel #\n"); + return KDB_BADINT; + } + xpc_kdb_print_channel(&part->channels[ch_number]); + } else { + for (ch_number = 0; ch_number < part->nchannels; + ch_number++) { + xpc_kdb_print_channel(&part-> + channels[ch_number]); + } + } + + } else { + ch = (xpc_channel_t *) addr; + + for (partid = 1; partid < MAX_PARTITIONS; partid++) { + part = &xpc_partitions[partid]; + if (part->setup_state != XPC_P_UNSET && + part->setup_state != XPC_P_TORNDOWN && + ch >= part->channels) { + ch_number = ch - part->channels; + if (ch_number < part->nchannels && + ch == &part->channels[ch_number]) { + break; + } + } + } + if (partid == MAX_PARTITIONS) { + kdb_printf("invalid channel address\n"); + return KDB_BADADDR; + } + xpc_kdb_print_channel(ch); + } + + return 0; +} + + +static void +xpc_kdb_print_local_msgqueue(xpc_channel_t *ch) +{ + int i; + char *prefix; + xpc_msg_t *msg = ch->local_msgqueue; + s64 w_remote_GP_get = ch->w_remote_GP.get % ch->local_nentries; + s64 remote_GP_get = ch->remote_GP.get % ch->local_nentries; + s64 local_GP_put = ch->local_GP->put % ch->local_nentries; + s64 w_local_GP_put = ch->w_local_GP.put % ch->local_nentries; + + + kdb_printf("local message queue (0x%p):\n\n", (void *) msg); + + for (i = 0; i < ch->local_nentries; i++) { + kdb_printf("0x%p: flags=0x%x number=%ld", (void *) msg, + msg->flags, msg->number); + + prefix = " <--"; + + if (i == w_remote_GP_get) { + kdb_printf("%s w_remote_GP.get", prefix); + prefix = ","; + } + if (i == remote_GP_get) { + kdb_printf("%s remote_GP.get", prefix); + prefix = ","; + } + if (i == local_GP_put) { + kdb_printf("%s local_GP->put", prefix); + prefix = ","; + } + if (i == w_local_GP_put) { + kdb_printf("%s w_local_GP.put", prefix); + } + kdb_printf("\n"); + + msg = (xpc_msg_t *) ((u64) msg + ch->msg_size); + } +} + + +static void +xpc_kdb_print_remote_msgqueue(xpc_channel_t *ch) +{ + int i; + char *prefix; + xpc_msg_t *msg = ch->remote_msgqueue; + s64 local_GP_get = ch->local_GP->get % ch->remote_nentries; + s64 w_local_GP_get = ch->w_local_GP.get % ch->remote_nentries; + s64 next_msg_to_pull = ch->next_msg_to_pull % ch->remote_nentries; + s64 w_remote_GP_put = ch->w_remote_GP.put % ch->remote_nentries; + s64 remote_GP_put = ch->remote_GP.put % ch->remote_nentries; + + + kdb_printf("cached remote message queue (0x%p):\n\n", (void *) msg); + + for (i = 0; i < ch->remote_nentries; i++) { + kdb_printf("0x%p: flags=0x%x number=%ld", (void *) msg, + msg->flags, msg->number); + + prefix = " <--"; + + if (i == local_GP_get) { + kdb_printf("%s local_GP->get", prefix); + prefix = ","; + } + if (i == w_local_GP_get) { + kdb_printf("%s w_local_GP.get", prefix); + prefix = ","; + } + if (i == next_msg_to_pull) { + kdb_printf("%s next_msg_to_pull", prefix); + prefix = ","; + } + if (i == w_remote_GP_put) { + kdb_printf("%s w_remote_GP.put", prefix); + prefix = ","; + } + if (i == remote_GP_put) { + kdb_printf("%s remote_GP.put", prefix); + } + kdb_printf("\n"); + + msg = (xpc_msg_t *) ((u64) msg + ch->msg_size); + } +} + + +/* + * Display XPC specified message queue. + * + * xpcmque local|remote + */ +static int +xpc_kdb_msgqueue(int argc, const char **argv, const char **envp, + struct pt_regs *regs) +{ + int ret, ch_number; + unsigned long ulong_partid; + partid_t partid; + xpc_partition_t *part; + xpc_channel_t *ch; + + + if (argc != 3) { + return KDB_ARGCOUNT; + } + + ret = kdbgetularg(argv[1], (unsigned long *) &ulong_partid); + if (ret) { + return ret; + } + partid = (partid_t) ulong_partid; + if (partid <= 0 || partid >= MAX_PARTITIONS) { + kdb_printf("invalid partid\n"); + return KDB_BADINT; + } + + ret = kdbgetularg(argv[2], (unsigned long *) &ch_number); + if (ret) { + return ret; + } + if (ch_number < 0 || ch_number >= XPC_NCHANNELS) { + kdb_printf("invalid channel #\n"); + return KDB_BADINT; + } + + part = &xpc_partitions[partid]; + + if (part->setup_state == XPC_P_UNSET) { + kdb_printf("partition is UNSET\n"); + return 0; + } + if (part->setup_state == XPC_P_TORNDOWN) { + kdb_printf("partition is TORNDOWN\n"); + return 0; + } + + if (ch_number >= part->nchannels) { + kdb_printf("unsupported channel #\n"); + return KDB_BADINT; + } + + ch = &part->channels[ch_number]; + + if (!(ch->flags & XPC_C_SETUP)) { + kdb_printf("message queues are not SETUP\n"); + return 0; + } + + if (strcmp(argv[3], "r") == 0 || strcmp(argv[3], "remote") == 0) { + xpc_kdb_print_remote_msgqueue(ch); + } else if (strcmp(argv[3], "l") == 0 || strcmp(argv[3], "local") == 0) { + xpc_kdb_print_local_msgqueue(ch); + } else { + kdb_printf("unknown msg queue selected\n"); + return KDB_BADINT; + } + + return 0; +} + + +static void +xpc_kdb_print_msg_flags(u8 flags) +{ + kdb_printf("\tflags=0x%x", flags); + + if (flags & XPC_M_INTERRUPT) kdb_printf(" INTERRUPT"); + if (flags & XPC_M_READY) kdb_printf(" READY"); + if (flags & XPC_M_DONE) kdb_printf(" DONE"); + + kdb_printf("\n"); +} + + +/* + * Display XPC message. + * + * xpcmsg + */ +static int +xpc_kdb_msg(int argc, const char **argv, const char **envp, + struct pt_regs *regs) +{ + int ret, nextarg = argc; + long offset = 0; + unsigned long addr; + xpc_msg_t *msg; + + + if (argc != 1) { + return KDB_ARGCOUNT; + } + + ret = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (ret) { + return ret; + } + + msg = (xpc_msg_t *) addr; + kdb_printf("msg (0x%p):\n", (void *) msg); + xpc_kdb_print_msg_flags(msg->flags); + kdb_printf("\tnumber=%ld\n", msg->number); + kdb_printf("\t&payload=0x%p\n", (void *) &msg->payload); + + return 0; +} + + +static void +xpc_kdb_print_notify_queue(xpc_channel_t *ch) +{ + int i; + char *prefix; + xpc_notify_t *notify = ch->notify_queue; + s64 w_remote_GP_get = ch->w_remote_GP.get % ch->local_nentries; + s64 remote_GP_get = ch->remote_GP.get % ch->local_nentries; + s64 local_GP_put = ch->local_GP->put % ch->local_nentries; + s64 w_local_GP_put = ch->w_local_GP.put % ch->local_nentries; + + + kdb_printf("notify queue (0x%p):\n\n", (void *) notify); + + for (i = 0; i < ch->local_nentries; i++) { + kdb_printf("0x%p: type=0x%x", (void *) notify, notify->type); + + if (notify->type == XPC_N_CALL) { + kdb_printf(" CALL func=0x%p key=0x%p", + (void *) notify->func, notify->key); + } + + prefix = " <--"; + + if (i == w_remote_GP_get) { + kdb_printf("%s w_remote_GP.get", prefix); + prefix = ","; + } + if (i == remote_GP_get) { + kdb_printf("%s remote_GP.get", prefix); + prefix = ","; + } + if (i == local_GP_put) { + kdb_printf("%s local_GP->put", prefix); + prefix = ","; + } + if (i == w_local_GP_put) { + kdb_printf("%s w_local_GP.put", prefix); + } + kdb_printf("\n"); + + notify++; + } +} + + +/* + * Display XPC specified notify queue. + * + * xpcnque + */ +static int +xpc_kdb_notify_queue(int argc, const char **argv, const char **envp, + struct pt_regs *regs) +{ + int ret, ch_number; + unsigned long ulong_partid; + partid_t partid; + xpc_partition_t *part; + xpc_channel_t *ch; + + + if (argc != 2) { + return KDB_ARGCOUNT; + } + + ret = kdbgetularg(argv[1], (unsigned long *) &ulong_partid); + if (ret) { + return ret; + } + partid = (partid_t) ulong_partid; + if (partid <= 0 || partid >= MAX_PARTITIONS) { + kdb_printf("invalid partid\n"); + return KDB_BADINT; + } + + ret = kdbgetularg(argv[2], (unsigned long *) &ch_number); + if (ret) { + return ret; + } + if (ch_number < 0 || ch_number >= XPC_NCHANNELS) { + kdb_printf("invalid channel #\n"); + return KDB_BADINT; + } + + part = &xpc_partitions[partid]; + + if (part->setup_state == XPC_P_UNSET) { + kdb_printf("partition is UNSET\n"); + return 0; + } + if (part->setup_state == XPC_P_TORNDOWN) { + kdb_printf("partition is TORNDOWN\n"); + return 0; + } + + if (ch_number >= part->nchannels) { + kdb_printf("unsupported channel #\n"); + return KDB_BADINT; + } + + ch = &part->channels[ch_number]; + + if (!(ch->flags & XPC_C_SETUP)) { + kdb_printf("notify queue is not SETUP\n"); + return 0; + } + + xpc_kdb_print_notify_queue(ch); + + return 0; +} + + +static struct notifier_block xpc_kdb_notifier = + { xpc_kdb_state_change, NULL, 0 }; + +#endif /* CONFIG_KDB */ + + +void +xpc_kdb_register(void) +{ +#ifdef CONFIG_KDB + (void) notifier_chain_register(&kdb_notifier_list, &xpc_kdb_notifier); + + (void) kdb_register("xpcdown", xpc_kdb_down, "", + "Mark this partition as being down", 0); + + (void) kdb_register("xpcrp", xpc_kdb_rsvd_page, "", + "Display XPC reserved page", 0); + (void) kdb_register("xpcvars", xpc_kdb_variables, "[ ]", + "Display XPC variables", 0); + (void) kdb_register("xpcpart", xpc_kdb_partitions, "[ | " + " ]", "Display xpc_partition_t entries", 0); + (void) kdb_register("xpcchan", xpc_kdb_channels, " | " + "[ ]", "Display xpc_channel_t entries", 0); + (void) kdb_register("xpcmque", xpc_kdb_msgqueue, " " + "local|remote", "Display local or remote msg queue", 0); + (void) kdb_register("xpcmsg", xpc_kdb_msg, "", + "Display xpc_msg_t", 0); + (void) kdb_register("xpcnque", xpc_kdb_notify_queue, " " + "", "Display notify queue", 0); +#endif /* CONFIG_KDB */ +} + + +void +xpc_kdb_unregister(void) +{ +#ifdef CONFIG_KDB + (void) notifier_chain_unregister(&kdb_notifier_list, &xpc_kdb_notifier); + + (void) kdb_unregister("xpcdown"); + + (void) kdb_unregister("xpcrp"); + (void) kdb_unregister("xpcvars"); + (void) kdb_unregister("xpcpart"); + (void) kdb_unregister("xpcchan"); + (void) kdb_unregister("xpcmque"); + (void) kdb_unregister("xpcmsg"); + (void) kdb_unregister("xpcnque"); +#endif /* CONFIG_KDB */ +} + + +#ifdef CONFIG_KDB + +MODULE_PARM(xpc_kdb_support, "1i"); +MODULE_PARM_DESC(xpc_kdb_support, "Should dropping into kdb continue " + "heartbeating to other partitions."); + +#endif /* CONFIG_KDB */ + Index: linux/arch/ia64/sn/kernel/xpc_main.c =================================================================== --- linux.orig/arch/ia64/sn/kernel/xpc_main.c 1969-12-31 18:00:00.000000000 -0600 +++ linux/arch/ia64/sn/kernel/xpc_main.c 2004-04-23 13:43:07.000000000 -0500 @@ -0,0 +1,1285 @@ +/* + * 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. + * + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + */ + + +/* + * 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 (XPMEM and XPNET). + * + * + * 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 + * a IPI. The AMO indicates where data is to be pulled from, so + * after the IPI arrives, the remote partition checks the AMO words. + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define __KERNEL_SYSCALLS__ /* needed for waitpid() */ +#include /* needed for waitpid() */ +#include "xpc.h" + + +/* once Linux 2.4 is no longer supported, eliminate these gyrations */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) && \ + LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + +#define IRQ_HANDLED + +#define THREAD_INFO task_struct +#define CURRENT_THREAD_INFO() current + +#define NASID_SLICE_TO_CPUID_ERROR smp_num_cpus + +#define sys_sched_setscheduler sched_setscheduler +static inline _syscall3(long, sched_setscheduler, pid_t, pid, + int, policy, struct sched_param *, param); + +#define DAEMONIZE(name...) { daemonize(); sprintf(current->comm, name); } +#define DEQUEUE_SIGNAL(task, mask, info) dequeue_signal(mask, info) + +#define cpumask_of_cpu(cpu) \ + ({ \ + cpumask_t __cpu_mask = CPU_MASK_NONE; \ + __set_bit(cpu, &__cpu_mask); \ + &__cpu_mask; \ + }) +#define cpumask_of_allcpus() \ + ({ \ + cpumask_t __cpu_mask = CPU_MASK_ALL; \ + &__cpu_mask; \ + }) + +#else /* LINUX_VERSION_CODE == Linux 2.4 */ + +#include + +#define EXPORT_NO_SYMBOLS + +#define THREAD_INFO thread_info +#define CURRENT_THREAD_INFO current_thread_info + +#define NASID_SLICE_TO_CPUID_ERROR NR_CPUS + +#define DAEMONIZE(name...) daemonize(name) +#define DEQUEUE_SIGNAL(task, mask, info) dequeue_signal(task, mask, info) + +#define set_migratable_thread() + +#define cpumask_of_allcpus() \ + ({ \ + cpumask_t __cpu_mask = CPU_MASK_ALL; \ + __cpu_mask; \ + }) + +#endif /* LINUX_VERSION_CODE == Linux 2.4 */ + + +/* define two DPRINTK dbgtk message buffers */ + +DECLARE_DPRINTK(xpc_part, 1000, XPC_DBG_P_DEFCAPTURE_SETS, + XPC_DBG_P_DEFCONSOLE_SETS, XPC_DBG_P_SET_DESCRIPTION); + +DECLARE_DPRINTK(xpc_chan, 2000, XPC_DBG_C_DEFCAPTURE_SETS, + XPC_DBG_C_DEFCONSOLE_SETS, XPC_DBG_C_SET_DESCRIPTION); + + +/* systune related variables for /proc/sys directories */ + +static int xpc_hb_min = 1; +static int xpc_hb_max = 10; + +static int xpc_hb_check_min = 10; +static int xpc_hb_check_max = 120; + +static ctl_table xpc_sys_xpc_hb_dir[] = { + { + 1, + "hb_interval", + &xpc_hb_interval, + sizeof(int), + 0644, + NULL, + &proc_dointvec_minmax, + &sysctl_intvec, + NULL, + &xpc_hb_min, &xpc_hb_max + }, + { + 2, + "hb_check_interval", + &xpc_hb_check_interval, + sizeof(int), + 0644, + NULL, + &proc_dointvec_minmax, + &sysctl_intvec, + NULL, + &xpc_hb_check_min, &xpc_hb_check_max + }, + {0} +}; +static ctl_table xpc_sys_xpc_dir[] = { + { + 1, + "hb", + NULL, + 0, + 0555, + xpc_sys_xpc_hb_dir + }, + {0} +}; +static ctl_table xpc_sys_dir[] = { + { + 1, + "xpc", + NULL, + 0, + 0555, + xpc_sys_xpc_dir + }, + {0} +}; +static struct ctl_table_header *xpc_sysctl; + + +/* #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); + +volatile static unsigned long xpc_hb_check_timeout; + +/* xpc_hb_checker thread exited notification */ +static DECLARE_MUTEX_LOCKED(xpc_hb_checker_exited); + +/* xpc_discovery thread exited notification */ +static DECLARE_MUTEX_LOCKED(xpc_discovery_exited); + + +static struct timer_list xpc_hb_timer; + + +static void xpc_kthread_waitmsgs(xpc_partition_t *, xpc_channel_t *); + + +// >>> XPC is going to try to keep all activity for a given partition +// >>> constrained to a single CPU. And if we allocate all structures for +// >>> that partition from that CPU we will have memory affinity. We should +// >>> minimize the pulling of cachelines from one CPU to another. +// >>> +// >>> This will require that heartbeat portion of XPC create a partition/CPU +// >>> mapping for each of the other partitions with respect to CPUs in this +// >>> partition. For performance it would be nice if each partition could get +// >>> a unique CPU, but the enabled hardware may not allow for that. The +// >>> heartbeat code will ensure that when it calls xpc_partition_up() for a +// >>> given partition that it is running on the CPU mapped to that partition. +// >>> This will allow XPC to allocate data structures for that partition that +// >>> will always have affinity when subsequently referenced from that same +// >>> CPU. When XPC interrupts another partition it will interrupt the +// >>> mapped CPU. We will need to decide whether the kernel threads that +// >>> the IRQ handler will wakeup should be pinned to the same CPU as the IRQ +// >>> handler. +// >>> +// >>> This scheme will allow XPC to run in parallel for different partitions, +// >>> but all channels within a partition will be serialized. Is this a good +// >>> or a bad thing? + + +// >>> Make sure that all waits/sleeps/blocks are interruptible! + + +/* + * Notify the heartbeat check thread that an IRQ has been received. + */ +static irqreturn_t +xpc_act_IRQ_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + 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++; + + if (XPC_TICKS >= xpc_hb_check_timeout) { + wake_up_interruptible(&xpc_act_IRQ_wq); + } + + xpc_hb_timer.expires = XPC_TICKS + + (xpc_hb_interval * XPC_TICKS_PER_SEC); + 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_user_nice(current, 19); + + spin_lock_irq(¤t->sighand->siglock); + /* + * The following allows SIGCHLD to be delivered. Without it, + * SIGCHLD gets dropped on the floor. + */ + current->sighand->action[SIGCHLD-1].sa.sa_handler = SIG_IGN; + siginitsetinv(¤t->blocked, sigmask(SIGCHLD) | sigmask(SIGHUP)); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + + set_cpus_allowed(current, cpumask_of_cpu(XPC_HB_CHECK_CPU)); + + xpc_hb_check_timeout = XPC_TICKS + + (xpc_hb_check_interval * XPC_TICKS_PER_SEC); + + while (!xpc_exiting) { + + /* wait for IRQ or timeout */ + + (void) wait_event_interruptible(xpc_act_IRQ_wq, + (last_IRQ_count < atomic_read(&xpc_act_IRQ_rcvd) || + XPC_TICKS >= xpc_hb_check_timeout || + xpc_exiting)); + + DPRINTK(xpc_part, XPC_DBG_P_HEARTBEATV, + "woke up with %d ticks rem; %d IRQs have been " + "received\n", (int) (xpc_hb_check_timeout - XPC_TICKS), + atomic_read(&xpc_act_IRQ_rcvd) - last_IRQ_count); + + + while (signal_pending(current)) { + unsigned long sig; + siginfo_t sig_info; + + spin_lock_irq(¤t->sighand->siglock); + sig = DEQUEUE_SIGNAL(current, ¤t->blocked, + &sig_info); + spin_unlock_irq(¤t->sighand->siglock); + + DPRINTK(xpc_part, XPC_DBG_P_HEARTBEAT, + "received signal %lu\n", sig); + + if (sig == SIGCHLD) { + while((waitpid(-1, NULL, WNOHANG)) > 0) { + /* empty */ + } + + } + /* SIGHUP is ignored, all other signals are blocked */ + } + + + /* checking of remote heartbeats is skewed by IRQ handling */ + if (XPC_TICKS >= xpc_hb_check_timeout) { + DPRINTK(xpc_part, XPC_DBG_P_HEARTBEATV, + "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; + } + + + new_IRQ_count = atomic_read(&xpc_act_IRQ_rcvd); + if (last_IRQ_count < new_IRQ_count || force_IRQ != 0) { + force_IRQ = 0; + + DPRINTK(xpc_part, XPC_DBG_P_HEARTBEATV, + "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 = XPC_TICKS + + (xpc_hb_check_interval * XPC_TICKS_PER_SEC); + } + } + + DPRINTK(xpc_part, XPC_DBG_P_HEARTBEAT, + "heartbeat checker is exiting\n"); + + + /* mark this thread as inactive */ + up(&xpc_hb_checker_exited); + 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(); + + DPRINTK(xpc_part, XPC_DBG_P_DISCOVERY, + "discovery thread is exiting\n"); + + /* mark this thread as inactive */ + up(&xpc_discovery_exited); + return 0; +} + + +/* + * Orders cpus and attempts a round-robin assignment of cpus. Each + * time it is called, it will give the next in the order. + * + * Ordering starts at compact node ID 1 (second node in the + * partition) and grabs the second cpu on that node. Next is the + * second cpu on cnode 2. This continues for all cnodes and then + * wraps to the first cpu for each cnode. + * + * There seems to be some inconsistency in the meaning and usage of + * the word 'slice'. Some places equate slice with CPU, others seem to + * equate it with the Front Side Bus slot. There are two CPUs (numbered 0 & 1) + * and two FSB slots (numbered 0 & 2) per nasid. The function + * nasid_slice_to_cpuid() equates slice to the FSB slot. + */ +static int +xpc_next_cpu(void) +{ + static int last_cnodeid = 0; + static int last_slice = 2; + int next_cpu; + nasid_t nasid; + + + next_cpu = -1; + while (next_cpu == -1) { + if (++last_cnodeid >= numnodes) { + last_cnodeid = 0; + last_slice = (last_slice + 2) % 4; + } + + /* skip headless nodes */ + if (is_headless_node(last_cnodeid)) { + continue; + } + + nasid = cnodeid_to_nasid(last_cnodeid); + next_cpu = nasid_slice_to_cpuid(nasid, last_slice); + if (next_cpu == NASID_SLICE_TO_CPUID_ERROR) { + next_cpu = -1; + } + } + return next_cpu; +} + + +/* + * 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 xpc_t +xpc_make_first_contact(xpc_partition_t *part) +{ + xpc_t ret; + + + while ((ret = xpc_pull_remote_vars_part(part)) != xpcSuccess) { + if (ret != xpcRetry) { + XPC_DEACTIVATE_PARTITION(part, ret); + return ret; + } + + DPRINTK(xpc_chan, XPC_DBG_C_SETUP, + "waiting to make first contact with partition %d\n", + XPC_PARTID(part)); + + /* wait a 1/4 of a second or so */ + set_current_state(TASK_INTERRUPTIBLE); + (void) schedule_timeout(0.25 * HZ); + + 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(xpc_partition_t *part) +{ + unsigned long sig; + siginfo_t sig_info; + + + while (part->act_state != XPC_P_DEACTIVATING || + atomic_read(&part->nchannels_active) > 0) { + + 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 || + part->local_IPI_amo != 0 || + (part->act_state == XPC_P_DEACTIVATING && + atomic_read(&part->nchannels_active) == 0))); + 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? + + + /* + * Reap any children that may have exited. + * >>> Re-package the following into a common function that + * >>> both xpc_partition and xpc_channel can use? + */ + while (signal_pending(current)) { + + spin_lock_irq(¤t->sighand->siglock); + sig = DEQUEUE_SIGNAL(current, ¤t->blocked, + &sig_info); + spin_unlock_irq(¤t->sighand->siglock); + + DPRINTK(xpc_chan, XPC_DBG_C_KTHREAD, + "signal_pending, sig=%ld\n", sig); + + // >>> at the moment we're ignoring all but SIGCHLD + + if (sig == SIGCHLD) { + while (waitpid(-1, NULL, WNOHANG) > 0) { + /* nothing more to do */ + } + } + } + } +} + + +/* + * 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(xpc_partition_t *part) +{ + XP_ASSERT(part->channels == NULL); + + DPRINTK(xpc_chan, XPC_DBG_C_SETUP, + "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. + */ + + /* allow the channel mgr and its children to run on any CPU */ + set_migratable_thread(); + set_cpus_allowed(current, cpumask_of_allcpus()); + + (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; + xpc_partition_t *part = &xpc_partitions[partid]; + unsigned long irq_flags; + struct sched_param param = { sched_priority: MAX_USER_RT_PRIO - 1 }; + mm_segment_t saved_addr_limit; + struct THREAD_INFO *thread_info = CURRENT_THREAD_INFO(); + + + XP_ASSERT(partid > 0 && partid < MAX_PARTITIONS); + + + /* indicate the thread is activating */ + + 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; + } + + XP_ASSERT(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); + + + part->act_cpu = xpc_next_cpu(); + DPRINTK(xpc_part, XPC_DBG_P_ACT, + "bringing partition %d up; pinning to %d\n", + partid, part->act_cpu); + + XP_ASSERT(cpu_online(part->act_cpu)); + + DAEMONIZE("xpc%02d", partid); + + /* + * The XPC tasks need to become realtime tasks to prevent a significant + * performance degradation. We will use the setscheduler system call + * for this. + * + * The changing of addr_limit to KERNEL_DS is necessary to ensure the + * kernel sched_param (¶m) is recognized as being from kernel + * address space and not user. + */ + memcpy(&saved_addr_limit, &thread_info->addr_limit, + sizeof(mm_segment_t)); + thread_info->addr_limit.seg = KERNEL_DS.seg; + sys_sched_setscheduler(current->pid, SCHED_FIFO, ¶m); + memcpy(&thread_info->addr_limit, &saved_addr_limit, + sizeof(mm_segment_t)); + + set_cpus_allowed(current, cpumask_of_cpu(part->act_cpu)); + + /* + * 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) { + DPRINTK_ALWAYS(xpc_part, XPC_DBG_P_ACT, + "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; + } + + XPC_ALLOW_HB(partid, xpc_vars); + 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); + + 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(xpc_partition_t *part) +{ + partid_t partid = XPC_PARTID(part); + unsigned long irq_flags; + pid_t pid; + + + spin_lock_irqsave(&part->act_lock, irq_flags); + + pid = kernel_thread(xpc_activating, (void *) ((u64) partid), SIGCHLD); + + XP_ASSERT(part->act_state == XPC_P_INACTIVE); + + if (pid > 0) { + part->act_state = XPC_P_ACTIVATION_REQ; + XPC_SET_REASON(part, xpcCloneKThread, __LINE__); + } else { + XPC_SET_REASON(part, xpcCloneKThreadFailed, __LINE__); + } + + spin_unlock_irqrestore(&part->act_lock, irq_flags); +} + + +/* + * 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. + * + * regs - processor's context before the processor entered + * interrupt code. NOT USED. + */ +irqreturn_t +xpc_notify_IRQ_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + partid_t partid = (partid_t) (u64) dev_id; + xpc_partition_t *part = &xpc_partitions[partid]; + + + XP_ASSERT(partid > 0 && partid < 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(xpc_partition_t *part) +{ + if (XPC_PART_REF(part)) { + xpc_check_for_channel_activity(part); + + part->dropped_IPI_timer.expires = XPC_TICKS + + XPC_P_DROPPED_IPI_WAIT; + add_timer(&part->dropped_IPI_timer); + XPC_PART_DEREF(part); + } +} + + +void +xpc_activate_kthreads(xpc_channel_t *ch, int needed) +{ + int idle = atomic_read(&ch->kthreads_idle); + int assigned = atomic_read(&ch->kthreads_assigned); + int wakeup; + + + XP_ASSERT(needed > 0); + + if (idle > 0) { + wakeup = (needed > idle) ? idle : needed; + needed -= wakeup; + + DPRINTK(xpc_chan, XPC_DBG_C_KTHREAD, + "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; + } + } + + DPRINTK(xpc_chan, XPC_DBG_C_KTHREAD, + "create %d new kthreads, partid=%d, channel=%d\n", + needed, ch->partid, ch->number); + + xpc_create_kthreads(ch, needed); +} + + +/* + * This function is where XPC's kthreads wait for messages to deliver. + */ +static void +xpc_kthread_waitmsgs(xpc_partition_t *part, xpc_channel_t *ch) +{ + do { + /* deliver messages to their intended recipients */ + + while (ch->w_local_GP.get < ch->w_remote_GP.put && + !(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; + } + + DPRINTK(xpc_chan, XPC_DBG_C_IPI, + "idle kthread calling " + "wait_event_exclusive_interruptible()\n"); + + (void) wait_event_exclusive_interruptible(ch->idle_wq, + (ch->w_local_GP.get < + ch->w_remote_GP.put || + (ch->flags & XPC_C_DISCONNECTING))); + + atomic_dec(&ch->kthreads_idle); + + } while (!(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); + xpc_partition_t *part = &xpc_partitions[partid]; + xpc_channel_t *ch; + int n_needed; + + + DAEMONIZE("xpc%02dc%d", partid, ch_number); + + DPRINTK(xpc_chan, XPC_DBG_C_KTHREAD, + "kthread starting, partid=%d, channel=%d\n", partid, ch_number); + + ch = &part->channels[ch_number]; + + if (!(ch->flags & XPC_C_DISCONNECTING)) { + XP_ASSERT(ch->flags & XPC_C_CONNECTED); + + /* let registerer know that connection has been established */ + + if (atomic_read(&ch->kthreads_assigned) == 1) { + xpc_connected_callout(ch); + + /* + * 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); + } + } + + xpc_kthread_waitmsgs(part, ch); + } + + if (atomic_dec_return(&ch->kthreads_assigned) == 0 && + ((ch->flags & XPC_C_CONNECTCALLOUT) || + (ch->reason != xpcUnregistering && + ch->reason != xpcOtherUnregistering))) { + xpc_disconnected_callout(ch); + } + + + XPC_MSGQUEUE_DEREF(ch); + + DPRINTK(xpc_chan, XPC_DBG_C_KTHREAD, + "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 +xpc_create_kthreads(xpc_channel_t *ch, int needed) +{ + unsigned long irq_flags; + pid_t pid; + xpc_args_t args = XPC_PACK_ARGS(ch->partid, ch->number); + + + while (needed-- > 0) { + pid = kernel_thread(xpc_daemonize_kthread, + (void *) args, SIGCHLD); + if (pid < 0) { + /* the fork failed */ + + 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. + * + * No XPC_MSGQUEUE_REF() is needed here since + * the channel mgr is doing this. + */ + spin_lock_irqsave(&ch->lock, irq_flags); + XPC_DISCONNECT_CHANNEL(ch, xpcLackOfResources, + &irq_flags); + spin_unlock_irqrestore(&ch->lock, irq_flags); + } + break; + } + + /* + * 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. + */ + (void) XPC_PART_REF(&xpc_partitions[ch->partid]); + XPC_MSGQUEUE_REF(ch); + atomic_inc(&ch->kthreads_assigned); + ch->kthreads_created++; // >>> temporary debug only!!! + } +} + + +void +xpc_disconnect_wait(int ch_number) +{ + partid_t partid; + xpc_partition_t *part; + xpc_channel_t *ch; + + + /* now wait for all callouts to the caller's function to cease */ + for (partid = 1; partid < MAX_PARTITIONS; partid++) { + part = &xpc_partitions[partid]; + + if (XPC_PART_REF(part)) { + ch = &part->channels[ch_number]; + +// >>> how do we keep from falling into the window between our check and going +// >>> down and coming back up where sema is re-inited? + if (ch->flags & XPC_C_SETUP) { + (void) down(&ch->teardown_sema); + } + + XPC_PART_DEREF(part); + } + } +} + + +void __exit +xpc_exit(void) +{ + partid_t partid; + int active_part_count; + xpc_partition_t *part; + + + /* now it's time to eliminate our heartbeat */ + del_timer_sync(&xpc_hb_timer); + xpc_vars->heartbeating_to_mask = 0; + + /* indicate to others that our reserved page is uninitialized */ + xpc_rsvd_page->vars_pa = 0; + + /* + * Ignore all incoming interrupts. Without interupts the heartbeat + * checker won't activate any new partitions that may come up. + */ + free_irq(SGI_XPC_ACTIVATE, NULL); + + /* + * Cause the heartbeat checker and the discovery threads to exit. + * We don't want them attempting to activate new partitions as we + * try to deactivate the existing ones. + */ + xpc_exiting = 1; + wake_up_interruptible(&xpc_act_IRQ_wq); + + /* wait for the heartbeat checker thread to mark itself inactive */ + down(&xpc_hb_checker_exited); + + /* wait for the discovery thread to mark itself inactive */ + down(&xpc_discovery_exited); + + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(0.3 * HZ); + set_current_state(TASK_RUNNING); + + + /* wait for all partitions to become inactive */ + + do { + active_part_count = 0; + + for (partid = 1; partid < MAX_PARTITIONS; partid++) { + part = &xpc_partitions[partid]; + if (part->act_state != XPC_P_INACTIVE) { + active_part_count++; + + XPC_DEACTIVATE_PARTITION(part, xpcUnloading); + } + } + + if (active_part_count) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(0.3 * HZ); + set_current_state(TASK_RUNNING); + } + + } while (active_part_count > 0); + + + /* 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); + } + + XPC_KDB_UNREGISTER(); + UNREG_DPRINTK(xpc_chan); + UNREG_DPRINTK(xpc_part); +} +module_exit(xpc_exit); + + +int __init +xpc_init(void) +{ + int ret; + partid_t partid; + xpc_partition_t *part; + pid_t pid; + + + XP_ASSERT(L1_CACHE_ALIGNED(xpc_remote_copy_buffer)); + XP_ASSERT(XPC_VARS_ALIGNED_SIZE <= XPC_RSVD_PAGE_ALIGNED_SIZE); + + REG_DPRINTK(xpc_part); + REG_DPRINTK(xpc_chan); + XPC_KDB_REGISTER(); + + xpc_sysctl = register_sysctl_table(xpc_sys_dir, 1); + + /* + * 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 < MAX_PARTITIONS; partid++) { + part = &xpc_partitions[partid]; + + XP_ASSERT(L1_CACHE_ALIGNED(part)); + + part->act_IRQ_rcvd = 0; + spin_lock_init(&part->act_lock); + part->act_state = XPC_P_INACTIVE; + XPC_SET_REASON(part, 0, 0); + 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) { + DPRINTK_ALWAYS(xpc_part, (XPC_DBG_P_INIT | XPC_DBG_P_ERROR), + KERN_ERR "XPC: can't register ACTIVATE IRQ handler, " + "errno=%d\n", -ret); + + xpc_restrict_IPI_ops(); + + if (xpc_sysctl) { + unregister_sysctl_table(xpc_sysctl); + } + + XPC_KDB_UNREGISTER(); + UNREG_DPRINTK(xpc_chan); + UNREG_DPRINTK(xpc_part); + 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) { + DPRINTK_ALWAYS(xpc_part, (XPC_DBG_P_INIT | XPC_DBG_P_ERROR), + KERN_ERR "XPC: SAL could not locate a reserved page\n"); + + free_irq(SGI_XPC_ACTIVATE, NULL); + xpc_restrict_IPI_ops(); + + if (xpc_sysctl) { + unregister_sysctl_table(xpc_sysctl); + } + + XPC_KDB_UNREGISTER(); + UNREG_DPRINTK(xpc_chan); + UNREG_DPRINTK(xpc_part); + return -EBUSY; + } + + + /* + * Set the beating to other partitions into motion. This is + * the last requirement for other partitions' discovery to + * initiate communications with us. + */ + init_timer(&xpc_hb_timer); + xpc_hb_timer.function = xpc_hb_beater; + xpc_hb_beater(0); + + + /* + * The real work-horse behind xpc. This processes incoming + * interrupts and monitors remote heartbeats. + */ + pid = kernel_thread(xpc_hb_checker, NULL, SIGCHLD); + if (pid < 0) { + DPRINTK_ALWAYS(xpc_part, (XPC_DBG_P_INIT | XPC_DBG_P_ERROR), + KERN_ERR "XPC: failed while forking hb check thread\n"); + + /* indicate to others that our reserved page is uninitialized */ + xpc_rsvd_page->vars_pa = 0; + + del_timer_sync(&xpc_hb_timer); + free_irq(SGI_XPC_ACTIVATE, NULL); + xpc_restrict_IPI_ops(); + + if (xpc_sysctl) { + unregister_sysctl_table(xpc_sysctl); + } + + XPC_KDB_UNREGISTER(); + UNREG_DPRINTK(xpc_chan); + UNREG_DPRINTK(xpc_part); + 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, SIGCHLD); + if (pid < 0) { + DPRINTK_ALWAYS(xpc_part, (XPC_DBG_P_INIT | XPC_DBG_P_ERROR), + KERN_ERR "XPC: failed while forking discovery thread\n"); + + /* mark this new thread as a non-starter */ + up(&xpc_discovery_exited); + + xpc_exit(); + return -EBUSY; + } + + + /* set the interface to point at XPC's functions */ + xpc_set_interface(xpc_initiate_connect, xpc_initiate_disconnect, + xpc_allocate, xpc_send, xpc_send_notify, xpc_received, + xpc_partid_to_nasids); + + return 0; +} +module_init(xpc_init); + + +MODULE_AUTHOR("Silicon Graphics, Inc."); +MODULE_DESCRIPTION("Cross Partition Communication (XPC) support"); +MODULE_LICENSE("GPL"); + +MODULE_PARM(xpc_hb_interval, "1i"); +MODULE_PARM_DESC(xpc_hb_interval, "Number of seconds between " + "heartbeat increments."); + +MODULE_PARM(xpc_hb_check_interval, "1i"); +MODULE_PARM_DESC(xpc_hb_check_interval, "Number of seconds between " + "heartbeat checks."); + +EXPORT_NO_SYMBOLS; + Index: linux/arch/ia64/sn/kernel/xpc_partition.c =================================================================== --- linux.orig/arch/ia64/sn/kernel/xpc_partition.c 1969-12-31 18:00:00.000000000 -0600 +++ linux/arch/ia64/sn/kernel/xpc_partition.c 2004-04-23 13:43:07.000000000 -0500 @@ -0,0 +1,933 @@ +/* + * 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. + * + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + */ + + +/* + * Cross Partition Communication (XPC) partition support. + * + * This is the part of XPC that detects the presence/absence of + * other partitions. It provides a heartbeat and monitors the + * heartbeats of other partitions. + * + */ + + +#ifndef SN_PROM +#define __KERNEL_SYSCALLS__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else /* ! SN_PROM */ +#include +#endif /* ! SN_PROM */ +#include "xpc.h" + + +/* + * Since the BIST collides with memory operations on SHUB 1.1 + * sn_change_memprotect() cannot be used. + */ +#define DQLP_ADDR(_x) ((u64 *) GLOBAL_MMR_ADDR(cnodeid_to_nasid(_x), \ + SH_MD_DQLP_MMR_DIR_PRIVEC0)) +#define DQRP_ADDR(_x) ((u64 *) GLOBAL_MMR_ADDR(cnodeid_to_nasid(_x), \ + SH_MD_DQRP_MMR_DIR_PRIVEC0)) + + +/* XPC is exiting flag */ +volatile int xpc_exiting = 0; + + +/* SH_IPI_ACCESS shub register value on startup */ +static u64 xpc_sh_ipi_access = 0; + + +/* original protection values for each node */ +u64 xpc_prot_vec[MAX_COMPACT_NODES]; + + +/* this partition's reserved page */ +volatile xpc_rsvd_page_t *xpc_rsvd_page; + +/* this partition's XPC variables (within the reserved page) */ +xpc_vars_t *xpc_vars; +xpc_vars_part_t *xpc_vars_part; + + +/* + * For performance reasons, each entry of xpc_partitions[] is cacheline + * aligned. And xpc_partitions[] is padded with an additional entry at the + * end so that the last legitimate entry doesn't share its cacheline with + * another variable. + */ +xpc_partition_t xpc_partitions[MAX_PARTITIONS + 1]; + + +/* + * Generic buffer used to store a local copy of the remote partitions + * reserved page or XPC variables. + * + * xpc_discovery runs only once and is a seperate thread that is + * very likely going to be processing in parallel with receiving + * interrupts. + */ +char ____cacheline_aligned + xpc_remote_copy_buffer[XPC_RSVD_PAGE_ALIGNED_SIZE]; + + +/* systune related variables */ +int xpc_hb_interval = XPC_HB_DEFAULT_INTERVAL; +int xpc_hb_check_interval = XPC_HB_CHECK_DEFAULT_TIMEOUT; + + +/* + * Given a nasid, get the physical address of the partition's reserved page + * for that nasid. This function returns 0 on any error. + */ +static u64 +xpc_get_rsvd_page_pa(nasid_t nasid, u64 *buf, u64 buf_size) +{ + volatile struct ia64_sal_retval sret; + bte_result_t bte_res; + + + sret.v0 = 0; /* cookie */ + sret.v1 = nasid; /* remote address, for first call it's nasid */ + sret.v2 = 0; /* length */ + + while (1) { + + SAL_CALL(sret, SN_SAL_GET_PARTITION_ADDR, sret.v0, sret.v1, + (u64) buf, sret.v2, 0, 0, 0); + + DPRINTK(xpc_part, (XPC_DBG_P_INITV | XPC_DBG_P_ACTV), + "SAL returned with status=%li, " + "cookie=0x%016lx, address=0x%016lx, len=0x%016lx\n", + sret.status, sret.v0, sret.v1, sret.v2); + + if (sret.status != SALRET_MORE_PASSES) { + break; + } + + XP_ASSERT_ALWAYS(buf_size >= sret.v2); + + bte_res = xp_bte_copy(sret.v1, ia64_tpa((__u64) buf), buf_size, + (BTE_NOTIFY | BTE_WACQUIRE), NULL); + if (bte_res != BTE_SUCCESS) { + DPRINTK(xpc_part, (XPC_DBG_P_INITV | XPC_DBG_P_ACTV), + "xp_bte_copy failed %i\n", bte_res); + + sret.status = SALRET_ERROR; + break; + } + } + + DPRINTK(xpc_part, (XPC_DBG_P_INITV | XPC_DBG_P_ACTV), + "reserved page at phys address 0x%016lx\n", + ((sret.status == SALRET_OK) ? sret.v1 : 0UL)); + + return ((sret.status == SALRET_OK) ? sret.v1 : 0UL); +} + + +/* + * Fill the partition reserved page with the information needed by + * other partitions to discover we are alive and establish initial + * communications. + */ +xpc_rsvd_page_t * +xpc_rsvd_page_init(void) +{ + xpc_rsvd_page_t *rp; + AMO_t *amos_page; + u64 next_cl, nasid_array = 0; + int i, ret; + + + /* get the local reserved page's address */ + + rp = (xpc_rsvd_page_t *) xpc_get_rsvd_page_pa(cnodeid_to_nasid(0), + (u64 *) xpc_remote_copy_buffer, + XPC_RSVD_PAGE_ALIGNED_SIZE); + if (rp == NULL) { + DPRINTK(xpc_part, XPC_DBG_P_INIT, + "failed to locate the reserved page\n"); + return rp; + } + rp = __va(rp); + + XP_ASSERT_ALWAYS(rp->partid == sn_local_partid()); + + rp->version = XPC_RP_VERSION; + + /* + * Place the XPC variables on the cache line following the + * reserved page structure. + */ + next_cl = (u64) rp + XPC_RSVD_PAGE_ALIGNED_SIZE; + xpc_vars = (xpc_vars_t *) next_cl; + + /* + * Before clearing xpc_vars, see if a page of AMOs had been previously + * allocated. If not we'll need to allocate one and set permissions + * so that cross-partition AMOs are allowed. + * + * The allocated AMO page needs MCA reporting to remain disabled after + * XPC has unloaded. To make this work, we keep a copy of the pointer + * to this page (i.e., amos_page) in the xpc_vars_t structure, which + * is pointed to by the reserved page, and re-use that saved copy on + * subsequent loads of XPC. This AMO page is never freed, and its + * memory protections are never restricted. + */ + if ((amos_page = xpc_vars->amos_page) == NULL) { + amos_page = (AMO_t *) fetchop_kalloc_page(0); + if (amos_page == NULL) { + DPRINTK_ALWAYS(xpc_part, + (XPC_DBG_P_INIT | XPC_DBG_P_ERROR), + KERN_ERR "XPC: can't allocate fetchop page\n"); + return NULL; + } + + /* + * Open up AMO-R/W to cpu. This is done for Shub 1.1 systems + * when xpc_allow_IPI_ops() is called via xpc_hb_init(). + */ + if (!enable_shub_wars_1_1()) { + ret = sn_change_memprotect(ia64_tpa((__u64) amos_page), + PAGE_SIZE, SN_MEMPROT_ACCESS_CLASS_1, + &nasid_array); + if (ret != 0) { + DPRINTK_ALWAYS(xpc_part, + (XPC_DBG_P_INIT | XPC_DBG_P_ERROR), + KERN_ERR "XPC: can't change memory " + "protections\n"); + fetchop_kfree_page((unsigned long) amos_page); + return NULL; + } + } + } + + memset(xpc_vars, 0, sizeof(xpc_vars_t)); + + /* + * Place the XPC per partition specific variables on the cache line + * following the XPC variables structure. + */ + next_cl += XPC_VARS_ALIGNED_SIZE; + memset((u64 *) next_cl, 0, sizeof(xpc_vars_part_t) * MAX_PARTITIONS); + xpc_vars_part = (xpc_vars_part_t *) next_cl; + xpc_vars->vars_part_pa = __pa(next_cl); + + xpc_vars->version = XPC_V_VERSION; + xpc_vars->act_cpuid = cpu_physical_id(0); + xpc_vars->amos_page = amos_page; /* save for next load of XPC */ + + + /* + * Initialize the activation related AMO variables. + */ + xpc_vars->act_amos = xpc_IPI_init(MAX_PARTITIONS); + for (i = 1; i < XP_NUM_NASID_WORDS; i++) { + xpc_IPI_init(i + MAX_PARTITIONS); + } + /* export AMO page's physical address to other partitions */ + xpc_vars->amos_page_pa = ia64_tpa((__u64) xpc_vars->amos_page); + + xpc_vars->partid = sn_local_partid(); // >>> backward compatibility + + /* + * This signifies to the remote partition that our reserved + * page is initialized. + */ + rp->vars_pa = __pa(xpc_vars); + + return rp; +} + + +/* + * Change protections to allow IPI operations (and AMO operations on + * Shub 1.1 systems). + */ +void +xpc_allow_IPI_ops(void) +{ + int node; + + + // >>> Change SH_IPI_ACCESS code to use SAL call once it is available. + xpc_sh_ipi_access = (u64) HUB_L((u64 *) LOCAL_MMR_ADDR(SH_IPI_ACCESS)); + for (node = 0; node < numnodes; node++) { + HUB_S((u64 *) GLOBAL_MMR_ADDR(cnodeid_to_nasid(node), + SH_IPI_ACCESS), -1UL); + + /* + * Since the BIST collides with memory operations on SHUB 1.1 + * sn_change_memprotect() cannot be used. + */ + if (enable_shub_wars_1_1()) { + /* open up everything */ + xpc_prot_vec[node] = (u64) HUB_L(DQLP_ADDR(node)); + HUB_S(DQLP_ADDR(node), xpc_prot_vec[node] | (-1UL)); + HUB_S(DQRP_ADDR(node), xpc_prot_vec[node] | (-1UL)); + } + } +} + + +/* + * Restrict protections to disallow IPI operations (and AMO operations on + * Shub 1.1 systems). + */ +void +xpc_restrict_IPI_ops(void) +{ + int node; + + + // >>> Change SH_IPI_ACCESS code to use SAL call once it is available. + for (node = 0; node < numnodes; node++) { + HUB_S((u64 *) GLOBAL_MMR_ADDR(cnodeid_to_nasid(node), + SH_IPI_ACCESS), xpc_sh_ipi_access); + + if (enable_shub_wars_1_1()) { + HUB_S(DQLP_ADDR(node), xpc_prot_vec[node]); + HUB_S(DQRP_ADDR(node), xpc_prot_vec[node]); + } + } +} + + +/* + * At periodic intervals, scan through all active partitions and ensure + * their heartbeat is still active. If not, the partition is deactivated. + */ +void +xpc_check_remote_hb(void) +{ + xpc_vars_t *remote_vars; + xpc_partition_t *part; + partid_t partid; + bte_result_t bres; + + + remote_vars = (xpc_vars_t *) xpc_remote_copy_buffer; + + for (partid = 1; partid < MAX_PARTITIONS; partid++) { + if (partid == sn_local_partid()) { + continue; + } + + part = &xpc_partitions[partid]; + + if (part->act_state == XPC_P_INACTIVE || + part->act_state == XPC_P_DEACTIVATING) { + continue; + } + + /* pull the remote_hb cache line */ + bres = xp_bte_copy(part->remote_vars_pa, + ia64_tpa((__u64) remote_vars), + XPC_VARS_ALIGNED_SIZE, + (BTE_NOTIFY | BTE_WACQUIRE), NULL); + if (bres != BTE_SUCCESS) { + XPC_DEACTIVATE_PARTITION(part, + xpc_map_bte_errors(bres)); + continue; + } + + DPRINTK(xpc_part, XPC_DBG_P_HEARTBEATV, + "partid = %d, heartbeat = %ld, last_heartbeat = %ld, " + "kdb_status = %ld, HB_mask = 0x%lx\n", partid, + remote_vars->heartbeat, part->last_heartbeat, + remote_vars->kdb_status, + remote_vars->heartbeating_to_mask); + + if (((remote_vars->heartbeat == part->last_heartbeat) && + (remote_vars->kdb_status == 0)) || + !XPC_HB_ALLOWED(sn_local_partid(), remote_vars)) { + + XPC_DEACTIVATE_PARTITION(part, xpcNoHeartbeat); + continue; + } + + part->last_heartbeat = remote_vars->heartbeat; + } +} + + +/* + * Get a copy of the remote partition's rsvd page. + * + * remote_rp points to a buffer that is cacheline aligned for BTE copies and + * assumed to be of size XPC_RSVD_PAGE_ALIGNED_SIZE. + */ +static xpc_t +xpc_get_remote_rp(nasid_t nasid, u64 *discovered_nasids, + xpc_rsvd_page_t *remote_rp, u64 *remote_rsvd_page_pa) +{ + int bres, i; + partid_t partid; + + + /* get the reserved page's physical address */ + + *remote_rsvd_page_pa = xpc_get_rsvd_page_pa(nasid, (u64 *) remote_rp, + XPC_RSVD_PAGE_ALIGNED_SIZE); + if (*remote_rsvd_page_pa == 0) { + return xpcNoRsvdPageAddr; + } + + + /* pull over the reserved page structure */ + + bres = xp_bte_copy(*remote_rsvd_page_pa, ia64_tpa((__u64) remote_rp), + XPC_RSVD_PAGE_ALIGNED_SIZE, + (BTE_NOTIFY | BTE_WACQUIRE), NULL); + if (bres != BTE_SUCCESS) { + return xpc_map_bte_errors(bres); + } + + + if (discovered_nasids != NULL) { + for (i = 0; i < XP_NUM_NASID_WORDS; i++) { + discovered_nasids[i] |= remote_rp->part_nasids[i]; + } + } + + + /* check that the partid is for another partition */ + + partid = remote_rp->partid; + if (remote_rp->partid < 1 || remote_rp->partid > (MAX_PARTITIONS - 1)) { + return xpcInvalidPartid; + } + + if (remote_rp->partid == sn_local_partid()) { + return xpcLocalPartid; + } + + + if (XPC_VERSION_MAJOR(remote_rp->version) != + XPC_VERSION_MAJOR(XPC_RP_VERSION)) { + return xpcBadVersion; + } + + return xpcSuccess; +} + + +/* + * Get a copy of the remote partition's XPC variables. + * + * remote_vars points to a buffer that is cacheline aligned for BTE copies and + * assumed to be of size XPC_VARS_ALIGNED_SIZE. + */ +static xpc_t +xpc_get_remote_vars(u64 remote_vars_pa, xpc_vars_t *remote_vars) +{ + int bres; + + + if (remote_vars_pa == 0) { + return xpcVarsNotSet; + } + + + /* pull over the cross partition variables */ + + bres = xp_bte_copy(remote_vars_pa, ia64_tpa((__u64) remote_vars), + XPC_VARS_ALIGNED_SIZE, + (BTE_NOTIFY | BTE_WACQUIRE), NULL); + if (bres != BTE_SUCCESS) { + return xpc_map_bte_errors(bres); + } + + if (XPC_VERSION_MAJOR(remote_vars->version) != + XPC_VERSION_MAJOR(XPC_V_VERSION)) { + return xpcBadVersion; + } + + return xpcSuccess; +} + + +/* + * Prior code has determine the nasid which generated an IPI. Inspect + * that nasid to determine if its partition needs to be activated or + * deactivated. + * + * A partition is consider "awaiting activation" if our partition + * flags indicate it is not active and it has a heartbeat. A + * partition is considered "awaiting deactivation" if our partition + * flags indicate it is active but it has no heartbeat or it is not + * sending its heartbeat to us. + * + * To determine the heartbeat, the remote nasid must have a properly + * initialized reserved page. + */ +static void +xpc_identify_act_IRQ_req(nasid_t nasid) +{ + xpc_rsvd_page_t *remote_rp; + xpc_vars_t *remote_vars; + u64 remote_rsvd_page_pa; + u64 remote_vars_pa; + partid_t partid; + xpc_partition_t *part; + xpc_t ret; + + + /* pull over the reserved page structure */ + + remote_rp = (xpc_rsvd_page_t *) xpc_remote_copy_buffer; + + ret = xpc_get_remote_rp(nasid, NULL, remote_rp, &remote_rsvd_page_pa); + if (ret != xpcSuccess) { + DPRINTK_ALWAYS(xpc_part, (XPC_DBG_P_ACT | XPC_DBG_P_ERROR), + KERN_WARNING "XPC: unable to get reserved page from " + "nasid %d, which sent interrupt, reason=%s\n", nasid, + xpc_get_ascii_reason_code(ret)); + return; + } + + remote_vars_pa = remote_rp->vars_pa; + partid = remote_rp->partid; + part = &xpc_partitions[partid]; + + + /* pull over the cross partition variables */ + + remote_vars = (xpc_vars_t *) xpc_remote_copy_buffer; + + ret = xpc_get_remote_vars(remote_vars_pa, remote_vars); + if (ret != xpcSuccess) { + + DPRINTK_ALWAYS(xpc_part, (XPC_DBG_P_ACT | XPC_DBG_P_ERROR), + KERN_WARNING "XPC: unable to get XPC variables from " + "nasid %d, which sent interrupt, reason=%s\n", + nasid, xpc_get_ascii_reason_code(ret)); + + XPC_DEACTIVATE_PARTITION(part, ret); + return; + } + + + part->act_IRQ_rcvd++; + + DPRINTK(xpc_part, XPC_DBG_P_ACTV, + "partid for nasid %d is %d; IRQs = %d; HB = %ld:0x%lx\n", + (int) nasid, (int) partid, part->act_IRQ_rcvd, + remote_vars->heartbeat, remote_vars->heartbeating_to_mask); + + + if (part->act_state == XPC_P_INACTIVE) { + + part->remote_rp_pa = remote_rsvd_page_pa; + DPRINTK(xpc_part, XPC_DBG_P_ACTV, + " remote_rp_pa = 0x%016lx\n", part->remote_rp_pa); + + part->remote_vars_pa = remote_vars_pa; + DPRINTK(xpc_part, XPC_DBG_P_ACTV, + " remote_vars_pa = 0x%016lx\n", part->remote_vars_pa); + + part->last_heartbeat = remote_vars->heartbeat; + DPRINTK(xpc_part, XPC_DBG_P_ACTV, + " last_heartbeat = 0x%016lx\n", part->last_heartbeat); + + part->remote_vars_part_pa = remote_vars->vars_part_pa; + DPRINTK(xpc_part, XPC_DBG_P_ACTV, + " remote_vars_part_pa = 0x%016lx\n", + part->remote_vars_part_pa); + + part->remote_act_cpuid = remote_vars->act_cpuid; + DPRINTK(xpc_part, XPC_DBG_P_ACTV, + " remote_act_cpuid = 0x%x\n", part->remote_act_cpuid); + + part->remote_amos_page_pa = remote_vars->amos_page_pa; + DPRINTK(xpc_part, XPC_DBG_P_ACTV, + " remote_amos_page_pa = 0x%lx\n", + part->remote_amos_page_pa); + + xpc_activate_partition(part); + + } else if (part->remote_amos_page_pa != remote_vars->amos_page_pa || + !XPC_HB_ALLOWED(sn_local_partid(), remote_vars)) { + + part->reactivate_nasid = nasid; + XPC_DEACTIVATE_PARTITION(part, xpcReactivating); + } +} + + +/* + * Loop through the activation AMO variables and process any bits + * which are set. Each bit indicates a nasid sending a partition + * activation or deactivation request. + * + * Return #of IRQs detected. + */ +int +xpc_identify_act_IRQ_sender(void) +{ + int word, bit; + u64 nasid_mask; + u64 nasid; /* remote nasid */ + int n_IRQs_detected = 0; + AMO_t *act_amos; + xpc_rsvd_page_t *rp = (xpc_rsvd_page_t *) xpc_rsvd_page; + + + act_amos = xpc_vars->act_amos; + + + /* scan through act AMO variable looking for non-zero entries */ + for (word = 0; word < XP_NUM_NASID_WORDS; word++) { + + nasid_mask = xpc_IPI_receive(&act_amos[word]); + if (nasid_mask == 0) { + /* no IRQs from nasids in this variable */ + continue; + } + + DPRINTK(xpc_part, XPC_DBG_P_ACTV, + "AMO[%d] gave back 0x%lx\n", word, nasid_mask); + + + /* + * If this nasid has been added to the machine since + * our partition was reset, this will retain the + * remote nasid in our reserved pages machine mask. + * This is used in the event of module reload. + */ + rp->mach_nasids[word] |= nasid_mask; + + + /* locate the nasid(s) which sent interrupts */ + + for (bit = 0; bit < (8 * sizeof(u64)); bit++) { + if (nasid_mask & (1UL << bit)) { + n_IRQs_detected++; + nasid = XPC_NASID_FROM_W_B(word, bit); + DPRINTK(xpc_part, XPC_DBG_P_ACT, + "interrupt from nasid %ld\n", nasid); + xpc_identify_act_IRQ_req(nasid); + } + } + } + return n_IRQs_detected; +} + + +/* + * Mark specified partition as active. + */ +xpc_t +xpc_mark_partition_active(xpc_partition_t *part) +{ + unsigned long irq_flags; + xpc_t ret; + + + DPRINTK(xpc_part, XPC_DBG_P_ACT, + "setting partition %d to ACTIVE\n", XPC_PARTID(part)); + + spin_lock_irqsave(&part->act_lock, irq_flags); + if (part->act_state == XPC_P_ACTIVATING) { + part->act_state = XPC_P_ACTIVE; + ret = xpcSuccess; + } else { + XP_ASSERT(part->reason != xpcSuccess); + ret = part->reason; + } + spin_unlock_irqrestore(&part->act_lock, irq_flags); + + return ret; +} + + +/* + * Notify XPC that the partition is down. + */ +void +xpc_deactivate_partition(const int line, xpc_partition_t *part, xpc_t reason) +{ + unsigned long irq_flags; + partid_t partid = XPC_PARTID(part); + + + spin_lock_irqsave(&part->act_lock, irq_flags); + + if (part->act_state == XPC_P_INACTIVE) { + XPC_SET_REASON(part, reason, line); + spin_unlock_irqrestore(&part->act_lock, irq_flags); + if (reason == xpcReactivating) { + /* we interrupt ourselves to reactivate partition */ + XPC_IPI_SEND_REACTIVATE(part); + } + return; + } + if (part->act_state == XPC_P_DEACTIVATING) { + if ((part->reason == xpcUnloading && reason != xpcUnloading) || + reason == xpcReactivating) { + XPC_SET_REASON(part, reason, line); + } + spin_unlock_irqrestore(&part->act_lock, irq_flags); + return; + } + + part->act_state = XPC_P_DEACTIVATING; + XPC_SET_REASON(part, reason, line); + + spin_unlock_irqrestore(&part->act_lock, irq_flags); + + XPC_DISALLOW_HB(partid, xpc_vars); + + DPRINTK(xpc_part, XPC_DBG_P_ACT, + "bringing partition %d down, reason = %d\n", partid, reason); + + xpc_partition_down(part, reason); +} + + +/* + * Mark specified partition as active. + */ +void +xpc_mark_partition_inactive(xpc_partition_t *part) +{ + unsigned long irq_flags; + + + DPRINTK(xpc_part, XPC_DBG_P_ACT, + "setting partition %d to INACTIVE\n", XPC_PARTID(part)); + + spin_lock_irqsave(&part->act_lock, irq_flags); + part->act_state = XPC_P_INACTIVE; + spin_unlock_irqrestore(&part->act_lock, irq_flags); + part->remote_rp_pa = 0; +} + + +/* + * SAL has provided a partition and machine mask. The partition mask + * contains a bit for each even nasid in our partition. The machine + * mask contains a bit for each even nasid in the entire machine. + * + * Using those two bit arrays, we can determine which nasids are + * known in the machine. Each should also have a reserved page + * initialized if they are available for partitioning. + */ +void +xpc_discovery(void) +{ + char *bte_buf; + xpc_rsvd_page_t *remote_rp; + xpc_vars_t *remote_vars; + u64 remote_rsvd_page_pa; + u64 remote_vars_pa; + u64 nodes_per_region; + int region; + nasid_t nasid; + xpc_rsvd_page_t *rp; + partid_t partid; + xpc_partition_t *part; + u64 *discovered_nasids; + xpc_t ret; + + + bte_buf = kmalloc(XPC_RSVD_PAGE_ALIGNED_SIZE + L1_CACHE_BYTES, + GFP_KERNEL); + if (bte_buf == NULL) { + return; + } + remote_rp = (xpc_rsvd_page_t *) L1_CACHE_ALIGN((u64) bte_buf); + remote_vars = (xpc_vars_t *) remote_rp; + + + discovered_nasids = (u64 *) kmalloc(sizeof(u64) * XP_NUM_NASID_WORDS, + GFP_KERNEL); + if (discovered_nasids == NULL) { + kfree(bte_buf); + return; + } + memset(discovered_nasids, 0, sizeof(u64) * XP_NUM_NASID_WORDS); + + rp = (xpc_rsvd_page_t *) xpc_rsvd_page; + + nodes_per_region = (u64) HUB_L((u64 *) LOCAL_MMR_ADDR(SH_SHUB_ID)); + nodes_per_region &= SH_SHUB_ID_NODES_PER_BIT_MASK; + nodes_per_region = (nodes_per_region >> SH_SHUB_ID_NODES_PER_BIT_SHFT); + + + for (region = 0; region < MAX_REGIONS; region++) { + + if (xpc_exiting) { + break; + } + + DPRINTK(xpc_part, XPC_DBG_P_DISCOVERYV, + "searching region %d\n", region); + + for (nasid = (region * nodes_per_region * 2); + nasid < ((region + 1) * nodes_per_region * 2); + nasid += 2) { + + if (xpc_exiting) { + break; + } + + DPRINTK(xpc_part, XPC_DBG_P_DISCOVERYV, + "checking nasid %d\n", nasid); + + + if (XPC_NASID_IN_ARRAY(nasid, rp->part_nasids)) { + DPRINTK(xpc_part, XPC_DBG_P_DISCOVERYV, + "PROM indicates Nasid %d is part " + "of the local partition; " + "skipping region\n", nasid); + break; + } + + if (!(XPC_NASID_IN_ARRAY(nasid, rp->mach_nasids))) { + DPRINTK(xpc_part, XPC_DBG_P_DISCOVERYV, + "PROM indicates Nasid %d was not " + "on Numa-Link network at " + "reset\n", nasid); + continue; + } + + if (XPC_NASID_IN_ARRAY(nasid, discovered_nasids)) { + DPRINTK(xpc_part, XPC_DBG_P_DISCOVERYV, + "Nasid %d is part of a partition " + "which was previously discovered\n", + nasid); + continue; + } + + + /* pull over the reserved page structure */ + + ret = xpc_get_remote_rp(nasid, discovered_nasids, + remote_rp, &remote_rsvd_page_pa); + if (ret != xpcSuccess) { + DPRINTK(xpc_part, XPC_DBG_P_DISCOVERYV, + "unable to get reserved page from " + "nasid %d, reason=%s\n", nasid, + xpc_get_ascii_reason_code(ret)); + + if (ret == xpcLocalPartid) { + break; + } + continue; + } + + remote_vars_pa = remote_rp->vars_pa; + + partid = remote_rp->partid; + part = &xpc_partitions[partid]; + + + /* pull over the cross partition variables */ + + ret = xpc_get_remote_vars(remote_vars_pa, remote_vars); + if (ret != xpcSuccess) { + DPRINTK(xpc_part, XPC_DBG_P_DISCOVERYV, + "unable to get XPC variables from " + "nasid %d, reason=%s\n", nasid, + xpc_get_ascii_reason_code(ret)); + + XPC_DEACTIVATE_PARTITION(part, ret); + continue; + } + + if (part->act_state != XPC_P_INACTIVE) { + DPRINTK(xpc_part, XPC_DBG_P_DISCOVERYV, + "partition %d on nasid %d is already " + "activating\n", partid, nasid); + break; + } + + /* + * 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 thus 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_act_amos_pa after + * module reloads and system reboots. + */ + if (sn_register_xp_addr_region( + remote_vars->amos_page_pa, + PAGE_SIZE, 1) < 0) { + DPRINTK(xpc_part, XPC_DBG_P_DISCOVERYV, + "partition %d failed to register " + "xp_addr region 0x%016lx\n", partid, + remote_vars->amos_page_pa); + + XPC_SET_REASON(part, xpcPhysAddrRegFailed, + __LINE__); + break; + } + + /* + * The remote nasid is valid and available. + * Send an interrupt to that nasid to notify + * it that we are ready to begin activation. + */ + DPRINTK(xpc_part, XPC_DBG_P_DISCOVERYV, + "sending an interrupt to AMO 0x%lx, cpuid " + "0x%lx\n", remote_vars->amos_page_pa, + remote_vars->act_cpuid); + + XPC_IPI_SEND_ACTIVATE(remote_vars); + } + } + + kfree(discovered_nasids); + kfree(bte_buf); +} + + +/* + * Given a partid, get the nasids owned by that partition from the + * remote partitions reserved page. + */ +xpc_t +xpc_partid_to_nasids(partid_t partid, void *nasid_masks) +{ + xpc_partition_t *part; + u64 part_nasid_pa; + int bte_res; + + + part = &xpc_partitions[partid]; + if (part->remote_rp_pa == 0) { + return xpcPartitionDown; + } + + part_nasid_pa = part->remote_rp_pa + + (u64) &((xpc_rsvd_page_t *) 0)->part_nasids; + + bte_res = xp_bte_copy(part_nasid_pa, ia64_tpa((__u64) nasid_masks), + L1_CACHE_ALIGN(CNASID_MASK_BYTES), + (BTE_NOTIFY | BTE_WACQUIRE), NULL); + + return xpc_map_bte_errors(bte_res); +} + Index: linux/arch/ia64/sn/kernel/xpc_stubs.h =================================================================== --- linux.orig/arch/ia64/sn/kernel/xpc_stubs.h 1969-12-31 18:00:00.000000000 -0600 +++ linux/arch/ia64/sn/kernel/xpc_stubs.h 2004-04-23 13:44:31.000000000 -0500 @@ -0,0 +1,66 @@ +/* + * 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. + * + * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. + */ + + +/* + * Cross Partition Communication (XPC) stubs specific to the standard version. + */ + + +#ifndef _IA64_SN_KERNEL_XPC_STUBS_H +#define _IA64_SN_KERNEL_XPC_STUBS_H + + +/* once Linux 2.4 is no longer supported, eliminate this macro-gyration */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) && \ + LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) +#define irqreturn_t void +#endif + + +#define XPC_KDB_REGISTER() xpc_kdb_register() +#define XPC_KDB_UNREGISTER() xpc_kdb_unregister() + +#define XPC_CONNECTED_CALLOUT(_ch) xpc_create_kthreads(_ch, 1) +#define XPC_DISCONNECTED_CALLOUT(_ch, _ch_flags, _irq_flags) + +#define XPC_INITIATE_MSG_DELIVERY(_ch, _nmsgs) \ + if ((_ch)->flags & XPC_C_CONNECTCALLOUT) { \ + xpc_activate_kthreads(_ch, _nmsgs); \ + } + +#define XPC_PROCESS_CHANNEL_ACTIVITY(_p) xpc_wakeup_channel_mgr(_p) + +#define XPC_PROCESS_PARTITION_DOWN(_p) xpc_wakeup_channel_mgr(_p) + +#define XPC_DISCONNECT_WAIT(_ch_number) xpc_disconnect_wait(_ch_number) + +#define XPC_INIT_TIMER(_timer, _function, _data, _expires) \ + { \ + init_timer(_timer); \ + (_timer)->function = (void (*)(unsigned long)) _function; \ + (_timer)->data = (unsigned long) _data; \ + (_timer)->expires = (unsigned long) _expires; \ + add_timer(_timer); \ + } +#define XPC_DEL_TIMER(_timer) del_timer_sync(_timer) + + +#define XPC_REQUEST_IRQ(_irq, _handler, _flags, _dev_name, _dev_id) \ + request_irq(_irq, _handler, _flags, _dev_name, _dev_id) +#define XPC_FREE_IRQ(_irq, _dev_id) free_irq(_irq, _dev_id) + + +/* clock ticks */ +#define XPC_TICKS jiffies +#define XPC_TICKS_PER_SEC HZ + + +#endif /* _IA64_SN_KERNEL_XPC_STUBS_H */ +