Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
fac84ef2
Commit
fac84ef2
authored
Oct 28, 2005
by
Tony Luck
Browse files
Options
Browse Files
Download
Plain Diff
Pull xpc-disengage into release branch
parents
d73dee6e
27929029
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
1142 additions
and
370 deletions
+1142
-370
arch/ia64/sn/kernel/xpc.h
arch/ia64/sn/kernel/xpc.h
+324
-42
arch/ia64/sn/kernel/xpc_channel.c
arch/ia64/sn/kernel/xpc_channel.c
+195
-134
arch/ia64/sn/kernel/xpc_main.c
arch/ia64/sn/kernel/xpc_main.c
+256
-74
arch/ia64/sn/kernel/xpc_partition.c
arch/ia64/sn/kernel/xpc_partition.c
+355
-118
include/asm-ia64/sn/xp.h
include/asm-ia64/sn/xp.h
+12
-2
No files found.
arch/ia64/sn/kernel/xpc.h
View file @
fac84ef2
...
...
@@ -57,7 +57,7 @@
#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 XPC_HB_CHECK_DEFAULT_
INTERVAL
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"
...
...
@@ -67,34 +67,82 @@
#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.
* the reserved page
*
* SAL reserves one page of memory per partition for XPC. Though a full page
* in length (16384 bytes), its starting address is not page aligned, but it
* is cacheline aligned. The reserved page consists of the following:
*
* reserved page header
*
* The first cacheline of the reserved page contains the header
* (struct xpc_rsvd_page). Before SAL initialization has completed,
* SAL has set up the following fields of the reserved page header:
* SAL_signature, SAL_version, partid, and nasids_size. The other
* fields are set up by XPC. (xpc_rsvd_page points to the local
* partition's reserved page.)
*
* 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.
* part_nasids mask
* mach_nasids mask
*
* SAL also sets up two bitmaps (or masks), one that reflects the actual
* nasids in this partition (part_nasids), and the other that reflects
* the actual nasids in the entire machine (mach_nasids). We're only
* interested in the even numbered nasids (which contain the processors
* and/or memory), so we only need half as many bits to represent the
* nasids. The part_nasids mask is located starting at the first cacheline
* following the reserved page header. The mach_nasids mask follows right
* after the part_nasids mask. The size in bytes of each mask is reflected
* by the reserved page header field 'nasids_size'. (Local partition's
* mask pointers are xpc_part_nasids and xpc_mach_nasids.)
*
* vars
* vars part
*
* Immediately following the mach_nasids mask are the XPC variables
* required by other partitions. First are those that are generic to all
* partitions (vars), followed on the next available cacheline by those
* which are partition specific (vars part). These are setup by XPC.
* (Local partition's vars pointers are xpc_vars and xpc_vars_part.)
*
* Note: Until vars_pa is set, the partition XPC code has not been initialized.
*/
struct
xpc_rsvd_page
{
u64
SAL_signature
;
/* SAL unique signature */
u64
SAL_version
;
/* SAL
specified
version */
u8
partid
;
/*
partition ID from SAL
*/
u64
SAL_signature
;
/* SAL
:
unique signature */
u64
SAL_version
;
/* SAL
:
version */
u8
partid
;
/*
SAL: partition ID
*/
u8
version
;
u8
pad
[
6
];
/* pad to u64 align
*/
u8
pad
1
[
6
];
/* align to next u64 in cacheline
*/
volatile
u64
vars_pa
;
u64
part_nasids
[
XP_NASID_MASK_WORDS
]
____cacheline_aligned
;
u64
mach_nasids
[
XP_NASID_MASK_WORDS
]
____cacheline_aligned
;
struct
timespec
stamp
;
/* time when reserved page was setup by XPC */
u64
pad2
[
9
];
/* align to last u64 in cacheline */
u64
nasids_size
;
/* SAL: size of each nasid mask in bytes */
};
#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(struct xpc_rsvd_page)))
#define XPC_RP_VERSION _XPC_VERSION(1,1)
/* version 1.1 of the reserved page */
#define XPC_SUPPORTS_RP_STAMP(_version) \
(_version >= _XPC_VERSION(1,1))
/*
* compare stamps - the return value is:
*
* < 0, if stamp1 < stamp2
* = 0, if stamp1 == stamp2
* > 0, if stamp1 > stamp2
*/
static
inline
int
xpc_compare_stamps
(
struct
timespec
*
stamp1
,
struct
timespec
*
stamp2
)
{
int
ret
;
if
((
ret
=
stamp1
->
tv_sec
-
stamp2
->
tv_sec
)
==
0
)
{
ret
=
stamp1
->
tv_nsec
-
stamp2
->
tv_nsec
;
}
return
ret
;
}
/*
...
...
@@ -121,11 +169,58 @@ struct xpc_vars {
u64
vars_part_pa
;
u64
amos_page_pa
;
/* paddr of page of AMOs from MSPEC driver */
AMO_t
*
amos_page
;
/* vaddr of page of AMOs from MSPEC driver */
AMO_t
*
act_amos
;
/* pointer to the first activation AMO */
};
#define XPC_V_VERSION _XPC_VERSION(3,0)
/* version 3.0 of the cross vars */
#define XPC_VARS_ALIGNED_SIZE (L1_CACHE_ALIGN(sizeof(struct xpc_vars)))
#define XPC_V_VERSION _XPC_VERSION(3,1)
/* version 3.1 of the cross vars */
#define XPC_SUPPORTS_DISENGAGE_REQUEST(_version) \
(_version >= _XPC_VERSION(3,1))
static
inline
int
xpc_hb_allowed
(
partid_t
partid
,
struct
xpc_vars
*
vars
)
{
return
((
vars
->
heartbeating_to_mask
&
(
1UL
<<
partid
))
!=
0
);
}
static
inline
void
xpc_allow_hb
(
partid_t
partid
,
struct
xpc_vars
*
vars
)
{
u64
old_mask
,
new_mask
;
do
{
old_mask
=
vars
->
heartbeating_to_mask
;
new_mask
=
(
old_mask
|
(
1UL
<<
partid
));
}
while
(
cmpxchg
(
&
vars
->
heartbeating_to_mask
,
old_mask
,
new_mask
)
!=
old_mask
);
}
static
inline
void
xpc_disallow_hb
(
partid_t
partid
,
struct
xpc_vars
*
vars
)
{
u64
old_mask
,
new_mask
;
do
{
old_mask
=
vars
->
heartbeating_to_mask
;
new_mask
=
(
old_mask
&
~
(
1UL
<<
partid
));
}
while
(
cmpxchg
(
&
vars
->
heartbeating_to_mask
,
old_mask
,
new_mask
)
!=
old_mask
);
}
/*
* The AMOs page consists of a number of AMO variables which are divided into
* four groups, The first two groups are used to identify an IRQ's sender.
* These two groups consist of 64 and 128 AMO variables respectively. The last
* two groups, consisting of just one AMO variable each, are used to identify
* the remote partitions that are currently engaged (from the viewpoint of
* the XPC running on the remote partition).
*/
#define XPC_NOTIFY_IRQ_AMOS 0
#define XPC_ACTIVATE_IRQ_AMOS (XPC_NOTIFY_IRQ_AMOS + XP_MAX_PARTITIONS)
#define XPC_ENGAGED_PARTITIONS_AMO (XPC_ACTIVATE_IRQ_AMOS + XP_NASID_MASK_WORDS)
#define XPC_DISENGAGE_REQUEST_AMO (XPC_ENGAGED_PARTITIONS_AMO + 1)
/*
* The following structure describes the per partition specific variables.
...
...
@@ -165,6 +260,16 @@ struct xpc_vars_part {
#define XPC_VP_MAGIC2 0x0073726176435058L
/* 'XPCvars\0'L (little endian) */
/* the reserved page sizes and offsets */
#define XPC_RP_HEADER_SIZE L1_CACHE_ALIGN(sizeof(struct xpc_rsvd_page))
#define XPC_RP_VARS_SIZE L1_CACHE_ALIGN(sizeof(struct xpc_vars))
#define XPC_RP_PART_NASIDS(_rp) (u64 *) ((u8 *) _rp + XPC_RP_HEADER_SIZE)
#define XPC_RP_MACH_NASIDS(_rp) (XPC_RP_PART_NASIDS(_rp) + xp_nasid_mask_words)
#define XPC_RP_VARS(_rp) ((struct xpc_vars *) XPC_RP_MACH_NASIDS(_rp) + xp_nasid_mask_words)
#define XPC_RP_VARS_PART(_rp) (struct xpc_vars_part *) ((u8 *) XPC_RP_VARS(rp) + XPC_RP_VARS_SIZE)
/*
* Functions registered by add_timer() or called by kernel_thread() only
...
...
@@ -349,6 +454,9 @@ struct xpc_channel {
atomic_t
n_on_msg_allocate_wq
;
/* #on msg allocation wait queue */
wait_queue_head_t
msg_allocate_wq
;
/* msg allocation wait queue */
u8
delayed_IPI_flags
;
/* IPI flags received, but delayed */
/* action until channel disconnected */
/* queue of msg senders who want to be notified when msg received */
atomic_t
n_to_notify
;
/* #of msg senders to notify */
...
...
@@ -358,7 +466,7 @@ struct xpc_channel {
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
*/
struct
semaphore
wdisconnect_sema
;
/* wait for channel disconnect
*/
struct
xpc_openclose_args
*
local_openclose_args
;
/* args passed on */
/* opening or closing of channel */
...
...
@@ -410,6 +518,8 @@ struct xpc_channel {
#define XPC_C_DISCONNECTED 0x00002000
/* channel is disconnected */
#define XPC_C_DISCONNECTING 0x00004000
/* channel is being disconnected */
#define XPC_C_DISCONNECTCALLOUT 0x00008000
/* chan disconnected callout made */
#define XPC_C_WDISCONNECT 0x00010000
/* waiting for channel disconnect */
...
...
@@ -422,6 +532,8 @@ struct xpc_partition {
/* XPC HB infrastructure */
u8
remote_rp_version
;
/* version# of partition's rsvd pg */
struct
timespec
remote_rp_stamp
;
/* time when rsvd pg was initialized */
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 */
...
...
@@ -432,14 +544,18 @@ struct xpc_partition {
u32
act_IRQ_rcvd
;
/* IRQs since activation */
spinlock_t
act_lock
;
/* protect updating of act_state */
u8
act_state
;
/* from XPC HB viewpoint */
u8
remote_vars_version
;
/* version# of partition's vars */
enum
xpc_retval
reason
;
/* reason partition is deactivating */
int
reason_line
;
/* line# deactivation initiated from */
int
reactivate_nasid
;
/* nasid in partition to reactivate */
unsigned
long
disengage_request_timeout
;
/* timeout in jiffies */
struct
timer_list
disengage_request_timer
;
/* XPC infrastructure referencing and teardown control */
volatile
u8
setup_state
;
/* infrastructure setup state */
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 */
...
...
@@ -454,6 +570,7 @@ struct xpc_partition {
u8
nchannels
;
/* #of defined channels supported */
atomic_t
nchannels_active
;
/* #of channels that are not DISCONNECTED */
atomic_t
nchannels_engaged
;
/* #of channels engaged with remote part */
struct
xpc_channel
*
channels
;
/* array of channel structures */
void
*
local_GPs_base
;
/* base address of kmalloc'd space */
...
...
@@ -518,6 +635,7 @@ struct xpc_partition {
#define XPC_P_TORNDOWN 0x03
/* infrastructure is torndown */
/*
* struct xpc_partition IPI_timer #of seconds to wait before checking for
* dropped IPIs. These occur whenever an IPI amo write doesn't complete until
...
...
@@ -526,6 +644,13 @@ struct xpc_partition {
#define XPC_P_DROPPED_IPI_WAIT (0.25 * HZ)
/* number of seconds to wait for other partitions to disengage */
#define XPC_DISENGAGE_REQUEST_DEFAULT_TIMELIMIT 90
/* interval in seconds to print 'waiting disengagement' messages */
#define XPC_DISENGAGE_PRINTMSG_INTERVAL 10
#define XPC_PARTID(_p) ((partid_t) ((_p) - &xpc_partitions[0]))
...
...
@@ -534,24 +659,20 @@ struct xpc_partition {
extern
struct
xpc_registration
xpc_registrations
[];
/*
>>> found in xpc_main.c only
*/
/*
found in xpc_main.c
*/
extern
struct
device
*
xpc_part
;
extern
struct
device
*
xpc_chan
;
extern
int
xpc_disengage_request_timelimit
;
extern
irqreturn_t
xpc_notify_IRQ_handler
(
int
,
void
*
,
struct
pt_regs
*
);
extern
void
xpc_dropped_IPI_check
(
struct
xpc_partition
*
);
extern
void
xpc_activate_partition
(
struct
xpc_partition
*
);
extern
void
xpc_activate_kthreads
(
struct
xpc_channel
*
,
int
);
extern
void
xpc_create_kthreads
(
struct
xpc_channel
*
,
int
);
extern
void
xpc_disconnect_wait
(
int
);
/* found in xpc_main.c and efi-xpc.c */
extern
void
xpc_activate_partition
(
struct
xpc_partition
*
);
/* found in xpc_partition.c */
extern
int
xpc_exiting
;
extern
int
xpc_hb_interval
;
extern
int
xpc_hb_check_interval
;
extern
struct
xpc_vars
*
xpc_vars
;
extern
struct
xpc_rsvd_page
*
xpc_rsvd_page
;
extern
struct
xpc_vars_part
*
xpc_vars_part
;
...
...
@@ -561,6 +682,7 @@ extern struct xpc_rsvd_page *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
int
xpc_partition_disengaged
(
struct
xpc_partition
*
);
extern
enum
xpc_retval
xpc_mark_partition_active
(
struct
xpc_partition
*
);
extern
void
xpc_mark_partition_inactive
(
struct
xpc_partition
*
);
extern
void
xpc_discovery
(
void
);
...
...
@@ -585,8 +707,8 @@ extern void xpc_connected_callout(struct xpc_channel *);
extern
void
xpc_deliver_msg
(
struct
xpc_channel
*
);
extern
void
xpc_disconnect_channel
(
const
int
,
struct
xpc_channel
*
,
enum
xpc_retval
,
unsigned
long
*
);
extern
void
xpc_disconnect
ed
_callout
(
struct
xpc_channel
*
);
extern
void
xpc_partition_down
(
struct
xpc_partition
*
,
enum
xpc_retval
);
extern
void
xpc_disconnect
ing
_callout
(
struct
xpc_channel
*
);
extern
void
xpc_partition_
going_
down
(
struct
xpc_partition
*
,
enum
xpc_retval
);
extern
void
xpc_teardown_infrastructure
(
struct
xpc_partition
*
);
...
...
@@ -673,6 +795,157 @@ xpc_part_ref(struct xpc_partition *part)
/*
* This next set of inlines are used to keep track of when a partition is
* potentially engaged in accessing memory belonging to another partition.
*/
static
inline
void
xpc_mark_partition_engaged
(
struct
xpc_partition
*
part
)
{
unsigned
long
irq_flags
;
AMO_t
*
amo
=
(
AMO_t
*
)
__va
(
part
->
remote_amos_page_pa
+
(
XPC_ENGAGED_PARTITIONS_AMO
*
sizeof
(
AMO_t
)));
local_irq_save
(
irq_flags
);
/* set bit corresponding to our partid in remote partition's AMO */
FETCHOP_STORE_OP
(
TO_AMO
((
u64
)
&
amo
->
variable
),
FETCHOP_OR
,
(
1UL
<<
sn_partition_id
));
/*
* 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.
*/
(
void
)
xp_nofault_PIOR
((
u64
*
)
GLOBAL_MMR_ADDR
(
NASID_GET
(
&
amo
->
variable
),
xp_nofault_PIOR_target
));
local_irq_restore
(
irq_flags
);
}
static
inline
void
xpc_mark_partition_disengaged
(
struct
xpc_partition
*
part
)
{
unsigned
long
irq_flags
;
AMO_t
*
amo
=
(
AMO_t
*
)
__va
(
part
->
remote_amos_page_pa
+
(
XPC_ENGAGED_PARTITIONS_AMO
*
sizeof
(
AMO_t
)));
local_irq_save
(
irq_flags
);
/* clear bit corresponding to our partid in remote partition's AMO */
FETCHOP_STORE_OP
(
TO_AMO
((
u64
)
&
amo
->
variable
),
FETCHOP_AND
,
~
(
1UL
<<
sn_partition_id
));
/*
* 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.
*/
(
void
)
xp_nofault_PIOR
((
u64
*
)
GLOBAL_MMR_ADDR
(
NASID_GET
(
&
amo
->
variable
),
xp_nofault_PIOR_target
));
local_irq_restore
(
irq_flags
);
}
static
inline
void
xpc_request_partition_disengage
(
struct
xpc_partition
*
part
)
{
unsigned
long
irq_flags
;
AMO_t
*
amo
=
(
AMO_t
*
)
__va
(
part
->
remote_amos_page_pa
+
(
XPC_DISENGAGE_REQUEST_AMO
*
sizeof
(
AMO_t
)));
local_irq_save
(
irq_flags
);
/* set bit corresponding to our partid in remote partition's AMO */
FETCHOP_STORE_OP
(
TO_AMO
((
u64
)
&
amo
->
variable
),
FETCHOP_OR
,
(
1UL
<<
sn_partition_id
));
/*
* 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.
*/
(
void
)
xp_nofault_PIOR
((
u64
*
)
GLOBAL_MMR_ADDR
(
NASID_GET
(
&
amo
->
variable
),
xp_nofault_PIOR_target
));
local_irq_restore
(
irq_flags
);
}
static
inline
void
xpc_cancel_partition_disengage_request
(
struct
xpc_partition
*
part
)
{
unsigned
long
irq_flags
;
AMO_t
*
amo
=
(
AMO_t
*
)
__va
(
part
->
remote_amos_page_pa
+
(
XPC_DISENGAGE_REQUEST_AMO
*
sizeof
(
AMO_t
)));
local_irq_save
(
irq_flags
);
/* clear bit corresponding to our partid in remote partition's AMO */
FETCHOP_STORE_OP
(
TO_AMO
((
u64
)
&
amo
->
variable
),
FETCHOP_AND
,
~
(
1UL
<<
sn_partition_id
));
/*
* 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.
*/
(
void
)
xp_nofault_PIOR
((
u64
*
)
GLOBAL_MMR_ADDR
(
NASID_GET
(
&
amo
->
variable
),
xp_nofault_PIOR_target
));
local_irq_restore
(
irq_flags
);
}
static
inline
u64
xpc_partition_engaged
(
u64
partid_mask
)
{
AMO_t
*
amo
=
xpc_vars
->
amos_page
+
XPC_ENGAGED_PARTITIONS_AMO
;
/* return our partition's AMO variable ANDed with partid_mask */
return
(
FETCHOP_LOAD_OP
(
TO_AMO
((
u64
)
&
amo
->
variable
),
FETCHOP_LOAD
)
&
partid_mask
);
}
static
inline
u64
xpc_partition_disengage_requested
(
u64
partid_mask
)
{
AMO_t
*
amo
=
xpc_vars
->
amos_page
+
XPC_DISENGAGE_REQUEST_AMO
;
/* return our partition's AMO variable ANDed with partid_mask */
return
(
FETCHOP_LOAD_OP
(
TO_AMO
((
u64
)
&
amo
->
variable
),
FETCHOP_LOAD
)
&
partid_mask
);
}
static
inline
void
xpc_clear_partition_engaged
(
u64
partid_mask
)
{
AMO_t
*
amo
=
xpc_vars
->
amos_page
+
XPC_ENGAGED_PARTITIONS_AMO
;
/* clear bit(s) based on partid_mask in our partition's AMO */
FETCHOP_STORE_OP
(
TO_AMO
((
u64
)
&
amo
->
variable
),
FETCHOP_AND
,
~
partid_mask
);
}
static
inline
void
xpc_clear_partition_disengage_request
(
u64
partid_mask
)
{
AMO_t
*
amo
=
xpc_vars
->
amos_page
+
XPC_DISENGAGE_REQUEST_AMO
;
/* clear bit(s) based on partid_mask in our partition's AMO */
FETCHOP_STORE_OP
(
TO_AMO
((
u64
)
&
amo
->
variable
),
FETCHOP_AND
,
~
partid_mask
);
}
/*
* 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,
...
...
@@ -722,13 +995,13 @@ xpc_IPI_send(AMO_t *amo, u64 flag, int nasid, int phys_cpuid, int vector)
* Flag the appropriate AMO variable and send an IPI to the specified node.
*/
static
inline
void
xpc_activate_IRQ_send
(
u64
amos_page
,
int
from_nasid
,
int
to_nasid
,
xpc_activate_IRQ_send
(
u64
amos_page
_pa
,
int
from_nasid
,
int
to_nasid
,
int
to_phys_cpuid
)
{
int
w_index
=
XPC_NASID_W_INDEX
(
from_nasid
);
int
b_index
=
XPC_NASID_B_INDEX
(
from_nasid
);
AMO_t
*
amos
=
(
AMO_t
*
)
__va
(
amos_page
+
(
XP_MAX_PARTITION
S
*
sizeof
(
AMO_t
)));
AMO_t
*
amos
=
(
AMO_t
*
)
__va
(
amos_page
_pa
+
(
XPC_ACTIVATE_IRQ_AMO
S
*
sizeof
(
AMO_t
)));
(
void
)
xpc_IPI_send
(
&
amos
[
w_index
],
(
1UL
<<
b_index
),
to_nasid
,
...
...
@@ -756,6 +1029,13 @@ xpc_IPI_send_reactivate(struct xpc_partition *part)
xpc_vars
->
act_nasid
,
xpc_vars
->
act_phys_cpuid
);
}
static
inline
void
xpc_IPI_send_disengage
(
struct
xpc_partition
*
part
)
{
xpc_activate_IRQ_send
(
part
->
remote_amos_page_pa
,
cnodeid_to_nasid
(
0
),
part
->
remote_act_nasid
,
part
->
remote_act_phys_cpuid
);
}
/*
* IPIs associated with SGI_XPC_NOTIFY IRQ.
...
...
@@ -836,6 +1116,7 @@ xpc_notify_IRQ_send_local(struct xpc_channel *ch, u8 ipi_flag,
/* 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_SET_IPI_FLAGS(_amo, _c, _f) (_amo) |= ((u64) (_f) << ((_c) * 8))
#define XPC_ANY_OPENCLOSE_IPI_FLAGS_SET(_amo) ((_amo) & 0x0f0f0f0f0f0f0f0f)
#define XPC_ANY_MSG_IPI_FLAGS_SET(_amo) ((_amo) & 0x1010101010101010)
...
...
@@ -903,17 +1184,18 @@ xpc_IPI_send_local_msgrequest(struct xpc_channel *ch)
* 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
* (XP_MAX_PARTITIONS) AMO variables for message notification (xpc_main.c)
* and an additional 16 AMO variables for partition activation (xpc_hb.c).
* AMO variables (based on XP_MAX_PARTITIONS) for message notification and an
* additional 128 AMO variables (based on XP_NASID_MASK_WORDS) for partition
* activation and 2 AMO variables for partition deactivation.
*/
static
inline
AMO_t
*
xpc_IPI_init
(
partid_t
partid
)
xpc_IPI_init
(
int
index
)
{
AMO_t
*
part_amo
=
xpc_vars
->
amos_page
+
partid
;
AMO_t
*
amo
=
xpc_vars
->
amos_page
+
index
;
xpc_IPI_receive
(
part_amo
);
return
part_
amo
;
(
void
)
xpc_IPI_receive
(
amo
);
/* clear AMO variable */
return
amo
;
}
...
...
arch/ia64/sn/kernel/xpc_channel.c
View file @
fac84ef2
...
...
@@ -57,6 +57,7 @@ xpc_initialize_channels(struct xpc_partition *part, partid_t partid)
spin_lock_init
(
&
ch
->
lock
);
sema_init
(
&
ch
->
msg_to_pull_sema
,
1
);
/* mutex */
sema_init
(
&
ch
->
wdisconnect_sema
,
0
);
/* event wait */
atomic_set
(
&
ch
->
n_on_msg_allocate_wq
,
0
);
init_waitqueue_head
(
&
ch
->
msg_allocate_wq
);
...
...
@@ -166,6 +167,7 @@ xpc_setup_infrastructure(struct xpc_partition *part)
xpc_initialize_channels
(
part
,
partid
);
atomic_set
(
&
part
->
nchannels_active
,
0
);
atomic_set
(
&
part
->
nchannels_engaged
,
0
);
/* local_IPI_amo were set to 0 by an earlier memset() */
...
...
@@ -555,8 +557,6 @@ xpc_allocate_msgqueues(struct xpc_channel *ch)
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
);
...
...
@@ -625,6 +625,55 @@ xpc_process_connect(struct xpc_channel *ch, unsigned long *irq_flags)
}
/*
* Notify those who wanted to be notified upon delivery of their message.
*/
static
void
xpc_notify_senders
(
struct
xpc_channel
*
ch
,
enum
xpc_retval
reason
,
s64
put
)
{
struct
xpc_notify
*
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
(
&
notify
->
type
,
notify_type
,
0
)
!=
notify_type
)
{
continue
;
}
DBUG_ON
(
notify_type
!=
XPC_N_CALL
);
atomic_dec
(
&
ch
->
n_to_notify
);
if
(
notify
->
func
!=
NULL
)
{
dev_dbg
(
xpc_chan
,
"notify->func() called, notify=0x%p, "
"msg_number=%ld, partid=%d, channel=%d
\n
"
,
(
void
*
)
notify
,
get
,
ch
->
partid
,
ch
->
number
);
notify
->
func
(
reason
,
ch
->
partid
,
ch
->
number
,
notify
->
key
);
dev_dbg
(
xpc_chan
,
"notify->func() returned, "
"notify=0x%p, msg_number=%ld, partid=%d, "
"channel=%d
\n
"
,
(
void
*
)
notify
,
get
,
ch
->
partid
,
ch
->
number
);
}
}
}
/*
* Free up message queues and other stuff that were allocated for the specified
* channel.
...
...
@@ -669,9 +718,6 @@ xpc_free_msgqueues(struct xpc_channel *ch)
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
);
}
}
...
...
@@ -683,7 +729,7 @@ static void
xpc_process_disconnect
(
struct
xpc_channel
*
ch
,
unsigned
long
*
irq_flags
)
{
struct
xpc_partition
*
part
=
&
xpc_partitions
[
ch
->
partid
];
u32
ch
_flags
=
ch
->
flags
;
u32
ch
annel_was_connected
=
(
ch
->
flags
&
XPC_C_WASCONNECTED
)
;
DBUG_ON
(
!
spin_is_locked
(
&
ch
->
lock
));
...
...
@@ -701,12 +747,13 @@ xpc_process_disconnect(struct xpc_channel *ch, unsigned long *irq_flags)
}
DBUG_ON
(
atomic_read
(
&
ch
->
kthreads_assigned
)
!=
0
);
/* it's now safe to free the channel's message queues */
xpc_free_msgqueues
(
ch
);
DBUG_ON
(
ch
->
flags
&
XPC_C_SETUP
);
if
(
part
->
act_state
==
XPC_P_DEACTIVATING
)
{
/* can't proceed until the other side disengages from us */
if
(
xpc_partition_engaged
(
1UL
<<
ch
->
partid
))
{
return
;
}
if
(
part
->
act_state
!=
XPC_P_DEACTIVATING
)
{
}
else
{
/* as long as the other side is up do the full protocol */
...
...
@@ -724,16 +771,42 @@ xpc_process_disconnect(struct xpc_channel *ch, unsigned long *irq_flags)
}
}
/* wake those waiting for notify completion */
if
(
atomic_read
(
&
ch
->
n_to_notify
)
>
0
)
{
/* >>> we do callout while holding ch->lock */
xpc_notify_senders
(
ch
,
ch
->
reason
,
ch
->
w_local_GP
.
put
);
}
/* both sides are disconnected now */
ch
->
flags
=
XPC_C_DISCONNECTED
;
/* clear all flags, but this one */
/* it's now safe to free the channel's message queues */
xpc_free_msgqueues
(
ch
);
/* mark disconnected, clear all other flags except XPC_C_WDISCONNECT */
ch
->
flags
=
(
XPC_C_DISCONNECTED
|
(
ch
->
flags
&
XPC_C_WDISCONNECT
));
atomic_dec
(
&
part
->
nchannels_active
);
if
(
ch
_flags
&
XPC_C_WASCONNECTED
)
{
if
(
ch
annel_was_connected
)
{
dev_info
(
xpc_chan
,
"channel %d to partition %d disconnected, "
"reason=%d
\n
"
,
ch
->
number
,
ch
->
partid
,
ch
->
reason
);
}
if
(
ch
->
flags
&
XPC_C_WDISCONNECT
)
{
spin_unlock_irqrestore
(
&
ch
->
lock
,
*
irq_flags
);
up
(
&
ch
->
wdisconnect_sema
);
spin_lock_irqsave
(
&
ch
->
lock
,
*
irq_flags
);
}
else
if
(
ch
->
delayed_IPI_flags
)
{
if
(
part
->
act_state
!=
XPC_P_DEACTIVATING
)
{
/* time to take action on any delayed IPI flags */
spin_lock
(
&
part
->
IPI_lock
);
XPC_SET_IPI_FLAGS
(
part
->
local_IPI_amo
,
ch
->
number
,
ch
->
delayed_IPI_flags
);
spin_unlock
(
&
part
->
IPI_lock
);
}
ch
->
delayed_IPI_flags
=
0
;
}
}
...
...
@@ -754,6 +827,19 @@ xpc_process_openclose_IPI(struct xpc_partition *part, int ch_number,
spin_lock_irqsave
(
&
ch
->
lock
,
irq_flags
);
again:
if
((
ch
->
flags
&
XPC_C_DISCONNECTED
)
&&
(
ch
->
flags
&
XPC_C_WDISCONNECT
))
{
/*
* Delay processing IPI flags until thread waiting disconnect
* has had a chance to see that the channel is disconnected.
*/
ch
->
delayed_IPI_flags
|=
IPI_flags
;
spin_unlock_irqrestore
(
&
ch
->
lock
,
irq_flags
);
return
;
}
if
(
IPI_flags
&
XPC_IPI_CLOSEREQUEST
)
{
...
...
@@ -764,7 +850,7 @@ xpc_process_openclose_IPI(struct xpc_partition *part, int ch_number,
/*
* If RCLOSEREQUEST is set, we're probably waiting for
* RCLOSEREPLY. We should find it and a ROPENREQUEST packed
* with this RCLOSE
QREUQ
EST in the IPI_flags.
* with this RCLOSE
REQU
EST in the IPI_flags.
*/
if
(
ch
->
flags
&
XPC_C_RCLOSEREQUEST
)
{
...
...
@@ -779,14 +865,22 @@ xpc_process_openclose_IPI(struct xpc_partition *part, int ch_number,
/* both sides have finished disconnecting */
xpc_process_disconnect
(
ch
,
&
irq_flags
);
DBUG_ON
(
!
(
ch
->
flags
&
XPC_C_DISCONNECTED
));
goto
again
;
}
if
(
ch
->
flags
&
XPC_C_DISCONNECTED
)
{
// >>> explain this section
if
(
!
(
IPI_flags
&
XPC_IPI_OPENREQUEST
))
{
DBUG_ON
(
part
->
act_state
!=
XPC_P_DEACTIVATING
);
if
((
XPC_GET_IPI_FLAGS
(
part
->
local_IPI_amo
,
ch_number
)
&
XPC_IPI_OPENREQUEST
))
{
DBUG_ON
(
ch
->
delayed_IPI_flags
!=
0
);
spin_lock
(
&
part
->
IPI_lock
);
XPC_SET_IPI_FLAGS
(
part
->
local_IPI_amo
,
ch_number
,
XPC_IPI_CLOSEREQUEST
);
spin_unlock
(
&
part
->
IPI_lock
);
}
spin_unlock_irqrestore
(
&
ch
->
lock
,
irq_flags
);
return
;
}
...
...
@@ -816,9 +910,13 @@ xpc_process_openclose_IPI(struct xpc_partition *part, int ch_number,
}
XPC_DISCONNECT_CHANNEL
(
ch
,
reason
,
&
irq_flags
);
}
else
{
xpc_process_disconnect
(
ch
,
&
irq_flags
);
DBUG_ON
(
IPI_flags
&
XPC_IPI_CLOSEREPLY
);
spin_unlock_irqrestore
(
&
ch
->
lock
,
irq_flags
);
return
;
}
xpc_process_disconnect
(
ch
,
&
irq_flags
);
}
...
...
@@ -834,7 +932,20 @@ xpc_process_openclose_IPI(struct xpc_partition *part, int ch_number,
}
DBUG_ON
(
!
(
ch
->
flags
&
XPC_C_CLOSEREQUEST
));
DBUG_ON
(
!
(
ch
->
flags
&
XPC_C_RCLOSEREQUEST
));
if
(
!
(
ch
->
flags
&
XPC_C_RCLOSEREQUEST
))
{
if
((
XPC_GET_IPI_FLAGS
(
part
->
local_IPI_amo
,
ch_number
)
&
XPC_IPI_CLOSEREQUEST
))
{
DBUG_ON
(
ch
->
delayed_IPI_flags
!=
0
);
spin_lock
(
&
part
->
IPI_lock
);
XPC_SET_IPI_FLAGS
(
part
->
local_IPI_amo
,
ch_number
,
XPC_IPI_CLOSEREPLY
);
spin_unlock
(
&
part
->
IPI_lock
);
}
spin_unlock_irqrestore
(
&
ch
->
lock
,
irq_flags
);
return
;
}
ch
->
flags
|=
XPC_C_RCLOSEREPLY
;
...
...
@@ -852,8 +963,14 @@ xpc_process_openclose_IPI(struct xpc_partition *part, int ch_number,
"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
)
{
if
(
part
->
act_state
==
XPC_P_DEACTIVATING
||
(
ch
->
flags
&
XPC_C_ROPENREQUEST
))
{
spin_unlock_irqrestore
(
&
ch
->
lock
,
irq_flags
);
return
;
}
if
(
ch
->
flags
&
(
XPC_C_DISCONNECTING
|
XPC_C_WDISCONNECT
))
{
ch
->
delayed_IPI_flags
|=
XPC_IPI_OPENREQUEST
;
spin_unlock_irqrestore
(
&
ch
->
lock
,
irq_flags
);
return
;
}
...
...
@@ -867,8 +984,11 @@ xpc_process_openclose_IPI(struct xpc_partition *part, int ch_number,
* msg_size = size of channel's messages in bytes
* local_nentries = remote partition's local_nentries
*/
DBUG_ON
(
args
->
msg_size
==
0
);
DBUG_ON
(
args
->
local_nentries
==
0
);
if
(
args
->
msg_size
==
0
||
args
->
local_nentries
==
0
)
{
/* assume OPENREQUEST was delayed by mistake */
spin_unlock_irqrestore
(
&
ch
->
lock
,
irq_flags
);
return
;
}
ch
->
flags
|=
(
XPC_C_ROPENREQUEST
|
XPC_C_CONNECTING
);
ch
->
remote_nentries
=
args
->
local_nentries
;
...
...
@@ -906,7 +1026,13 @@ xpc_process_openclose_IPI(struct xpc_partition *part, int ch_number,
spin_unlock_irqrestore
(
&
ch
->
lock
,
irq_flags
);
return
;
}
DBUG_ON
(
!
(
ch
->
flags
&
XPC_C_OPENREQUEST
));
if
(
!
(
ch
->
flags
&
XPC_C_OPENREQUEST
))
{
XPC_DISCONNECT_CHANNEL
(
ch
,
xpcOpenCloseError
,
&
irq_flags
);
spin_unlock_irqrestore
(
&
ch
->
lock
,
irq_flags
);
return
;
}
DBUG_ON
(
!
(
ch
->
flags
&
XPC_C_ROPENREQUEST
));
DBUG_ON
(
ch
->
flags
&
XPC_C_CONNECTED
);
...
...
@@ -960,8 +1086,8 @@ xpc_connect_channel(struct xpc_channel *ch)
struct
xpc_registration
*
registration
=
&
xpc_registrations
[
ch
->
number
];
if
(
down_
interruptible
(
&
registration
->
sema
)
!=
0
)
{
return
xpc
Interrupted
;
if
(
down_
trylock
(
&
registration
->
sema
)
!=
0
)
{
return
xpc
Retry
;
}
if
(
!
XPC_CHANNEL_REGISTERED
(
ch
->
number
))
{
...
...
@@ -1039,55 +1165,6 @@ xpc_connect_channel(struct xpc_channel *ch)
}
/*
* Notify those who wanted to be notified upon delivery of their message.
*/
static
void
xpc_notify_senders
(
struct
xpc_channel
*
ch
,
enum
xpc_retval
reason
,
s64
put
)
{
struct
xpc_notify
*
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
(
&
notify
->
type
,
notify_type
,
0
)
!=
notify_type
)
{
continue
;
}
DBUG_ON
(
notify_type
!=
XPC_N_CALL
);
atomic_dec
(
&
ch
->
n_to_notify
);
if
(
notify
->
func
!=
NULL
)
{
dev_dbg
(
xpc_chan
,
"notify->func() called, notify=0x%p, "
"msg_number=%ld, partid=%d, channel=%d
\n
"
,
(
void
*
)
notify
,
get
,
ch
->
partid
,
ch
->
number
);
notify
->
func
(
reason
,
ch
->
partid
,
ch
->
number
,
notify
->
key
);
dev_dbg
(
xpc_chan
,
"notify->func() returned, "
"notify=0x%p, msg_number=%ld, partid=%d, "
"channel=%d
\n
"
,
(
void
*
)
notify
,
get
,
ch
->
partid
,
ch
->
number
);
}
}
}
/*
* Clear some of the msg flags in the local message queue.
*/
...
...
@@ -1240,6 +1317,7 @@ xpc_process_channel_activity(struct xpc_partition *part)
u64
IPI_amo
,
IPI_flags
;
struct
xpc_channel
*
ch
;
int
ch_number
;
u32
ch_flags
;
IPI_amo
=
xpc_get_IPI_flags
(
part
);
...
...
@@ -1266,8 +1344,9 @@ xpc_process_channel_activity(struct xpc_partition *part)
xpc_process_openclose_IPI
(
part
,
ch_number
,
IPI_flags
);
}
ch_flags
=
ch
->
flags
;
/* need an atomic snapshot of flags */
if
(
ch
->
flags
&
XPC_C_DISCONNECTING
)
{
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
);
...
...
@@ -1278,9 +1357,9 @@ xpc_process_channel_activity(struct xpc_partition *part)
continue
;
}
if
(
!
(
ch
->
flags
&
XPC_C_CONNECTED
))
{
if
(
!
(
ch
->
flags
&
XPC_C_OPENREQUEST
))
{
DBUG_ON
(
ch
->
flags
&
XPC_C_SETUP
);
if
(
!
(
ch
_
flags
&
XPC_C_CONNECTED
))
{
if
(
!
(
ch
_
flags
&
XPC_C_OPENREQUEST
))
{
DBUG_ON
(
ch
_
flags
&
XPC_C_SETUP
);
(
void
)
xpc_connect_channel
(
ch
);
}
else
{
spin_lock_irqsave
(
&
ch
->
lock
,
irq_flags
);
...
...
@@ -1305,8 +1384,8 @@ xpc_process_channel_activity(struct xpc_partition *part)
/*
* XPC's heartbeat code calls this function to inform XPC that a partition
ha
s
* go
ne
down. XPC responds by tearing down the XPartition Communication
* XPC's heartbeat code calls this function to inform XPC that a partition
i
s
* go
ing
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()
...
...
@@ -1314,7 +1393,7 @@ xpc_process_channel_activity(struct xpc_partition *part)
* at the same time.
*/
void
xpc_partition_down
(
struct
xpc_partition
*
part
,
enum
xpc_retval
reason
)
xpc_partition_
going_
down
(
struct
xpc_partition
*
part
,
enum
xpc_retval
reason
)
{
unsigned
long
irq_flags
;
int
ch_number
;
...
...
@@ -1330,12 +1409,11 @@ xpc_partition_down(struct xpc_partition *part, enum xpc_retval reason)
}
/* disconnect
all channels associated with the downed partitio
n */
/* disconnect
channels associated with the partition going dow
n */
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
);
...
...
@@ -1370,6 +1448,7 @@ xpc_teardown_infrastructure(struct xpc_partition *part)
* this partition.
*/
DBUG_ON
(
atomic_read
(
&
part
->
nchannels_engaged
)
!=
0
);
DBUG_ON
(
atomic_read
(
&
part
->
nchannels_active
)
!=
0
);
DBUG_ON
(
part
->
setup_state
!=
XPC_P_SETUP
);
part
->
setup_state
=
XPC_P_WTEARDOWN
;
...
...
@@ -1428,19 +1507,11 @@ xpc_initiate_connect(int ch_number)
if
(
xpc_part_ref
(
part
))
{
ch
=
&
part
->
channels
[
ch_number
];
if
(
!
(
ch
->
flags
&
XPC_C_DISCONNECTING
))
{
DBUG_ON
(
ch
->
flags
&
XPC_C_OPENREQUEST
);
DBUG_ON
(
ch
->
flags
&
XPC_C_CONNECTED
);
DBUG_ON
(
ch
->
flags
&
XPC_C_SETUP
);
/*
* Initiate the establishment of a connection
* on the newly registered channel to the
* remote partition.
*/
xpc_wakeup_channel_mgr
(
part
);
}
/*
* Initiate the establishment of a connection on the
* newly registered channel to the remote partition.
*/
xpc_wakeup_channel_mgr
(
part
);
xpc_part_deref
(
part
);
}
}
...
...
@@ -1450,9 +1521,6 @@ xpc_initiate_connect(int ch_number)
void
xpc_connected_callout
(
struct
xpc_channel
*
ch
)
{
unsigned
long
irq_flags
;
/* let the registerer know that a connection has been established */
if
(
ch
->
func
!=
NULL
)
{
...
...
@@ -1465,10 +1533,6 @@ xpc_connected_callout(struct xpc_channel *ch)
dev_dbg
(
xpc_chan
,
"ch->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
);
}
...
...
@@ -1506,8 +1570,12 @@ xpc_initiate_disconnect(int ch_number)
spin_lock_irqsave
(
&
ch
->
lock
,
irq_flags
);
XPC_DISCONNECT_CHANNEL
(
ch
,
xpcUnregistering
,
if
(
!
(
ch
->
flags
&
XPC_C_DISCONNECTED
))
{
ch
->
flags
|=
XPC_C_WDISCONNECT
;
XPC_DISCONNECT_CHANNEL
(
ch
,
xpcUnregistering
,
&
irq_flags
);
}
spin_unlock_irqrestore
(
&
ch
->
lock
,
irq_flags
);
...
...
@@ -1523,8 +1591,9 @@ xpc_initiate_disconnect(int 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().
* An OPEN is not allowed until XPC_C_DISCONNECTING is cleared by
* xpc_process_disconnect(), and if set, XPC_C_WDISCONNECT is cleared by
* xpc_disconnect_wait().
*
* THE CHANNEL IS TO BE LOCKED BY THE CALLER AND WILL REMAIN LOCKED UPON RETURN.
*/
...
...
@@ -1532,7 +1601,7 @@ void
xpc_disconnect_channel
(
const
int
line
,
struct
xpc_channel
*
ch
,
enum
xpc_retval
reason
,
unsigned
long
*
irq_flags
)
{
u32
flags
;
u32
channel_was_connected
=
(
ch
->
flags
&
XPC_C_CONNECTED
)
;
DBUG_ON
(
!
spin_is_locked
(
&
ch
->
lock
));
...
...
@@ -1547,61 +1616,53 @@ xpc_disconnect_channel(const int line, struct xpc_channel *ch,
XPC_SET_REASON
(
ch
,
reason
,
line
);
flags
=
ch
->
flags
;
ch
->
flags
|=
(
XPC_C_CLOSEREQUEST
|
XPC_C_DISCONNECTING
)
;
/* 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
)
{
if
(
channel_was_connected
)
{
ch
->
flags
|=
XPC_C_WASCONNECTED
;
}
spin_unlock_irqrestore
(
&
ch
->
lock
,
*
irq_flags
);
/* wake all idle kthreads so they can exit */
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_disconnect
ed
_callout
(
struct
xpc_channel
*
ch
)
xpc_disconnect
ing
_callout
(
struct
xpc_channel
*
ch
)
{
/*
* Let the channel's registerer know that the channel is
now
* Let the channel's registerer know that the channel is
being
* 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.
* informed of a connection being made.
*/
if
(
ch
->
func
!=
NULL
)
{
dev_dbg
(
xpc_chan
,
"ch->func() called, reason=
%d, partid=%d,
"
"
channel=%d
\n
"
,
ch
->
reason
,
ch
->
partid
,
ch
->
number
);
dev_dbg
(
xpc_chan
,
"ch->func() called, reason=
xpcDisconnecting,
"
"
partid=%d, channel=%d
\n
"
,
ch
->
partid
,
ch
->
number
);
ch
->
func
(
ch
->
reason
,
ch
->
partid
,
ch
->
number
,
NULL
,
ch
->
key
);
ch
->
func
(
xpcDisconnecting
,
ch
->
partid
,
ch
->
number
,
NULL
,
ch
->
key
);
dev_dbg
(
xpc_chan
,
"ch->func() returned, reason=%d, partid=%d, "
"channel=%d
\n
"
,
ch
->
reason
,
ch
->
partid
,
ch
->
number
);
dev_dbg
(
xpc_chan
,
"ch->func() returned, reason="
"xpcDisconnecting, partid=%d, channel=%d
\n
"
,
ch
->
partid
,
ch
->
number
);
}
}
...
...
@@ -1848,7 +1909,7 @@ xpc_send_msg(struct xpc_channel *ch, struct xpc_msg *msg, u8 notify_type,
xpc_notify_func
func
,
void
*
key
)
{
enum
xpc_retval
ret
=
xpcSuccess
;
struct
xpc_notify
*
notify
=
NULL
;
// >>> to keep the compiler happy!!
struct
xpc_notify
*
notify
=
notify
;
s64
put
,
msg_number
=
msg
->
number
;
...
...
arch/ia64/sn/kernel/xpc_main.c
View file @
fac84ef2
...
...
@@ -54,6 +54,7 @@
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/reboot.h>
#include <asm/sn/intr.h>
#include <asm/sn/sn_sal.h>
#include <asm/uaccess.h>
...
...
@@ -82,11 +83,17 @@ struct device *xpc_chan = &xpc_chan_dbg_subname;
/* systune related variables for /proc/sys directories */
static
int
xpc_hb_min
=
1
;
static
int
xpc_hb_max
=
10
;
static
int
xpc_hb_interval
=
XPC_HB_DEFAULT_INTERVAL
;
static
int
xpc_hb_min_interval
=
1
;
static
int
xpc_hb_max_interval
=
10
;
static
int
xpc_hb_check_min
=
10
;
static
int
xpc_hb_check_max
=
120
;
static
int
xpc_hb_check_interval
=
XPC_HB_CHECK_DEFAULT_INTERVAL
;
static
int
xpc_hb_check_min_interval
=
10
;
static
int
xpc_hb_check_max_interval
=
120
;
int
xpc_disengage_request_timelimit
=
XPC_DISENGAGE_REQUEST_DEFAULT_TIMELIMIT
;
static
int
xpc_disengage_request_min_timelimit
=
0
;
static
int
xpc_disengage_request_max_timelimit
=
120
;
static
ctl_table
xpc_sys_xpc_hb_dir
[]
=
{
{
...
...
@@ -99,7 +106,8 @@ static ctl_table xpc_sys_xpc_hb_dir[] = {
&
proc_dointvec_minmax
,
&
sysctl_intvec
,
NULL
,
&
xpc_hb_min
,
&
xpc_hb_max
&
xpc_hb_min_interval
,
&
xpc_hb_max_interval
},
{
2
,
...
...
@@ -111,7 +119,8 @@ static ctl_table xpc_sys_xpc_hb_dir[] = {
&
proc_dointvec_minmax
,
&
sysctl_intvec
,
NULL
,
&
xpc_hb_check_min
,
&
xpc_hb_check_max
&
xpc_hb_check_min_interval
,
&
xpc_hb_check_max_interval
},
{
0
}
};
...
...
@@ -124,6 +133,19 @@ static ctl_table xpc_sys_xpc_dir[] = {
0555
,
xpc_sys_xpc_hb_dir
},
{
2
,
"disengage_request_timelimit"
,
&
xpc_disengage_request_timelimit
,
sizeof
(
int
),
0644
,
NULL
,
&
proc_dointvec_minmax
,
&
sysctl_intvec
,
NULL
,
&
xpc_disengage_request_min_timelimit
,
&
xpc_disengage_request_max_timelimit
},
{
0
}
};
static
ctl_table
xpc_sys_dir
[]
=
{
...
...
@@ -148,10 +170,10 @@ static DECLARE_WAIT_QUEUE_HEAD(xpc_act_IRQ_wq);
static
unsigned
long
xpc_hb_check_timeout
;
/*
xpc_hb_checker thread exited notification
*/
/*
notification that the xpc_hb_checker thread has exited
*/
static
DECLARE_MUTEX_LOCKED
(
xpc_hb_checker_exited
);
/*
xpc_discovery thread exited notification
*/
/*
notification that the xpc_discovery thread has exited
*/
static
DECLARE_MUTEX_LOCKED
(
xpc_discovery_exited
);
...
...
@@ -161,6 +183,30 @@ static struct timer_list xpc_hb_timer;
static
void
xpc_kthread_waitmsgs
(
struct
xpc_partition
*
,
struct
xpc_channel
*
);
static
int
xpc_system_reboot
(
struct
notifier_block
*
,
unsigned
long
,
void
*
);
static
struct
notifier_block
xpc_reboot_notifier
=
{
.
notifier_call
=
xpc_system_reboot
,
};
/*
* Timer function to enforce the timelimit on the partition disengage request.
*/
static
void
xpc_timeout_partition_disengage_request
(
unsigned
long
data
)
{
struct
xpc_partition
*
part
=
(
struct
xpc_partition
*
)
data
;
DBUG_ON
(
jiffies
<
part
->
disengage_request_timeout
);
(
void
)
xpc_partition_disengaged
(
part
);
DBUG_ON
(
part
->
disengage_request_timeout
!=
0
);
DBUG_ON
(
xpc_partition_engaged
(
1UL
<<
XPC_PARTID
(
part
))
!=
0
);
}
/*
* Notify the heartbeat check thread that an IRQ has been received.
*/
...
...
@@ -214,12 +260,6 @@ xpc_hb_checker(void *ignore)
while
(
!
(
volatile
int
)
xpc_exiting
)
{
/* wait for IRQ or timeout */
(
void
)
wait_event_interruptible
(
xpc_act_IRQ_wq
,
(
last_IRQ_count
<
atomic_read
(
&
xpc_act_IRQ_rcvd
)
||
jiffies
>=
xpc_hb_check_timeout
||
(
volatile
int
)
xpc_exiting
));
dev_dbg
(
xpc_part
,
"woke up with %d ticks rem; %d IRQs have "
"been received
\n
"
,
(
int
)
(
xpc_hb_check_timeout
-
jiffies
),
...
...
@@ -240,6 +280,7 @@ xpc_hb_checker(void *ignore)
}
/* check for outstanding IRQs */
new_IRQ_count
=
atomic_read
(
&
xpc_act_IRQ_rcvd
);
if
(
last_IRQ_count
<
new_IRQ_count
||
force_IRQ
!=
0
)
{
force_IRQ
=
0
;
...
...
@@ -257,12 +298,18 @@ xpc_hb_checker(void *ignore)
xpc_hb_check_timeout
=
jiffies
+
(
xpc_hb_check_interval
*
HZ
);
}
/* wait for IRQ or timeout */
(
void
)
wait_event_interruptible
(
xpc_act_IRQ_wq
,
(
last_IRQ_count
<
atomic_read
(
&
xpc_act_IRQ_rcvd
)
||
jiffies
>=
xpc_hb_check_timeout
||
(
volatile
int
)
xpc_exiting
));
}
dev_dbg
(
xpc_part
,
"heartbeat checker is exiting
\n
"
);
/* mark this thread as
inactive
*/
/* mark this thread as
having exited
*/
up
(
&
xpc_hb_checker_exited
);
return
0
;
}
...
...
@@ -282,7 +329,7 @@ xpc_initiate_discovery(void *ignore)
dev_dbg
(
xpc_part
,
"discovery thread is exiting
\n
"
);
/* mark this thread as
inactive
*/
/* mark this thread as
having exited
*/
up
(
&
xpc_discovery_exited
);
return
0
;
}
...
...
@@ -309,7 +356,7 @@ xpc_make_first_contact(struct xpc_partition *part)
"partition %d
\n
"
,
XPC_PARTID
(
part
));
/* wait a 1/4 of a second or so */
msleep_interruptible
(
250
);
(
void
)
msleep_interruptible
(
250
);
if
(
part
->
act_state
==
XPC_P_DEACTIVATING
)
{
return
part
->
reason
;
...
...
@@ -336,7 +383,8 @@ static void
xpc_channel_mgr
(
struct
xpc_partition
*
part
)
{
while
(
part
->
act_state
!=
XPC_P_DEACTIVATING
||
atomic_read
(
&
part
->
nchannels_active
)
>
0
)
{
atomic_read
(
&
part
->
nchannels_active
)
>
0
||
!
xpc_partition_disengaged
(
part
))
{
xpc_process_channel_activity
(
part
);
...
...
@@ -360,7 +408,8 @@ xpc_channel_mgr(struct xpc_partition *part)
(
volatile
u64
)
part
->
local_IPI_amo
!=
0
||
((
volatile
u8
)
part
->
act_state
==
XPC_P_DEACTIVATING
&&
atomic_read
(
&
part
->
nchannels_active
)
==
0
)));
atomic_read
(
&
part
->
nchannels_active
)
==
0
&&
xpc_partition_disengaged
(
part
))));
atomic_set
(
&
part
->
channel_mgr_requests
,
1
);
// >>> Does it need to wakeup periodically as well? In case we
...
...
@@ -482,7 +531,7 @@ xpc_activating(void *__partid)
return
0
;
}
XPC_ALLOW_HB
(
partid
,
xpc_vars
);
xpc_allow_hb
(
partid
,
xpc_vars
);
xpc_IPI_send_activated
(
part
);
...
...
@@ -492,6 +541,7 @@ xpc_activating(void *__partid)
*/
(
void
)
xpc_partition_up
(
part
);
xpc_disallow_hb
(
partid
,
xpc_vars
);
xpc_mark_partition_inactive
(
part
);
if
(
part
->
reason
==
xpcReactivating
)
{
...
...
@@ -670,6 +720,7 @@ xpc_daemonize_kthread(void *args)
struct
xpc_partition
*
part
=
&
xpc_partitions
[
partid
];
struct
xpc_channel
*
ch
;
int
n_needed
;
unsigned
long
irq_flags
;
daemonize
(
"xpc%02dc%d"
,
partid
,
ch_number
);
...
...
@@ -680,11 +731,14 @@ xpc_daemonize_kthread(void *args)
ch
=
&
part
->
channels
[
ch_number
];
if
(
!
(
ch
->
flags
&
XPC_C_DISCONNECTING
))
{
DBUG_ON
(
!
(
ch
->
flags
&
XPC_C_CONNECTED
));
/* let registerer know that connection has been established */
if
(
atomic_read
(
&
ch
->
kthreads_assigned
)
==
1
)
{
spin_lock_irqsave
(
&
ch
->
lock
,
irq_flags
);
if
(
!
(
ch
->
flags
&
XPC_C_CONNECTCALLOUT
))
{
ch
->
flags
|=
XPC_C_CONNECTCALLOUT
;
spin_unlock_irqrestore
(
&
ch
->
lock
,
irq_flags
);
xpc_connected_callout
(
ch
);
/*
...
...
@@ -699,16 +753,28 @@ xpc_daemonize_kthread(void *args)
!
(
ch
->
flags
&
XPC_C_DISCONNECTING
))
{
xpc_activate_kthreads
(
ch
,
n_needed
);
}
}
else
{
spin_unlock_irqrestore
(
&
ch
->
lock
,
irq_flags
);
}
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
);
if
(
atomic_dec_return
(
&
ch
->
kthreads_assigned
)
==
0
)
{
spin_lock_irqsave
(
&
ch
->
lock
,
irq_flags
);
if
((
ch
->
flags
&
XPC_C_CONNECTCALLOUT
)
&&
!
(
ch
->
flags
&
XPC_C_DISCONNECTCALLOUT
))
{
ch
->
flags
|=
XPC_C_DISCONNECTCALLOUT
;
spin_unlock_irqrestore
(
&
ch
->
lock
,
irq_flags
);
xpc_disconnecting_callout
(
ch
);
}
else
{
spin_unlock_irqrestore
(
&
ch
->
lock
,
irq_flags
);
}
if
(
atomic_dec_return
(
&
part
->
nchannels_engaged
)
==
0
)
{
xpc_mark_partition_disengaged
(
part
);
xpc_IPI_send_disengage
(
part
);
}
}
...
...
@@ -740,12 +806,33 @@ xpc_create_kthreads(struct xpc_channel *ch, int needed)
unsigned
long
irq_flags
;
pid_t
pid
;
u64
args
=
XPC_PACK_ARGS
(
ch
->
partid
,
ch
->
number
);
struct
xpc_partition
*
part
=
&
xpc_partitions
[
ch
->
partid
];
while
(
needed
--
>
0
)
{
/*
* 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
(
part
);
xpc_msgqueue_ref
(
ch
);
if
(
atomic_inc_return
(
&
ch
->
kthreads_assigned
)
==
1
&&
atomic_inc_return
(
&
part
->
nchannels_engaged
)
==
1
)
{
xpc_mark_partition_engaged
(
part
);
}
pid
=
kernel_thread
(
xpc_daemonize_kthread
,
(
void
*
)
args
,
0
);
if
(
pid
<
0
)
{
/* the fork failed */
if
(
atomic_dec_return
(
&
ch
->
kthreads_assigned
)
==
0
&&
atomic_dec_return
(
&
part
->
nchannels_engaged
)
==
0
)
{
xpc_mark_partition_disengaged
(
part
);
xpc_IPI_send_disengage
(
part
);
}
xpc_msgqueue_deref
(
ch
);
xpc_part_deref
(
part
);
if
(
atomic_read
(
&
ch
->
kthreads_assigned
)
<
ch
->
kthreads_idle_limit
)
{
...
...
@@ -765,14 +852,6 @@ xpc_create_kthreads(struct xpc_channel *ch, int needed)
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!!!
}
}
...
...
@@ -781,87 +860,142 @@ xpc_create_kthreads(struct xpc_channel *ch, int needed)
void
xpc_disconnect_wait
(
int
ch_number
)
{
unsigned
long
irq_flags
;
partid_t
partid
;
struct
xpc_partition
*
part
;
struct
xpc_channel
*
ch
;
int
wakeup_channel_mgr
;
/* now wait for all callouts to the caller's function to cease */
for
(
partid
=
1
;
partid
<
XP_MAX_PARTITIONS
;
partid
++
)
{
part
=
&
xpc_partitions
[
partid
];
if
(
xpc_part_ref
(
part
))
{
ch
=
&
part
->
channels
[
ch_number
];
if
(
!
xpc_part_ref
(
part
))
{
continue
;
}
// >>> 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
);
}
ch
=
&
part
->
channels
[
ch_number
];
if
(
!
(
ch
->
flags
&
XPC_C_WDISCONNECT
))
{
xpc_part_deref
(
part
);
continue
;
}
(
void
)
down
(
&
ch
->
wdisconnect_sema
);
spin_lock_irqsave
(
&
ch
->
lock
,
irq_flags
);
DBUG_ON
(
!
(
ch
->
flags
&
XPC_C_DISCONNECTED
));
wakeup_channel_mgr
=
0
;
if
(
ch
->
delayed_IPI_flags
)
{
if
(
part
->
act_state
!=
XPC_P_DEACTIVATING
)
{
spin_lock
(
&
part
->
IPI_lock
);
XPC_SET_IPI_FLAGS
(
part
->
local_IPI_amo
,
ch
->
number
,
ch
->
delayed_IPI_flags
);
spin_unlock
(
&
part
->
IPI_lock
);
wakeup_channel_mgr
=
1
;
}
ch
->
delayed_IPI_flags
=
0
;
}
ch
->
flags
&=
~
XPC_C_WDISCONNECT
;
spin_unlock_irqrestore
(
&
ch
->
lock
,
irq_flags
);
if
(
wakeup_channel_mgr
)
{
xpc_wakeup_channel_mgr
(
part
);
}
xpc_part_deref
(
part
);
}
}
static
void
xpc_do_exit
(
void
)
xpc_do_exit
(
enum
xpc_retval
reason
)
{
partid_t
partid
;
int
active_part_count
;
struct
xpc_partition
*
part
;
unsigned
long
printmsg_time
;
/* 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
);
/* a 'rmmod XPC' and a 'reboot' cannot both end up here together */
DBUG_ON
(
xpc_exiting
==
1
);
/*
*
Cause the heartbeat checker and the discovery threads to exit.
*
We don't want them attempting to activate new partitions as we
* t
ry to deactivate the existing ones
.
*
Let the heartbeat checker thread and the discovery thread
*
(if one is running) know that they should exit. Also wake up
* t
he heartbeat checker thread in case it's sleeping
.
*/
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
);
/*
ignore all incoming interrupts
*/
free_irq
(
SGI_XPC_ACTIVATE
,
NULL
);
/* wait for the discovery thread to
mark itself inactive
*/
/* wait for the discovery thread to
exit
*/
down
(
&
xpc_discovery_exited
);
/* wait for the heartbeat checker thread to exit */
down
(
&
xpc_hb_checker_exited
);
msleep_interruptible
(
300
);
/* sleep for a 1/3 of a second or so */
(
void
)
msleep_interruptible
(
300
);
/* wait for all partitions to become inactive */
printmsg_time
=
jiffies
;
do
{
active_part_count
=
0
;
for
(
partid
=
1
;
partid
<
XP_MAX_PARTITIONS
;
partid
++
)
{
part
=
&
xpc_partitions
[
partid
];
if
(
part
->
act_state
!=
XPC_P_INACTIVE
)
{
active_part_count
++
;
XPC_DEACTIVATE_PARTITION
(
part
,
xpcUnloading
);
if
(
xpc_partition_disengaged
(
part
)
&&
part
->
act_state
==
XPC_P_INACTIVE
)
{
continue
;
}
active_part_count
++
;
XPC_DEACTIVATE_PARTITION
(
part
,
reason
);
}
if
(
active_part_count
)
msleep_interruptible
(
300
)
;
}
while
(
active_part_count
>
0
);
if
(
active_part_count
==
0
)
{
break
;
}
if
(
jiffies
>=
printmsg_time
)
{
dev_info
(
xpc_part
,
"waiting for partitions to "
"deactivate/disengage, active count=%d, remote "
"engaged=0x%lx
\n
"
,
active_part_count
,
xpc_partition_engaged
(
1UL
<<
partid
));
printmsg_time
=
jiffies
+
(
XPC_DISENGAGE_PRINTMSG_INTERVAL
*
HZ
);
}
/* sleep for a 1/3 of a second or so */
(
void
)
msleep_interruptible
(
300
);
}
while
(
1
);
DBUG_ON
(
xpc_partition_engaged
(
-
1UL
));
/* indicate to others that our reserved page is uninitialized */
xpc_rsvd_page
->
vars_pa
=
0
;
/* now it's time to eliminate our heartbeat */
del_timer_sync
(
&
xpc_hb_timer
);
DBUG_ON
(
xpc_vars
->
heartbeating_to_mask
!=
0
);
/* take ourselves off of the reboot_notifier_list */
(
void
)
unregister_reboot_notifier
(
&
xpc_reboot_notifier
);
/* close down protections for IPI operations */
xpc_restrict_IPI_ops
();
...
...
@@ -876,6 +1010,34 @@ xpc_do_exit(void)
}
/*
* This function is called when the system is being rebooted.
*/
static
int
xpc_system_reboot
(
struct
notifier_block
*
nb
,
unsigned
long
event
,
void
*
unused
)
{
enum
xpc_retval
reason
;
switch
(
event
)
{
case
SYS_RESTART
:
reason
=
xpcSystemReboot
;
break
;
case
SYS_HALT
:
reason
=
xpcSystemHalt
;
break
;
case
SYS_POWER_OFF
:
reason
=
xpcSystemPoweroff
;
break
;
default:
reason
=
xpcSystemGoingDown
;
}
xpc_do_exit
(
reason
);
return
NOTIFY_DONE
;
}
int
__init
xpc_init
(
void
)
{
...
...
@@ -891,11 +1053,11 @@ xpc_init(void)
/*
* xpc_remote_copy_buffer is used as a temporary buffer for bte_copy'ng
*
both a partition's reserved page and its XPC variables. Its size was
*
based on the size of a reserved page. So we need to ensure that th
e
*
XPC variable
s will fit as well.
*
various portions of a partition's reserved page. Its size is based
*
on the size of the reserved page header and part_nasids mask. So w
e
*
need to ensure that the other item
s will fit as well.
*/
if
(
XPC_
VARS_ALIGNED_SIZE
>
XPC_RSVD_PAGE_ALIGNED_SIZE
)
{
if
(
XPC_
RP_VARS_SIZE
>
XPC_RP_HEADER_SIZE
+
XP_NASID_MASK_BYTES
)
{
dev_err
(
xpc_part
,
"xpc_remote_copy_buffer is not big enough
\n
"
);
return
-
EPERM
;
}
...
...
@@ -924,6 +1086,12 @@ xpc_init(void)
spin_lock_init
(
&
part
->
act_lock
);
part
->
act_state
=
XPC_P_INACTIVE
;
XPC_SET_REASON
(
part
,
0
,
0
);
init_timer
(
&
part
->
disengage_request_timer
);
part
->
disengage_request_timer
.
function
=
xpc_timeout_partition_disengage_request
;
part
->
disengage_request_timer
.
data
=
(
unsigned
long
)
part
;
part
->
setup_state
=
XPC_P_UNSET
;
init_waitqueue_head
(
&
part
->
teardown_wq
);
atomic_set
(
&
part
->
references
,
0
);
...
...
@@ -980,6 +1148,13 @@ xpc_init(void)
}
/* add ourselves to the reboot_notifier_list */
ret
=
register_reboot_notifier
(
&
xpc_reboot_notifier
);
if
(
ret
!=
0
)
{
dev_warn
(
xpc_part
,
"can't register reboot notifier
\n
"
);
}
/*
* Set the beating to other partitions into motion. This is
* the last requirement for other partitions' discovery to
...
...
@@ -1001,6 +1176,9 @@ xpc_init(void)
/* indicate to others that our reserved page is uninitialized */
xpc_rsvd_page
->
vars_pa
=
0
;
/* take ourselves off of the reboot_notifier_list */
(
void
)
unregister_reboot_notifier
(
&
xpc_reboot_notifier
);
del_timer_sync
(
&
xpc_hb_timer
);
free_irq
(
SGI_XPC_ACTIVATE
,
NULL
);
xpc_restrict_IPI_ops
();
...
...
@@ -1024,7 +1202,7 @@ xpc_init(void)
/* mark this new thread as a non-starter */
up
(
&
xpc_discovery_exited
);
xpc_do_exit
();
xpc_do_exit
(
xpcUnloading
);
return
-
EBUSY
;
}
...
...
@@ -1043,7 +1221,7 @@ module_init(xpc_init);
void
__exit
xpc_exit
(
void
)
{
xpc_do_exit
();
xpc_do_exit
(
xpcUnloading
);
}
module_exit
(
xpc_exit
);
...
...
@@ -1060,3 +1238,7 @@ module_param(xpc_hb_check_interval, int, 0);
MODULE_PARM_DESC
(
xpc_hb_check_interval
,
"Number of seconds between "
"heartbeat checks."
);
module_param
(
xpc_disengage_request_timelimit
,
int
,
0
);
MODULE_PARM_DESC
(
xpc_disengage_request_timelimit
,
"Number of seconds to wait "
"for disengage request to complete."
);
arch/ia64/sn/kernel/xpc_partition.c
View file @
fac84ef2
...
...
@@ -47,13 +47,16 @@ static u64 xpc_sh2_IPI_access3;
u64
xpc_prot_vec
[
MAX_NUMNODES
];
/* this partition's reserved page */
/* this partition's reserved page
pointers
*/
struct
xpc_rsvd_page
*
xpc_rsvd_page
;
/* this partition's XPC variables (within the reserved page) */
static
u64
*
xpc_part_nasids
;
static
u64
*
xpc_mach_nasids
;
struct
xpc_vars
*
xpc_vars
;
struct
xpc_vars_part
*
xpc_vars_part
;
static
int
xp_nasid_mask_bytes
;
/* actual size in bytes of nasid mask */
static
int
xp_nasid_mask_words
;
/* actual size in words of nasid mask */
/*
* For performance reasons, each entry of xpc_partitions[] is cacheline
...
...
@@ -65,20 +68,16 @@ struct xpc_partition xpc_partitions[XP_MAX_PARTITIONS + 1];
/*
* Generic buffer used to store a local copy of the remote partitions
* reserved page or XPC variables.
* Generic buffer used to store a local copy of portions of a remote
* partition's reserved page (either its header and part_nasids mask,
* or its vars).
*
* 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
;
char
____cacheline_aligned
xpc_remote_copy_buffer
[
XPC_RP_HEADER_SIZE
+
XP_NASID_MASK_BYTES
];
/*
...
...
@@ -86,13 +85,16 @@ int xpc_hb_check_interval = XPC_HB_CHECK_DEFAULT_TIMEOUT;
* for that nasid. This function returns 0 on any error.
*/
static
u64
xpc_get_rsvd_page_pa
(
int
nasid
,
u64
buf
,
u64
buf_size
)
xpc_get_rsvd_page_pa
(
int
nasid
)
{
bte_result_t
bte_res
;
s64
status
;
u64
cookie
=
0
;
u64
rp_pa
=
nasid
;
/* seed with nasid */
u64
len
=
0
;
u64
buf
=
buf
;
u64
buf_len
=
0
;
void
*
buf_base
=
NULL
;
while
(
1
)
{
...
...
@@ -108,13 +110,22 @@ xpc_get_rsvd_page_pa(int nasid, u64 buf, u64 buf_size)
break
;
}
if
(
len
>
buf_size
)
{
dev_err
(
xpc_part
,
"len (=0x%016lx) > buf_size
\n
"
,
len
);
status
=
SALRET_ERROR
;
break
;
if
(
L1_CACHE_ALIGN
(
len
)
>
buf_len
)
{
if
(
buf_base
!=
NULL
)
{
kfree
(
buf_base
);
}
buf_len
=
L1_CACHE_ALIGN
(
len
);
buf
=
(
u64
)
xpc_kmalloc_cacheline_aligned
(
buf_len
,
GFP_KERNEL
,
&
buf_base
);
if
(
buf_base
==
NULL
)
{
dev_err
(
xpc_part
,
"unable to kmalloc "
"len=0x%016lx
\n
"
,
buf_len
);
status
=
SALRET_ERROR
;
break
;
}
}
bte_res
=
xp_bte_copy
(
rp_pa
,
ia64_tpa
(
buf
),
buf_
size
,
bte_res
=
xp_bte_copy
(
rp_pa
,
ia64_tpa
(
buf
),
buf_
len
,
(
BTE_NOTIFY
|
BTE_WACQUIRE
),
NULL
);
if
(
bte_res
!=
BTE_SUCCESS
)
{
dev_dbg
(
xpc_part
,
"xp_bte_copy failed %i
\n
"
,
bte_res
);
...
...
@@ -123,6 +134,10 @@ xpc_get_rsvd_page_pa(int nasid, u64 buf, u64 buf_size)
}
}
if
(
buf_base
!=
NULL
)
{
kfree
(
buf_base
);
}
if
(
status
!=
SALRET_OK
)
{
rp_pa
=
0
;
}
...
...
@@ -141,15 +156,15 @@ xpc_rsvd_page_init(void)
{
struct
xpc_rsvd_page
*
rp
;
AMO_t
*
amos_page
;
u64
rp_pa
,
n
ext_cl
,
n
asid_array
=
0
;
u64
rp_pa
,
nasid_array
=
0
;
int
i
,
ret
;
/* get the local reserved page's address */
rp_pa
=
xpc_get_rsvd_page_pa
(
cnodeid_to_nasid
(
0
),
(
u64
)
xpc_remote_copy_buffer
,
XPC_RSVD_PAGE_ALIGNED_SIZE
);
preempt_disable
();
rp_pa
=
xpc_get_rsvd_page_pa
(
cpuid_to_nasid
(
smp_processor_id
()));
preempt_enable
(
);
if
(
rp_pa
==
0
)
{
dev_err
(
xpc_part
,
"SAL failed to locate the reserved page
\n
"
);
return
NULL
;
...
...
@@ -164,12 +179,19 @@ xpc_rsvd_page_init(void)
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
=
(
struct
xpc_vars
*
)
next_cl
;
/* establish the actual sizes of the nasid masks */
if
(
rp
->
SAL_version
==
1
)
{
/* SAL_version 1 didn't set the nasids_size field */
rp
->
nasids_size
=
128
;
}
xp_nasid_mask_bytes
=
rp
->
nasids_size
;
xp_nasid_mask_words
=
xp_nasid_mask_bytes
/
8
;
/* setup the pointers to the various items in the reserved page */
xpc_part_nasids
=
XPC_RP_PART_NASIDS
(
rp
);
xpc_mach_nasids
=
XPC_RP_MACH_NASIDS
(
rp
);
xpc_vars
=
XPC_RP_VARS
(
rp
);
xpc_vars_part
=
XPC_RP_VARS_PART
(
rp
);
/*
* Before clearing xpc_vars, see if a page of AMOs had been previously
...
...
@@ -221,33 +243,32 @@ xpc_rsvd_page_init(void)
amos_page
=
(
AMO_t
*
)
TO_AMO
((
u64
)
amos_page
);
}
/* clear xpc_vars */
memset
(
xpc_vars
,
0
,
sizeof
(
struct
xpc_vars
));
/*
* 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
(
struct
xpc_vars_part
)
*
XP_MAX_PARTITIONS
);
xpc_vars_part
=
(
struct
xpc_vars_part
*
)
next_cl
;
xpc_vars
->
vars_part_pa
=
__pa
(
next_cl
);
xpc_vars
->
version
=
XPC_V_VERSION
;
xpc_vars
->
act_nasid
=
cpuid_to_nasid
(
0
);
xpc_vars
->
act_phys_cpuid
=
cpu_physical_id
(
0
);
xpc_vars
->
vars_part_pa
=
__pa
(
xpc_vars_part
);
xpc_vars
->
amos_page_pa
=
ia64_tpa
((
u64
)
amos_page
);
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
(
XP_MAX_PARTITIONS
);
for
(
i
=
1
;
i
<
XP_NASID_MASK_WORDS
;
i
++
)
{
xpc_IPI_init
(
i
+
XP_MAX_PARTITIONS
);
/* clear xpc_vars_part */
memset
((
u64
*
)
xpc_vars_part
,
0
,
sizeof
(
struct
xpc_vars_part
)
*
XP_MAX_PARTITIONS
);
/* initialize the activate IRQ related AMO variables */
for
(
i
=
0
;
i
<
xp_nasid_mask_words
;
i
++
)
{
(
void
)
xpc_IPI_init
(
XPC_ACTIVATE_IRQ_AMOS
+
i
);
}
/* export AMO page's physical address to other partitions */
xpc_vars
->
amos_page_pa
=
ia64_tpa
((
u64
)
xpc_vars
->
amos_page
);
/* initialize the engaged remote partitions related AMO variables */
(
void
)
xpc_IPI_init
(
XPC_ENGAGED_PARTITIONS_AMO
);
(
void
)
xpc_IPI_init
(
XPC_DISENGAGE_REQUEST_AMO
);
/* timestamp of when reserved page was setup by XPC */
rp
->
stamp
=
CURRENT_TIME
;
/*
* This signifies to the remote partition that our reserved
...
...
@@ -387,6 +408,11 @@ xpc_check_remote_hb(void)
remote_vars
=
(
struct
xpc_vars
*
)
xpc_remote_copy_buffer
;
for
(
partid
=
1
;
partid
<
XP_MAX_PARTITIONS
;
partid
++
)
{
if
(
xpc_exiting
)
{
break
;
}
if
(
partid
==
sn_partition_id
)
{
continue
;
}
...
...
@@ -401,7 +427,7 @@ xpc_check_remote_hb(void)
/* pull the remote_hb cache line */
bres
=
xp_bte_copy
(
part
->
remote_vars_pa
,
ia64_tpa
((
u64
)
remote_vars
),
XPC_
VARS_ALIGNED
_SIZE
,
XPC_
RP_VARS
_SIZE
,
(
BTE_NOTIFY
|
BTE_WACQUIRE
),
NULL
);
if
(
bres
!=
BTE_SUCCESS
)
{
XPC_DEACTIVATE_PARTITION
(
part
,
...
...
@@ -417,7 +443,7 @@ xpc_check_remote_hb(void)
if
(((
remote_vars
->
heartbeat
==
part
->
last_heartbeat
)
&&
(
remote_vars
->
kdb_status
==
0
))
||
!
XPC_HB_ALLOWED
(
sn_partition_id
,
remote_vars
))
{
!
xpc_hb_allowed
(
sn_partition_id
,
remote_vars
))
{
XPC_DEACTIVATE_PARTITION
(
part
,
xpcNoHeartbeat
);
continue
;
...
...
@@ -429,31 +455,31 @@ xpc_check_remote_hb(void)
/*
* Get a copy of the remote partition's rsvd page.
* Get a copy of
a portion 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.
* is large enough to contain a copy of their reserved page header and
* part_nasids mask.
*/
static
enum
xpc_retval
xpc_get_remote_rp
(
int
nasid
,
u64
*
discovered_nasids
,
struct
xpc_rsvd_page
*
remote_rp
,
u64
*
remote_r
svd_page
_pa
)
struct
xpc_rsvd_page
*
remote_rp
,
u64
*
remote_r
p
_pa
)
{
int
bres
,
i
;
/* 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
)
{
*
remote_rp_pa
=
xpc_get_rsvd_page_pa
(
nasid
);
if
(
*
remote_rp_pa
==
0
)
{
return
xpcNoRsvdPageAddr
;
}
/* pull over the reserved page
structure
*/
/* pull over the reserved page
header and part_nasids mask
*/
bres
=
xp_bte_copy
(
*
remote_r
svd_page
_pa
,
ia64_tpa
((
u64
)
remote_rp
),
XPC_R
SVD_PAGE_ALIGNED_SIZE
,
bres
=
xp_bte_copy
(
*
remote_r
p
_pa
,
ia64_tpa
((
u64
)
remote_rp
),
XPC_R
P_HEADER_SIZE
+
xp_nasid_mask_bytes
,
(
BTE_NOTIFY
|
BTE_WACQUIRE
),
NULL
);
if
(
bres
!=
BTE_SUCCESS
)
{
return
xpc_map_bte_errors
(
bres
);
...
...
@@ -461,8 +487,11 @@ xpc_get_remote_rp(int nasid, u64 *discovered_nasids,
if
(
discovered_nasids
!=
NULL
)
{
for
(
i
=
0
;
i
<
XP_NASID_MASK_WORDS
;
i
++
)
{
discovered_nasids
[
i
]
|=
remote_rp
->
part_nasids
[
i
];
u64
*
remote_part_nasids
=
XPC_RP_PART_NASIDS
(
remote_rp
);
for
(
i
=
0
;
i
<
xp_nasid_mask_words
;
i
++
)
{
discovered_nasids
[
i
]
|=
remote_part_nasids
[
i
];
}
}
...
...
@@ -489,10 +518,10 @@ xpc_get_remote_rp(int nasid, u64 *discovered_nasids,
/*
* Get a copy of the remote partition's XPC variables.
* Get a copy of the remote partition's XPC variables
from the reserved page
.
*
* remote_vars points to a buffer that is cacheline aligned for BTE copies and
* assumed to be of size XPC_
VARS_ALIGNED
_SIZE.
* assumed to be of size XPC_
RP_VARS
_SIZE.
*/
static
enum
xpc_retval
xpc_get_remote_vars
(
u64
remote_vars_pa
,
struct
xpc_vars
*
remote_vars
)
...
...
@@ -508,7 +537,7 @@ xpc_get_remote_vars(u64 remote_vars_pa, struct xpc_vars *remote_vars)
/* pull over the cross partition variables */
bres
=
xp_bte_copy
(
remote_vars_pa
,
ia64_tpa
((
u64
)
remote_vars
),
XPC_
VARS_ALIGNED
_SIZE
,
XPC_
RP_VARS
_SIZE
,
(
BTE_NOTIFY
|
BTE_WACQUIRE
),
NULL
);
if
(
bres
!=
BTE_SUCCESS
)
{
return
xpc_map_bte_errors
(
bres
);
...
...
@@ -524,7 +553,56 @@ xpc_get_remote_vars(u64 remote_vars_pa, struct xpc_vars *remote_vars)
/*
* Prior code has determine the nasid which generated an IPI. Inspect
* Update the remote partition's info.
*/
static
void
xpc_update_partition_info
(
struct
xpc_partition
*
part
,
u8
remote_rp_version
,
struct
timespec
*
remote_rp_stamp
,
u64
remote_rp_pa
,
u64
remote_vars_pa
,
struct
xpc_vars
*
remote_vars
)
{
part
->
remote_rp_version
=
remote_rp_version
;
dev_dbg
(
xpc_part
,
" remote_rp_version = 0x%016lx
\n
"
,
part
->
remote_rp_version
);
part
->
remote_rp_stamp
=
*
remote_rp_stamp
;
dev_dbg
(
xpc_part
,
" remote_rp_stamp (tv_sec = 0x%lx tv_nsec = 0x%lx
\n
"
,
part
->
remote_rp_stamp
.
tv_sec
,
part
->
remote_rp_stamp
.
tv_nsec
);
part
->
remote_rp_pa
=
remote_rp_pa
;
dev_dbg
(
xpc_part
,
" remote_rp_pa = 0x%016lx
\n
"
,
part
->
remote_rp_pa
);
part
->
remote_vars_pa
=
remote_vars_pa
;
dev_dbg
(
xpc_part
,
" remote_vars_pa = 0x%016lx
\n
"
,
part
->
remote_vars_pa
);
part
->
last_heartbeat
=
remote_vars
->
heartbeat
;
dev_dbg
(
xpc_part
,
" last_heartbeat = 0x%016lx
\n
"
,
part
->
last_heartbeat
);
part
->
remote_vars_part_pa
=
remote_vars
->
vars_part_pa
;
dev_dbg
(
xpc_part
,
" remote_vars_part_pa = 0x%016lx
\n
"
,
part
->
remote_vars_part_pa
);
part
->
remote_act_nasid
=
remote_vars
->
act_nasid
;
dev_dbg
(
xpc_part
,
" remote_act_nasid = 0x%x
\n
"
,
part
->
remote_act_nasid
);
part
->
remote_act_phys_cpuid
=
remote_vars
->
act_phys_cpuid
;
dev_dbg
(
xpc_part
,
" remote_act_phys_cpuid = 0x%x
\n
"
,
part
->
remote_act_phys_cpuid
);
part
->
remote_amos_page_pa
=
remote_vars
->
amos_page_pa
;
dev_dbg
(
xpc_part
,
" remote_amos_page_pa = 0x%lx
\n
"
,
part
->
remote_amos_page_pa
);
part
->
remote_vars_version
=
remote_vars
->
version
;
dev_dbg
(
xpc_part
,
" remote_vars_version = 0x%x
\n
"
,
part
->
remote_vars_version
);
}
/*
* Prior code has determined the nasid which generated an IPI. Inspect
* that nasid to determine if its partition needs to be activated or
* deactivated.
*
...
...
@@ -542,8 +620,12 @@ xpc_identify_act_IRQ_req(int nasid)
{
struct
xpc_rsvd_page
*
remote_rp
;
struct
xpc_vars
*
remote_vars
;
u64
remote_r
svd_page
_pa
;
u64
remote_r
p
_pa
;
u64
remote_vars_pa
;
int
remote_rp_version
;
int
reactivate
=
0
;
int
stamp_diff
;
struct
timespec
remote_rp_stamp
=
{
0
,
0
};
partid_t
partid
;
struct
xpc_partition
*
part
;
enum
xpc_retval
ret
;
...
...
@@ -553,7 +635,7 @@ xpc_identify_act_IRQ_req(int nasid)
remote_rp
=
(
struct
xpc_rsvd_page
*
)
xpc_remote_copy_buffer
;
ret
=
xpc_get_remote_rp
(
nasid
,
NULL
,
remote_rp
,
&
remote_r
svd_page
_pa
);
ret
=
xpc_get_remote_rp
(
nasid
,
NULL
,
remote_rp
,
&
remote_r
p
_pa
);
if
(
ret
!=
xpcSuccess
)
{
dev_warn
(
xpc_part
,
"unable to get reserved page from nasid %d, "
"which sent interrupt, reason=%d
\n
"
,
nasid
,
ret
);
...
...
@@ -561,6 +643,10 @@ xpc_identify_act_IRQ_req(int nasid)
}
remote_vars_pa
=
remote_rp
->
vars_pa
;
remote_rp_version
=
remote_rp
->
version
;
if
(
XPC_SUPPORTS_RP_STAMP
(
remote_rp_version
))
{
remote_rp_stamp
=
remote_rp
->
stamp
;
}
partid
=
remote_rp
->
partid
;
part
=
&
xpc_partitions
[
partid
];
...
...
@@ -586,44 +672,117 @@ xpc_identify_act_IRQ_req(int nasid)
"%ld:0x%lx
\n
"
,
(
int
)
nasid
,
(
int
)
partid
,
part
->
act_IRQ_rcvd
,
remote_vars
->
heartbeat
,
remote_vars
->
heartbeating_to_mask
);
if
(
xpc_partition_disengaged
(
part
)
&&
part
->
act_state
==
XPC_P_INACTIVE
)
{
if
(
part
->
act_state
==
XPC_P_INACTIVE
)
{
xpc_update_partition_info
(
part
,
remote_rp_version
,
&
remote_rp_stamp
,
remote_rp_pa
,
remote_vars_pa
,
remote_vars
);
part
->
remote_rp_pa
=
remote_rsvd_page_pa
;
dev_dbg
(
xpc_part
,
" remote_rp_pa = 0x%016lx
\n
"
,
part
->
remote_rp_pa
);
if
(
XPC_SUPPORTS_DISENGAGE_REQUEST
(
part
->
remote_vars_version
))
{
if
(
xpc_partition_disengage_requested
(
1UL
<<
partid
))
{
/*
* Other side is waiting on us to disengage,
* even though we already have.
*/
return
;
}
}
else
{
/* other side doesn't support disengage requests */
xpc_clear_partition_disengage_request
(
1UL
<<
partid
);
}
part
->
remote_vars_pa
=
remote_vars_pa
;
dev_dbg
(
xpc_part
,
" remote_vars_pa = 0x%016lx
\n
"
,
part
->
remote_vars_pa
);
xpc_activate_partition
(
part
)
;
return
;
}
part
->
last_heartbeat
=
remote_vars
->
heartbeat
;
dev_dbg
(
xpc_part
,
" last_heartbeat = 0x%016lx
\n
"
,
part
->
last_heartbeat
);
DBUG_ON
(
part
->
remote_rp_version
==
0
);
DBUG_ON
(
part
->
remote_vars_version
==
0
);
if
(
!
XPC_SUPPORTS_RP_STAMP
(
part
->
remote_rp_version
))
{
DBUG_ON
(
XPC_SUPPORTS_DISENGAGE_REQUEST
(
part
->
remote_vars_version
));
if
(
!
XPC_SUPPORTS_RP_STAMP
(
remote_rp_version
))
{
DBUG_ON
(
XPC_SUPPORTS_DISENGAGE_REQUEST
(
remote_vars
->
version
));
/* see if the other side rebooted */
if
(
part
->
remote_amos_page_pa
==
remote_vars
->
amos_page_pa
&&
xpc_hb_allowed
(
sn_partition_id
,
remote_vars
))
{
/* doesn't look that way, so ignore the IPI */
return
;
}
}
part
->
remote_vars_part_pa
=
remote_vars
->
vars_part_pa
;
dev_dbg
(
xpc_part
,
" remote_vars_part_pa = 0x%016lx
\n
"
,
part
->
remote_vars_part_pa
);
/*
* Other side rebooted and previous XPC didn't support the
* disengage request, so we don't need to do anything special.
*/
part
->
remote_act_nasid
=
remote_vars
->
act_nasid
;
dev_dbg
(
xpc_part
,
" remote_act_nasid = 0x%x
\n
"
,
part
->
remote_act_nasid
);
xpc_update_partition_info
(
part
,
remote_rp_version
,
&
remote_rp_stamp
,
remote_rp_pa
,
remote_vars_pa
,
remote_vars
);
part
->
reactivate_nasid
=
nasid
;
XPC_DEACTIVATE_PARTITION
(
part
,
xpcReactivating
);
return
;
}
part
->
remote_act_phys_cpuid
=
remote_vars
->
act_phys_cpuid
;
dev_dbg
(
xpc_part
,
" remote_act_phys_cpuid = 0x%x
\n
"
,
part
->
remote_act_phys_cpuid
);
DBUG_ON
(
!
XPC_SUPPORTS_DISENGAGE_REQUEST
(
part
->
remote_vars_version
));
part
->
remote_amos_page_pa
=
remote_vars
->
amos_page_pa
;
dev_dbg
(
xpc_part
,
" remote_amos_page_pa = 0x%lx
\n
"
,
part
->
remote_amos_page_pa
);
if
(
!
XPC_SUPPORTS_RP_STAMP
(
remote_rp_version
))
{
DBUG_ON
(
!
XPC_SUPPORTS_DISENGAGE_REQUEST
(
remote_vars
->
version
));
xpc_activate_partition
(
part
);
/*
* Other side rebooted and previous XPC did support the
* disengage request, but the new one doesn't.
*/
xpc_clear_partition_engaged
(
1UL
<<
partid
);
xpc_clear_partition_disengage_request
(
1UL
<<
partid
);
}
else
if
(
part
->
remote_amos_page_pa
!=
remote_vars
->
amos_page_pa
||
!
XPC_HB_ALLOWED
(
sn_partition_id
,
remote_vars
))
{
xpc_update_partition_info
(
part
,
remote_rp_version
,
&
remote_rp_stamp
,
remote_rp_pa
,
remote_vars_pa
,
remote_vars
);
reactivate
=
1
;
}
else
{
DBUG_ON
(
!
XPC_SUPPORTS_DISENGAGE_REQUEST
(
remote_vars
->
version
));
stamp_diff
=
xpc_compare_stamps
(
&
part
->
remote_rp_stamp
,
&
remote_rp_stamp
);
if
(
stamp_diff
!=
0
)
{
DBUG_ON
(
stamp_diff
>=
0
);
/*
* Other side rebooted and the previous XPC did support
* the disengage request, as does the new one.
*/
DBUG_ON
(
xpc_partition_engaged
(
1UL
<<
partid
));
DBUG_ON
(
xpc_partition_disengage_requested
(
1UL
<<
partid
));
xpc_update_partition_info
(
part
,
remote_rp_version
,
&
remote_rp_stamp
,
remote_rp_pa
,
remote_vars_pa
,
remote_vars
);
reactivate
=
1
;
}
}
if
(
!
xpc_partition_disengaged
(
part
))
{
/* still waiting on other side to disengage from us */
return
;
}
if
(
reactivate
)
{
part
->
reactivate_nasid
=
nasid
;
XPC_DEACTIVATE_PARTITION
(
part
,
xpcReactivating
);
}
else
if
(
XPC_SUPPORTS_DISENGAGE_REQUEST
(
part
->
remote_vars_version
)
&&
xpc_partition_disengage_requested
(
1UL
<<
partid
))
{
XPC_DEACTIVATE_PARTITION
(
part
,
xpcOtherGoingDown
);
}
}
...
...
@@ -643,14 +802,17 @@ xpc_identify_act_IRQ_sender(void)
u64
nasid
;
/* remote nasid */
int
n_IRQs_detected
=
0
;
AMO_t
*
act_amos
;
struct
xpc_rsvd_page
*
rp
=
(
struct
xpc_rsvd_page
*
)
xpc_rsvd_page
;
act_amos
=
xpc_vars
->
a
ct_amos
;
act_amos
=
xpc_vars
->
a
mos_page
+
XPC_ACTIVATE_IRQ_AMOS
;
/* scan through act AMO variable looking for non-zero entries */
for
(
word
=
0
;
word
<
XP_NASID_MASK_WORDS
;
word
++
)
{
for
(
word
=
0
;
word
<
xp_nasid_mask_words
;
word
++
)
{
if
(
xpc_exiting
)
{
break
;
}
nasid_mask
=
xpc_IPI_receive
(
&
act_amos
[
word
]);
if
(
nasid_mask
==
0
)
{
...
...
@@ -668,7 +830,7 @@ xpc_identify_act_IRQ_sender(void)
* remote nasid in our reserved pages machine mask.
* This is used in the event of module reload.
*/
rp
->
mach_nasids
[
word
]
|=
nasid_mask
;
xpc_
mach_nasids
[
word
]
|=
nasid_mask
;
/* locate the nasid(s) which sent interrupts */
...
...
@@ -687,6 +849,55 @@ xpc_identify_act_IRQ_sender(void)
}
/*
* See if the other side has responded to a partition disengage request
* from us.
*/
int
xpc_partition_disengaged
(
struct
xpc_partition
*
part
)
{
partid_t
partid
=
XPC_PARTID
(
part
);
int
disengaged
;
disengaged
=
(
xpc_partition_engaged
(
1UL
<<
partid
)
==
0
);
if
(
part
->
disengage_request_timeout
)
{
if
(
!
disengaged
)
{
if
(
jiffies
<
part
->
disengage_request_timeout
)
{
/* timelimit hasn't been reached yet */
return
0
;
}
/*
* Other side hasn't responded to our disengage
* request in a timely fashion, so assume it's dead.
*/
xpc_clear_partition_engaged
(
1UL
<<
partid
);
disengaged
=
1
;
}
part
->
disengage_request_timeout
=
0
;
/* cancel the timer function, provided it's not us */
if
(
!
in_interrupt
())
{
del_singleshot_timer_sync
(
&
part
->
disengage_request_timer
);
}
DBUG_ON
(
part
->
act_state
!=
XPC_P_DEACTIVATING
&&
part
->
act_state
!=
XPC_P_INACTIVE
);
if
(
part
->
act_state
!=
XPC_P_INACTIVE
)
{
xpc_wakeup_channel_mgr
(
part
);
}
if
(
XPC_SUPPORTS_DISENGAGE_REQUEST
(
part
->
remote_vars_version
))
{
xpc_cancel_partition_disengage_request
(
part
);
}
}
return
disengaged
;
}
/*
* Mark specified partition as active.
*/
...
...
@@ -721,7 +932,6 @@ xpc_deactivate_partition(const int line, struct xpc_partition *part,
enum
xpc_retval
reason
)
{
unsigned
long
irq_flags
;
partid_t
partid
=
XPC_PARTID
(
part
);
spin_lock_irqsave
(
&
part
->
act_lock
,
irq_flags
);
...
...
@@ -749,17 +959,27 @@ xpc_deactivate_partition(const int line, struct xpc_partition *part,
spin_unlock_irqrestore
(
&
part
->
act_lock
,
irq_flags
);
XPC_DISALLOW_HB
(
partid
,
xpc_vars
);
if
(
XPC_SUPPORTS_DISENGAGE_REQUEST
(
part
->
remote_vars_version
))
{
xpc_request_partition_disengage
(
part
);
xpc_IPI_send_disengage
(
part
);
dev_dbg
(
xpc_part
,
"bringing partition %d down, reason = %d
\n
"
,
partid
,
reason
);
/* set a timelimit on the disengage request */
part
->
disengage_request_timeout
=
jiffies
+
(
xpc_disengage_request_timelimit
*
HZ
);
part
->
disengage_request_timer
.
expires
=
part
->
disengage_request_timeout
;
add_timer
(
&
part
->
disengage_request_timer
);
}
dev_dbg
(
xpc_part
,
"bringing partition %d down, reason = %d
\n
"
,
XPC_PARTID
(
part
),
reason
);
xpc_partition_down
(
part
,
reason
);
xpc_partition_
going_
down
(
part
,
reason
);
}
/*
* Mark specified partition as active.
* Mark specified partition as
in
active.
*/
void
xpc_mark_partition_inactive
(
struct
xpc_partition
*
part
)
...
...
@@ -792,9 +1012,10 @@ xpc_discovery(void)
void
*
remote_rp_base
;
struct
xpc_rsvd_page
*
remote_rp
;
struct
xpc_vars
*
remote_vars
;
u64
remote_r
svd_page
_pa
;
u64
remote_r
p
_pa
;
u64
remote_vars_pa
;
int
region
;
int
region_size
;
int
max_regions
;
int
nasid
;
struct
xpc_rsvd_page
*
rp
;
...
...
@@ -804,7 +1025,8 @@ xpc_discovery(void)
enum
xpc_retval
ret
;
remote_rp
=
xpc_kmalloc_cacheline_aligned
(
XPC_RSVD_PAGE_ALIGNED_SIZE
,
remote_rp
=
xpc_kmalloc_cacheline_aligned
(
XPC_RP_HEADER_SIZE
+
xp_nasid_mask_bytes
,
GFP_KERNEL
,
&
remote_rp_base
);
if
(
remote_rp
==
NULL
)
{
return
;
...
...
@@ -812,13 +1034,13 @@ xpc_discovery(void)
remote_vars
=
(
struct
xpc_vars
*
)
remote_rp
;
discovered_nasids
=
kmalloc
(
sizeof
(
u64
)
*
XP_NASID_MASK_WORDS
,
discovered_nasids
=
kmalloc
(
sizeof
(
u64
)
*
xp_nasid_mask_words
,
GFP_KERNEL
);
if
(
discovered_nasids
==
NULL
)
{
kfree
(
remote_rp_base
);
return
;
}
memset
(
discovered_nasids
,
0
,
sizeof
(
u64
)
*
XP_NASID_MASK_WORDS
);
memset
(
discovered_nasids
,
0
,
sizeof
(
u64
)
*
xp_nasid_mask_words
);
rp
=
(
struct
xpc_rsvd_page
*
)
xpc_rsvd_page
;
...
...
@@ -827,11 +1049,19 @@ xpc_discovery(void)
* nodes that can comprise an access protection grouping. The access
* protection is in regards to memory, IOI and IPI.
*/
//>>> move the next two #defines into either include/asm-ia64/sn/arch.h or
//>>> include/asm-ia64/sn/addrs.h
#define SH1_MAX_REGIONS 64
#define SH2_MAX_REGIONS 256
max_regions
=
is_shub2
()
?
SH2_MAX_REGIONS
:
SH1_MAX_REGIONS
;
max_regions
=
64
;
region_size
=
sn_region_size
;
switch
(
region_size
)
{
case
128
:
max_regions
*=
2
;
case
64
:
max_regions
*=
2
;
case
32
:
max_regions
*=
2
;
region_size
=
16
;
DBUG_ON
(
!
is_shub2
());
}
for
(
region
=
0
;
region
<
max_regions
;
region
++
)
{
...
...
@@ -841,8 +1071,8 @@ xpc_discovery(void)
dev_dbg
(
xpc_part
,
"searching region %d
\n
"
,
region
);
for
(
nasid
=
(
region
*
sn_
region_size
*
2
);
nasid
<
((
region
+
1
)
*
sn_
region_size
*
2
);
for
(
nasid
=
(
region
*
region_size
*
2
);
nasid
<
((
region
+
1
)
*
region_size
*
2
);
nasid
+=
2
)
{
if
((
volatile
int
)
xpc_exiting
)
{
...
...
@@ -852,14 +1082,14 @@ xpc_discovery(void)
dev_dbg
(
xpc_part
,
"checking nasid %d
\n
"
,
nasid
);
if
(
XPC_NASID_IN_ARRAY
(
nasid
,
rp
->
part_nasids
))
{
if
(
XPC_NASID_IN_ARRAY
(
nasid
,
xpc_
part_nasids
))
{
dev_dbg
(
xpc_part
,
"PROM indicates Nasid %d is "
"part of the local partition; skipping "
"region
\n
"
,
nasid
);
break
;
}
if
(
!
(
XPC_NASID_IN_ARRAY
(
nasid
,
rp
->
mach_nasids
)))
{
if
(
!
(
XPC_NASID_IN_ARRAY
(
nasid
,
xpc_
mach_nasids
)))
{
dev_dbg
(
xpc_part
,
"PROM indicates Nasid %d was "
"not on Numa-Link network at reset
\n
"
,
nasid
);
...
...
@@ -877,7 +1107,7 @@ xpc_discovery(void)
/* pull over the reserved page structure */
ret
=
xpc_get_remote_rp
(
nasid
,
discovered_nasids
,
remote_rp
,
&
remote_r
svd_page
_pa
);
remote_rp
,
&
remote_r
p
_pa
);
if
(
ret
!=
xpcSuccess
)
{
dev_dbg
(
xpc_part
,
"unable to get reserved page "
"from nasid %d, reason=%d
\n
"
,
nasid
,
...
...
@@ -948,6 +1178,13 @@ xpc_discovery(void)
remote_vars
->
act_nasid
,
remote_vars
->
act_phys_cpuid
);
if
(
XPC_SUPPORTS_DISENGAGE_REQUEST
(
remote_vars
->
version
))
{
part
->
remote_amos_page_pa
=
remote_vars
->
amos_page_pa
;
xpc_mark_partition_disengaged
(
part
);
xpc_cancel_partition_disengage_request
(
part
);
}
xpc_IPI_send_activate
(
remote_vars
);
}
}
...
...
@@ -974,12 +1211,12 @@ xpc_initiate_partid_to_nasids(partid_t partid, void *nasid_mask)
return
xpcPartitionDown
;
}
part_nasid_pa
=
part
->
remote_rp_pa
+
(
u64
)
&
((
struct
xpc_rsvd_page
*
)
0
)
->
part_nasids
;
memset
(
nasid_mask
,
0
,
XP_NASID_MASK_BYTES
);
part_nasid_pa
=
(
u64
)
XPC_RP_PART_NASIDS
(
part
->
remote_rp_pa
);
bte_res
=
xp_bte_copy
(
part_nasid_pa
,
ia64_tpa
((
u64
)
nasid_mask
),
L1_CACHE_ALIGN
(
XP_NASID_MASK_BYTES
),
(
BTE_NOTIFY
|
BTE_WACQUIRE
),
NULL
);
xp_nasid_mask_bytes
,
(
BTE_NOTIFY
|
BTE_WACQUIRE
),
NULL
);
return
xpc_map_bte_errors
(
bte_res
);
}
...
...
include/asm-ia64/sn/xp.h
View file @
fac84ef2
...
...
@@ -217,7 +217,17 @@ enum xpc_retval {
xpcInvalidPartid
,
/* 42: invalid partition ID */
xpcLocalPartid
,
/* 43: local partition ID */
xpcUnknownReason
/* 44: unknown reason -- must be last in list */
xpcOtherGoingDown
,
/* 44: other side going down, reason unknown */
xpcSystemGoingDown
,
/* 45: system is going down, reason unknown */
xpcSystemHalt
,
/* 46: system is being halted */
xpcSystemReboot
,
/* 47: system is being rebooted */
xpcSystemPoweroff
,
/* 48: system is being powered off */
xpcDisconnecting
,
/* 49: channel disconnecting (closing) */
xpcOpenCloseError
,
/* 50: channel open/close protocol error */
xpcUnknownReason
/* 51: unknown reason -- must be last in list */
};
...
...
@@ -342,7 +352,7 @@ typedef void (*xpc_notify_func)(enum xpc_retval reason, partid_t partid,
*
* The 'func' field points to the function to call when aynchronous
* notification is required for such events as: a connection established/lost,
* or an incom
m
ing message received, or an error condition encountered. A
* or an incoming message received, or an error condition encountered. A
* non-NULL 'func' field indicates that there is an active registration for
* the channel.
*/
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment