sgi-xp: setup the activate GRU message queue

Setup the activate GRU message queue that is used for partition activation
and channel connection on UV systems.

Signed-off-by: Dean Nelson <dcn@sgi.com>
Cc: Jack Steiner <steiner@sgi.com>
Cc: "Luck, Tony" <tony.luck@intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Dean Nelson
2008-07-29 22:34:18 -07:00
committed by Linus Torvalds
parent 83469b5525
commit 5b8669dfd1
8 changed files with 1332 additions and 394 deletions

View File

@@ -141,8 +141,9 @@ static struct ctl_table_header *xpc_sysctl;
/* non-zero if any remote partition disengage was timed out */
int xpc_disengage_timedout;
/* #of activate IRQs received */
atomic_t xpc_activate_IRQ_rcvd = ATOMIC_INIT(0);
/* #of activate IRQs received and not yet processed */
int xpc_activate_IRQ_rcvd;
DEFINE_SPINLOCK(xpc_activate_IRQ_rcvd_lock);
/* IRQ handler notifies this wait queue on receipt of an IRQ */
DECLARE_WAIT_QUEUE_HEAD(xpc_activate_IRQ_wq);
@@ -169,10 +170,11 @@ static struct notifier_block xpc_die_notifier = {
.notifier_call = xpc_system_die,
};
int (*xpc_setup_partitions_sn) (void);
enum xp_retval (*xpc_get_partition_rsvd_page_pa) (void *buf, u64 *cookie,
unsigned long *rp_pa,
size_t *len);
enum xp_retval (*xpc_rsvd_page_init) (struct xpc_rsvd_page *rp);
int (*xpc_setup_rsvd_page_sn) (struct xpc_rsvd_page *rp);
void (*xpc_heartbeat_init) (void);
void (*xpc_heartbeat_exit) (void);
void (*xpc_increment_heartbeat) (void);
@@ -183,8 +185,8 @@ enum xp_retval (*xpc_get_remote_heartbeat) (struct xpc_partition *part);
enum xp_retval (*xpc_make_first_contact) (struct xpc_partition *part);
void (*xpc_notify_senders_of_disconnect) (struct xpc_channel *ch);
u64 (*xpc_get_chctl_all_flags) (struct xpc_partition *part);
enum xp_retval (*xpc_allocate_msgqueues) (struct xpc_channel *ch);
void (*xpc_free_msgqueues) (struct xpc_channel *ch);
enum xp_retval (*xpc_setup_msg_structures) (struct xpc_channel *ch);
void (*xpc_teardown_msg_structures) (struct xpc_channel *ch);
void (*xpc_process_msg_chctl_flags) (struct xpc_partition *part, int ch_number);
int (*xpc_n_of_deliverable_msgs) (struct xpc_channel *ch);
struct xpc_msg *(*xpc_get_deliverable_msg) (struct xpc_channel *ch);
@@ -196,9 +198,9 @@ void (*xpc_request_partition_reactivation) (struct xpc_partition *part);
void (*xpc_request_partition_deactivation) (struct xpc_partition *part);
void (*xpc_cancel_partition_deactivation_request) (struct xpc_partition *part);
void (*xpc_process_activate_IRQ_rcvd) (int n_IRQs_expected);
enum xp_retval (*xpc_setup_infrastructure) (struct xpc_partition *part);
void (*xpc_teardown_infrastructure) (struct xpc_partition *part);
void (*xpc_process_activate_IRQ_rcvd) (void);
enum xp_retval (*xpc_setup_ch_structures_sn) (struct xpc_partition *part);
void (*xpc_teardown_ch_structures_sn) (struct xpc_partition *part);
void (*xpc_indicate_partition_engaged) (struct xpc_partition *part);
int (*xpc_partition_engaged) (short partid);
@@ -215,6 +217,9 @@ void (*xpc_send_chctl_openrequest) (struct xpc_channel *ch,
void (*xpc_send_chctl_openreply) (struct xpc_channel *ch,
unsigned long *irq_flags);
void (*xpc_save_remote_msgqueue_pa) (struct xpc_channel *ch,
unsigned long msgqueue_pa);
enum xp_retval (*xpc_send_msg) (struct xpc_channel *ch, u32 flags,
void *payload, u16 payload_size, u8 notify_type,
xpc_notify_func func, void *key);
@@ -308,8 +313,6 @@ xpc_check_remote_hb(void)
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() */
@@ -325,43 +328,37 @@ xpc_hb_checker(void *ignore)
dev_dbg(xpc_part, "woke up with %d ticks rem; %d IRQs have "
"been received\n",
(int)(xpc_hb_check_timeout - jiffies),
atomic_read(&xpc_activate_IRQ_rcvd) - last_IRQ_count);
xpc_activate_IRQ_rcvd);
/* checking of remote heartbeats is skewed by IRQ handling */
if (time_is_before_eq_jiffies(xpc_hb_check_timeout)) {
xpc_hb_check_timeout = jiffies +
(xpc_hb_check_interval * HZ);
dev_dbg(xpc_part, "checking remote heartbeats\n");
xpc_check_remote_hb();
/*
* We need to periodically recheck to ensure no
* IRQ/amo pairs have been missed. That check
* must always reset xpc_hb_check_timeout.
* On sn2 we need to periodically recheck to ensure no
* IRQ/amo pairs have been missed.
*/
force_IRQ = 1;
if (is_shub())
force_IRQ = 1;
}
/* check for outstanding IRQs */
new_IRQ_count = atomic_read(&xpc_activate_IRQ_rcvd);
if (last_IRQ_count < new_IRQ_count || force_IRQ != 0) {
if (xpc_activate_IRQ_rcvd > 0 || force_IRQ != 0) {
force_IRQ = 0;
dev_dbg(xpc_part, "found an IRQ to process; will be "
"resetting xpc_hb_check_timeout\n");
xpc_process_activate_IRQ_rcvd(new_IRQ_count -
last_IRQ_count);
last_IRQ_count = new_IRQ_count;
xpc_hb_check_timeout = jiffies +
(xpc_hb_check_interval * HZ);
dev_dbg(xpc_part, "processing activate IRQs "
"received\n");
xpc_process_activate_IRQ_rcvd();
}
/* wait for IRQ or timeout */
(void)wait_event_interruptible(xpc_activate_IRQ_wq,
(last_IRQ_count < atomic_read(
&xpc_activate_IRQ_rcvd)
|| time_is_before_eq_jiffies(
(time_is_before_eq_jiffies(
xpc_hb_check_timeout) ||
xpc_activate_IRQ_rcvd > 0 ||
xpc_exiting));
}
@@ -436,6 +433,153 @@ xpc_channel_mgr(struct xpc_partition *part)
}
}
/*
* Guarantee that the kzalloc'd memory is cacheline aligned.
*/
void *
xpc_kzalloc_cacheline_aligned(size_t size, gfp_t flags, void **base)
{
/* see if kzalloc will give us cachline aligned memory by default */
*base = kzalloc(size, flags);
if (*base == NULL)
return NULL;
if ((u64)*base == L1_CACHE_ALIGN((u64)*base))
return *base;
kfree(*base);
/* nope, we'll have to do it ourselves */
*base = kzalloc(size + L1_CACHE_BYTES, flags);
if (*base == NULL)
return NULL;
return (void *)L1_CACHE_ALIGN((u64)*base);
}
/*
* Setup the channel structures necessary to support XPartition Communication
* between the specified remote partition and the local one.
*/
static enum xp_retval
xpc_setup_ch_structures(struct xpc_partition *part)
{
enum xp_retval ret;
int ch_number;
struct xpc_channel *ch;
short partid = XPC_PARTID(part);
/*
* Allocate all of the channel structures as a contiguous chunk of
* memory.
*/
DBUG_ON(part->channels != NULL);
part->channels = kzalloc(sizeof(struct xpc_channel) * XPC_MAX_NCHANNELS,
GFP_KERNEL);
if (part->channels == NULL) {
dev_err(xpc_chan, "can't get memory for channels\n");
return xpNoMemory;
}
/* allocate the remote open and close args */
part->remote_openclose_args =
xpc_kzalloc_cacheline_aligned(XPC_OPENCLOSE_ARGS_SIZE,
GFP_KERNEL, &part->
remote_openclose_args_base);
if (part->remote_openclose_args == NULL) {
dev_err(xpc_chan, "can't get memory for remote connect args\n");
ret = xpNoMemory;
goto out_1;
}
part->chctl.all_flags = 0;
spin_lock_init(&part->chctl_lock);
atomic_set(&part->channel_mgr_requests, 1);
init_waitqueue_head(&part->channel_mgr_wq);
part->nchannels = XPC_MAX_NCHANNELS;
atomic_set(&part->nchannels_active, 0);
atomic_set(&part->nchannels_engaged, 0);
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;
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);
init_completion(&ch->wdisconnect_wait);
atomic_set(&ch->n_on_msg_allocate_wq, 0);
init_waitqueue_head(&ch->msg_allocate_wq);
init_waitqueue_head(&ch->idle_wq);
}
ret = xpc_setup_ch_structures_sn(part);
if (ret != xpSuccess)
goto out_2;
/*
* With the setting of the partition setup_state to XPC_P_SS_SETUP,
* we're declaring that this partition is ready to go.
*/
part->setup_state = XPC_P_SS_SETUP;
return xpSuccess;
/* setup of ch structures failed */
out_2:
kfree(part->remote_openclose_args_base);
part->remote_openclose_args = NULL;
out_1:
kfree(part->channels);
part->channels = NULL;
return ret;
}
/*
* Teardown the channel structures necessary to support XPartition Communication
* between the specified remote partition and the local one.
*/
static void
xpc_teardown_ch_structures(struct xpc_partition *part)
{
DBUG_ON(atomic_read(&part->nchannels_engaged) != 0);
DBUG_ON(atomic_read(&part->nchannels_active) != 0);
/*
* Make this partition inaccessible to local processes by marking it
* as no longer setup. Then wait before proceeding with the teardown
* until all existing references cease.
*/
DBUG_ON(part->setup_state != XPC_P_SS_SETUP);
part->setup_state = XPC_P_SS_WTEARDOWN;
wait_event(part->teardown_wq, (atomic_read(&part->references) == 0));
/* now we can begin tearing down the infrastructure */
xpc_teardown_ch_structures_sn(part);
kfree(part->remote_openclose_args_base);
part->remote_openclose_args = NULL;
kfree(part->channels);
part->channels = NULL;
part->setup_state = XPC_P_SS_TORNDOWN;
}
/*
* 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
@@ -476,7 +620,7 @@ xpc_activating(void *__partid)
xpc_allow_hb(partid);
if (xpc_setup_infrastructure(part) == xpSuccess) {
if (xpc_setup_ch_structures(part) == xpSuccess) {
(void)xpc_part_ref(part); /* this will always succeed */
if (xpc_make_first_contact(part) == xpSuccess) {
@@ -486,7 +630,7 @@ xpc_activating(void *__partid)
}
xpc_part_deref(part);
xpc_teardown_infrastructure(part);
xpc_teardown_ch_structures(part);
}
xpc_disallow_hb(partid);
@@ -806,6 +950,56 @@ xpc_disconnect_wait(int ch_number)
}
}
static int
xpc_setup_partitions(void)
{
short partid;
struct xpc_partition *part;
xpc_partitions = kzalloc(sizeof(struct xpc_partition) *
xp_max_npartitions, GFP_KERNEL);
if (xpc_partitions == NULL) {
dev_err(xpc_part, "can't get memory for partition structure\n");
return -ENOMEM;
}
/*
* 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 = 0; partid < xp_max_npartitions; partid++) {
part = &xpc_partitions[partid];
DBUG_ON((u64)part != L1_CACHE_ALIGN((u64)part));
part->activate_IRQ_rcvd = 0;
spin_lock_init(&part->act_lock);
part->act_state = XPC_P_AS_INACTIVE;
XPC_SET_REASON(part, 0, 0);
init_timer(&part->disengage_timer);
part->disengage_timer.function =
xpc_timeout_partition_disengage;
part->disengage_timer.data = (unsigned long)part;
part->setup_state = XPC_P_SS_UNSET;
init_waitqueue_head(&part->teardown_wq);
atomic_set(&part->references, 0);
}
return xpc_setup_partitions_sn();
}
static void
xpc_teardown_partitions(void)
{
kfree(xpc_partitions);
}
static void
xpc_do_exit(enum xp_retval reason)
{
@@ -892,8 +1086,7 @@ xpc_do_exit(enum xp_retval reason)
DBUG_ON(xpc_any_partition_engaged());
DBUG_ON(xpc_any_hbs_allowed() != 0);
/* a zero timestamp indicates our rsvd page is not initialized */
xpc_rsvd_page->ts_jiffies = 0;
xpc_teardown_rsvd_page();
if (reason == xpUnloading) {
(void)unregister_die_notifier(&xpc_die_notifier);
@@ -906,7 +1099,7 @@ xpc_do_exit(enum xp_retval reason)
if (xpc_sysctl)
unregister_sysctl_table(xpc_sysctl);
kfree(xpc_partitions);
xpc_teardown_partitions();
if (is_shub())
xpc_exit_sn2();
@@ -1062,8 +1255,6 @@ int __init
xpc_init(void)
{
int ret;
short partid;
struct xpc_partition *part;
struct task_struct *kthread;
snprintf(xpc_part->bus_id, BUS_ID_SIZE, "part");
@@ -1076,56 +1267,29 @@ xpc_init(void)
* further to only support exactly 64 partitions on this
* architecture, no less.
*/
if (xp_max_npartitions != 64)
return -EINVAL;
ret = xpc_init_sn2();
if (ret != 0)
return ret;
if (xp_max_npartitions != 64) {
dev_err(xpc_part, "max #of partitions not set to 64\n");
ret = -EINVAL;
} else {
ret = xpc_init_sn2();
}
} else if (is_uv()) {
xpc_init_uv();
ret = xpc_init_uv();
} else {
return -ENODEV;
ret = -ENODEV;
}
xpc_partitions = kzalloc(sizeof(struct xpc_partition) *
xp_max_npartitions, GFP_KERNEL);
if (xpc_partitions == NULL) {
if (ret != 0)
return ret;
ret = xpc_setup_partitions();
if (ret != 0) {
dev_err(xpc_part, "can't get memory for partition structure\n");
ret = -ENOMEM;
goto out_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 = 0; partid < xp_max_npartitions; partid++) {
part = &xpc_partitions[partid];
DBUG_ON((u64)part != L1_CACHE_ALIGN((u64)part));
part->activate_IRQ_rcvd = 0;
spin_lock_init(&part->act_lock);
part->act_state = XPC_P_AS_INACTIVE;
XPC_SET_REASON(part, 0, 0);
init_timer(&part->disengage_timer);
part->disengage_timer.function =
xpc_timeout_partition_disengage;
part->disengage_timer.data = (unsigned long)part;
part->setup_state = XPC_P_SS_UNSET;
init_waitqueue_head(&part->teardown_wq);
atomic_set(&part->references, 0);
}
xpc_sysctl = register_sysctl_table(xpc_sys_dir);
/*
@@ -1133,10 +1297,9 @@ xpc_init(void)
* other partitions to discover we are alive and establish initial
* communications.
*/
xpc_rsvd_page = xpc_setup_rsvd_page();
if (xpc_rsvd_page == NULL) {
ret = xpc_setup_rsvd_page();
if (ret != 0) {
dev_err(xpc_part, "can't setup our reserved page\n");
ret = -EBUSY;
goto out_2;
}
@@ -1187,15 +1350,15 @@ xpc_init(void)
/* initialization was not successful */
out_3:
/* a zero timestamp indicates our rsvd page is not initialized */
xpc_rsvd_page->ts_jiffies = 0;
xpc_teardown_rsvd_page();
(void)unregister_die_notifier(&xpc_die_notifier);
(void)unregister_reboot_notifier(&xpc_reboot_notifier);
out_2:
if (xpc_sysctl)
unregister_sysctl_table(xpc_sysctl);
kfree(xpc_partitions);
xpc_teardown_partitions();
out_1:
if (is_shub())
xpc_exit_sn2();