#include <sys/types.h>
#include <sys/param.h>
#include <sys/t_lock.h>
#include <sys/thread.h>
#include <sys/cpuvar.h>
#include <sys/x_call.h>
#include <sys/xc_levels.h>
#include <sys/cpu.h>
#include <sys/psw.h>
#include <sys/sunddi.h>
#include <sys/debug.h>
#include <sys/systm.h>
#include <sys/archsystm.h>
#include <sys/machsystm.h>
#include <sys/mutex_impl.h>
#include <sys/stack.h>
#include <sys/promif.h>
#include <sys/x86_archext.h>
uint_t xc_collect_enable = 0;
uint64_t xc_total_cnt = 0;
uint64_t xc_multi_cnt = 0;
static volatile cpuset_t xc_priority_set_store;
static volatile ulong_t *xc_priority_set = CPUSET2BV(xc_priority_set_store);
static xc_data_t xc_priority_data;
static void
xc_decrement(struct machcpu *mcpu)
{
atomic_dec_32(&mcpu->xc_work_cnt);
}
static int
xc_increment(struct machcpu *mcpu)
{
int old;
do {
old = mcpu->xc_work_cnt;
} while (atomic_cas_32(&mcpu->xc_work_cnt, old, old + 1) != old);
return (old);
}
static void
xc_insert(void *queue, xc_msg_t *msg)
{
xc_msg_t *old_head;
ASSERT(msg->xc_command != XC_MSG_FREE ||
cpu[msg->xc_master] == NULL ||
queue == &cpu[msg->xc_master]->cpu_m.xc_free);
do {
old_head = (xc_msg_t *)*(volatile xc_msg_t **)queue;
msg->xc_next = old_head;
} while (atomic_cas_ptr(queue, old_head, msg) != old_head);
}
static xc_msg_t *
xc_extract(xc_msg_t **queue)
{
xc_msg_t *old_head;
do {
old_head = (xc_msg_t *)*(volatile xc_msg_t **)queue;
if (old_head == NULL)
return (old_head);
} while (atomic_cas_ptr(queue, old_head, old_head->xc_next) !=
old_head);
old_head->xc_next = NULL;
return (old_head);
}
static xc_msg_t *
xc_get(void)
{
struct machcpu *mcpup = &CPU->cpu_m;
xc_msg_t *msg = xc_extract(&mcpup->xc_msgbox);
mcpup->xc_curmsg = msg;
return (msg);
}
static uint_t xc_initialized = 0;
void
xc_init_cpu(struct cpu *cpup)
{
xc_msg_t *msg;
int c;
for (c = 0; c < max_ncpus; ++c) {
if (plat_dr_support_cpu()) {
msg = kmem_zalloc(sizeof (*msg), KM_SLEEP);
msg->xc_command = XC_MSG_FREE;
msg->xc_master = cpup->cpu_id;
xc_insert(&cpup->cpu_m.xc_free, msg);
} else if (cpu[c] != NULL && cpu[c] != cpup) {
msg = kmem_zalloc(sizeof (*msg), KM_SLEEP);
msg->xc_command = XC_MSG_FREE;
msg->xc_master = c;
xc_insert(&cpu[c]->cpu_m.xc_free, msg);
msg = kmem_zalloc(sizeof (*msg), KM_SLEEP);
msg->xc_command = XC_MSG_FREE;
msg->xc_master = cpup->cpu_id;
xc_insert(&cpup->cpu_m.xc_free, msg);
}
}
if (!plat_dr_support_cpu()) {
msg = kmem_zalloc(sizeof (*msg), KM_SLEEP);
msg->xc_command = XC_MSG_FREE;
msg->xc_master = cpup->cpu_id;
xc_insert(&cpup->cpu_m.xc_free, msg);
}
if (!xc_initialized)
xc_initialized = 1;
}
void
xc_fini_cpu(struct cpu *cpup)
{
xc_msg_t *msg;
ASSERT((cpup->cpu_flags & CPU_READY) == 0);
ASSERT(cpup->cpu_m.xc_msgbox == NULL);
ASSERT(cpup->cpu_m.xc_work_cnt == 0);
while ((msg = xc_extract(&cpup->cpu_m.xc_free)) != NULL) {
kmem_free(msg, sizeof (*msg));
}
}
#define XC_FLUSH_MAX_WAITS 1000
int
xc_flush_cpu(struct cpu *cpup)
{
int i;
ASSERT((cpup->cpu_flags & CPU_READY) == 0);
pause_cpus(cpup, NULL);
start_cpus();
for (i = 0; i < XC_FLUSH_MAX_WAITS; i++) {
if (cpup->cpu_m.xc_work_cnt == 0) {
break;
}
DELAY(1);
}
for (; i < XC_FLUSH_MAX_WAITS; i++) {
if (!BT_TEST(xc_priority_set, cpup->cpu_id)) {
break;
}
DELAY(1);
}
return (i >= XC_FLUSH_MAX_WAITS ? ETIME : 0);
}
uint_t
xc_serv(caddr_t arg1, caddr_t arg2)
{
struct machcpu *mcpup = &(CPU->cpu_m);
xc_msg_t *msg;
xc_data_t *data;
xc_msg_t *xc_waiters = NULL;
uint32_t num_waiting = 0;
xc_func_t func;
xc_arg_t a1;
xc_arg_t a2;
xc_arg_t a3;
uint_t rc = DDI_INTR_UNCLAIMED;
while (mcpup->xc_work_cnt != 0) {
rc = DDI_INTR_CLAIMED;
for (msg = NULL; msg == NULL; msg = xc_get()) {
if (BT_TEST(xc_priority_set, CPU->cpu_id)) {
func = xc_priority_data.xc_func;
a1 = xc_priority_data.xc_a1;
a2 = xc_priority_data.xc_a2;
a3 = xc_priority_data.xc_a3;
BT_ATOMIC_CLEAR(xc_priority_set, CPU->cpu_id);
xc_decrement(mcpup);
func(a1, a2, a3);
if (mcpup->xc_work_cnt == 0)
return (rc);
}
SMT_PAUSE();
}
switch (msg->xc_command) {
case XC_MSG_ASYNC:
data = &cpu[msg->xc_master]->cpu_m.xc_data;
func = data->xc_func;
a1 = data->xc_a1;
a2 = data->xc_a2;
a3 = data->xc_a3;
msg->xc_command = XC_MSG_DONE;
xc_insert(&cpu[msg->xc_master]->cpu_m.xc_msgbox, msg);
if (func != NULL)
(void) (*func)(a1, a2, a3);
xc_decrement(mcpup);
break;
case XC_MSG_SYNC:
data = &cpu[msg->xc_master]->cpu_m.xc_data;
if (data->xc_func != NULL)
(void) (*data->xc_func)(data->xc_a1,
data->xc_a2, data->xc_a3);
msg->xc_command = XC_MSG_WAITING;
xc_insert(&cpu[msg->xc_master]->cpu_m.xc_msgbox, msg);
break;
case XC_MSG_WAITING:
xc_insert(&xc_waiters, msg);
if (++num_waiting < mcpup->xc_wait_cnt)
break;
while ((msg = xc_extract(&xc_waiters)) != NULL) {
msg->xc_command = XC_MSG_RELEASED;
xc_insert(&cpu[msg->xc_slave]->cpu_m.xc_msgbox,
msg);
--num_waiting;
}
if (num_waiting != 0)
panic("wrong number waiting");
mcpup->xc_wait_cnt = 0;
break;
case XC_MSG_CALL:
data = &cpu[msg->xc_master]->cpu_m.xc_data;
if (data->xc_func != NULL)
(void) (*data->xc_func)(data->xc_a1,
data->xc_a2, data->xc_a3);
case XC_MSG_RELEASED:
msg->xc_command = XC_MSG_DONE;
xc_insert(&cpu[msg->xc_master]->cpu_m.xc_msgbox, msg);
xc_decrement(mcpup);
break;
case XC_MSG_DONE:
msg->xc_command = XC_MSG_FREE;
xc_insert(&mcpup->xc_free, msg);
xc_decrement(mcpup);
break;
case XC_MSG_FREE:
panic("free message 0x%p in msgbox", (void *)msg);
break;
default:
panic("bad message 0x%p in msgbox", (void *)msg);
break;
}
CPU->cpu_m.xc_curmsg = NULL;
}
return (rc);
}
static void
xc_common(
xc_func_t func,
xc_arg_t arg1,
xc_arg_t arg2,
xc_arg_t arg3,
ulong_t *set,
uint_t command)
{
int c;
struct cpu *cpup;
xc_msg_t *msg;
xc_data_t *data;
int cnt;
int save_spl;
if (!xc_initialized) {
if (BT_TEST(set, CPU->cpu_id) && (CPU->cpu_flags & CPU_READY) &&
func != NULL)
(void) (*func)(arg1, arg2, arg3);
return;
}
save_spl = splr(ipltospl(XC_HI_PIL));
data = &CPU->cpu_m.xc_data;
data->xc_func = func;
data->xc_a1 = arg1;
data->xc_a2 = arg2;
data->xc_a3 = arg3;
CPU->cpu_m.xc_wait_cnt = 0;
for (c = 0; c < max_ncpus; ++c) {
if (!BT_TEST(set, c))
continue;
cpup = cpu[c];
if (cpup == NULL || !(cpup->cpu_flags & CPU_READY))
continue;
msg = xc_extract(&CPU->cpu_m.xc_free);
if (msg == NULL)
panic("Ran out of free xc_msg_t's");
msg->xc_command = command;
if (msg->xc_master != CPU->cpu_id)
panic("msg %p has wrong xc_master", (void *)msg);
msg->xc_slave = c;
(void) xc_increment(&CPU->cpu_m);
if (command == XC_MSG_SYNC)
++CPU->cpu_m.xc_wait_cnt;
cnt = xc_increment(&cpup->cpu_m);
xc_insert(&cpup->cpu_m.xc_msgbox, msg);
if (cpup != CPU) {
if (cnt == 0) {
CPU_STATS_ADDQ(CPU, sys, xcalls, 1);
send_dirint(c, XC_HI_PIL);
if (xc_collect_enable)
++xc_total_cnt;
} else if (xc_collect_enable) {
++xc_multi_cnt;
}
}
}
(void) xc_serv(NULL, NULL);
splx(save_spl);
}
static void
xc_priority_common(
xc_func_t func,
xc_arg_t arg1,
xc_arg_t arg2,
xc_arg_t arg3,
ulong_t *set)
{
int i;
int c;
struct cpu *cpup;
for (c = 0; c < max_ncpus; ++c) {
cpup = cpu[c];
if (cpup == NULL || !(cpup->cpu_flags & CPU_READY))
continue;
for (i = 0; BT_TEST(xc_priority_set, c) && i < 40000; ++i)
SMT_PAUSE();
if (BT_TEST(xc_priority_set, c)) {
BT_ATOMIC_CLEAR(xc_priority_set, c);
if (cpup->cpu_m.xc_work_cnt > 0)
xc_decrement(&cpup->cpu_m);
}
}
xc_priority_data.xc_func = func;
xc_priority_data.xc_a1 = arg1;
xc_priority_data.xc_a2 = arg2;
xc_priority_data.xc_a3 = arg3;
for (c = 0; c < max_ncpus; ++c) {
if (!BT_TEST(set, c))
continue;
cpup = cpu[c];
if (cpup == NULL || !(cpup->cpu_flags & CPU_READY) ||
cpup == CPU)
continue;
(void) xc_increment(&cpup->cpu_m);
BT_ATOMIC_SET(xc_priority_set, c);
send_dirint(c, XC_HI_PIL);
for (i = 0; i < 10; ++i) {
(void) atomic_cas_ptr(&cpup->cpu_m.xc_msgbox,
cpup->cpu_m.xc_msgbox, cpup->cpu_m.xc_msgbox);
}
}
}
void
xc_priority(
xc_arg_t arg1,
xc_arg_t arg2,
xc_arg_t arg3,
ulong_t *set,
xc_func_t func)
{
extern int IGNORE_KERNEL_PREEMPTION;
int save_spl = splr(ipltospl(XC_HI_PIL));
int save_kernel_preemption = IGNORE_KERNEL_PREEMPTION;
IGNORE_KERNEL_PREEMPTION = 1;
xc_priority_common((xc_func_t)func, arg1, arg2, arg3, set);
IGNORE_KERNEL_PREEMPTION = save_kernel_preemption;
splx(save_spl);
}
void
kdi_xc_others(int this_cpu, void (*func)(void))
{
extern int IGNORE_KERNEL_PREEMPTION;
int save_kernel_preemption;
cpuset_t set;
if (!xc_initialized)
return;
save_kernel_preemption = IGNORE_KERNEL_PREEMPTION;
IGNORE_KERNEL_PREEMPTION = 1;
CPUSET_ALL_BUT(set, this_cpu);
xc_priority_common((xc_func_t)func, 0, 0, 0, CPUSET2BV(set));
IGNORE_KERNEL_PREEMPTION = save_kernel_preemption;
}
void
xc_call_nowait(
xc_arg_t arg1,
xc_arg_t arg2,
xc_arg_t arg3,
ulong_t *set,
xc_func_t func)
{
xc_common(func, arg1, arg2, arg3, set, XC_MSG_ASYNC);
}
void
xc_call(
xc_arg_t arg1,
xc_arg_t arg2,
xc_arg_t arg3,
ulong_t *set,
xc_func_t func)
{
xc_common(func, arg1, arg2, arg3, set, XC_MSG_CALL);
}
void
xc_sync(
xc_arg_t arg1,
xc_arg_t arg2,
xc_arg_t arg3,
ulong_t *set,
xc_func_t func)
{
xc_common(func, arg1, arg2, arg3, set, XC_MSG_SYNC);
}