#include "opt_ddb.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/rtprio.h>
#include <sys/queue.h>
#include <sys/thread2.h>
#include <sys/sysctl.h>
#include <sys/ktr.h>
#include <sys/kthread.h>
#include <machine/cpu.h>
#include <sys/lock.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <vm/vm_kern.h>
#include <vm/vm_object.h>
#include <vm/vm_page.h>
#include <vm/vm_map.h>
#include <vm/vm_pager.h>
#include <vm/vm_extern.h>
#include <vm/vm_zone.h>
#include <machine/stdarg.h>
#include <machine/smp.h>
#include <machine/clock.h>
#include <machine/atomic.h>
struct ipiq_stats {
int64_t ipiq_count;
int64_t ipiq_fifofull;
int64_t ipiq_avoided;
int64_t ipiq_passive;
int64_t ipiq_cscount;
} __cachealign;
static struct ipiq_stats ipiq_stats_percpu[MAXCPU];
#define ipiq_stat(gd) ipiq_stats_percpu[(gd)->gd_cpuid]
static int ipiq_debug;
#ifdef PANIC_DEBUG
static int panic_ipiq_cpu = -1;
static int panic_ipiq_count = 100;
#endif
SYSCTL_INT(_lwkt, OID_AUTO, ipiq_debug, CTLFLAG_RW, &ipiq_debug, 0,
"");
#ifdef PANIC_DEBUG
SYSCTL_INT(_lwkt, OID_AUTO, panic_ipiq_cpu, CTLFLAG_RW, &panic_ipiq_cpu, 0, "");
SYSCTL_INT(_lwkt, OID_AUTO, panic_ipiq_count, CTLFLAG_RW, &panic_ipiq_count, 0, "");
#endif
#define IPIQ_STRING "func=%p arg1=%p arg2=%d scpu=%d dcpu=%d"
#define IPIQ_ARGS void *func, void *arg1, int arg2, int scpu, int dcpu
#if !defined(KTR_IPIQ)
#define KTR_IPIQ KTR_ALL
#endif
KTR_INFO_MASTER(ipiq);
KTR_INFO(KTR_IPIQ, ipiq, send_norm, 0, IPIQ_STRING, IPIQ_ARGS);
KTR_INFO(KTR_IPIQ, ipiq, send_pasv, 1, IPIQ_STRING, IPIQ_ARGS);
KTR_INFO(KTR_IPIQ, ipiq, receive, 4, IPIQ_STRING, IPIQ_ARGS);
KTR_INFO(KTR_IPIQ, ipiq, sync_start, 5, "cpumask=%08lx", unsigned long mask);
KTR_INFO(KTR_IPIQ, ipiq, sync_end, 6, "cpumask=%08lx", unsigned long mask);
KTR_INFO(KTR_IPIQ, ipiq, cpu_send, 7, IPIQ_STRING, IPIQ_ARGS);
KTR_INFO(KTR_IPIQ, ipiq, send_end, 8, IPIQ_STRING, IPIQ_ARGS);
KTR_INFO(KTR_IPIQ, ipiq, sync_quick, 9, "cpumask=%08lx", unsigned long mask);
#define logipiq(name, func, arg1, arg2, sgd, dgd) \
KTR_LOG(ipiq_ ## name, func, arg1, arg2, sgd->gd_cpuid, dgd->gd_cpuid)
#define logipiq2(name, arg) \
KTR_LOG(ipiq_ ## name, arg)
static void lwkt_process_ipiq_nested(void);
static int lwkt_process_ipiq_core(globaldata_t sgd, lwkt_ipiq_t ip,
struct intrframe *frame, int limit);
static void lwkt_cpusync_remote1(lwkt_cpusync_t cs);
static void lwkt_cpusync_remote2(lwkt_cpusync_t cs);
#define IPIQ_SYSCTL(name) \
static int \
sysctl_##name(SYSCTL_HANDLER_ARGS) \
{ \
int64_t val = 0; \
int cpu, error; \
\
for (cpu = 0; cpu < ncpus; ++cpu) \
val += ipiq_stats_percpu[cpu].name; \
\
error = sysctl_handle_quad(oidp, &val, 0, req); \
if (error || req->newptr == NULL) \
return error; \
\
for (cpu = 0; cpu < ncpus; ++cpu) \
ipiq_stats_percpu[cpu].name = val; \
\
return 0; \
}
IPIQ_SYSCTL(ipiq_count);
IPIQ_SYSCTL(ipiq_fifofull);
IPIQ_SYSCTL(ipiq_avoided);
IPIQ_SYSCTL(ipiq_passive);
IPIQ_SYSCTL(ipiq_cscount);
SYSCTL_PROC(_lwkt, OID_AUTO, ipiq_count, (CTLTYPE_QUAD | CTLFLAG_RW),
0, 0, sysctl_ipiq_count, "Q", "Number of IPI's sent");
SYSCTL_PROC(_lwkt, OID_AUTO, ipiq_fifofull, (CTLTYPE_QUAD | CTLFLAG_RW),
0, 0, sysctl_ipiq_fifofull, "Q",
"Number of fifo full conditions detected");
SYSCTL_PROC(_lwkt, OID_AUTO, ipiq_avoided, (CTLTYPE_QUAD | CTLFLAG_RW),
0, 0, sysctl_ipiq_avoided, "Q",
"Number of IPI's avoided by interlock with target cpu");
SYSCTL_PROC(_lwkt, OID_AUTO, ipiq_passive, (CTLTYPE_QUAD | CTLFLAG_RW),
0, 0, sysctl_ipiq_passive, "Q",
"Number of passive IPI messages sent");
SYSCTL_PROC(_lwkt, OID_AUTO, ipiq_cscount, (CTLTYPE_QUAD | CTLFLAG_RW),
0, 0, sysctl_ipiq_cscount, "Q",
"Number of cpu synchronizations");
int
lwkt_send_ipiq3(globaldata_t target, ipifunc3_t func, void *arg1, int arg2)
{
lwkt_ipiq_t ip;
int windex;
int level1;
int level2;
long rflags;
struct globaldata *gd = mycpu;
logipiq(send_norm, func, arg1, arg2, gd, target);
if (target == gd) {
func(arg1, arg2, NULL);
logipiq(send_end, func, arg1, arg2, gd, target);
return(0);
}
crit_enter();
++gd->gd_intr_nesting_level;
#ifdef INVARIANTS
if (gd->gd_intr_nesting_level > 20)
panic("lwkt_send_ipiq: TOO HEAVILY NESTED!");
#endif
KKASSERT(curthread->td_critcount);
++ipiq_stat(gd).ipiq_count;
ip = &gd->gd_ipiq[target->gd_cpuid];
if (gd->gd_processing_ipiq == 0) {
level1 = MAXCPUFIFO / 2;
level2 = MAXCPUFIFO / 4;
} else {
level1 = MAXCPUFIFO - 3;
level2 = MAXCPUFIFO - 5;
}
if (ip->ip_windex - ip->ip_rindex > level1) {
#ifndef _KERNEL_VIRTUAL
uint64_t tsc_base = rdtsc();
#endif
int repeating = 0;
int olimit;
rflags = read_rflags();
cpu_enable_intr();
++ipiq_stat(gd).ipiq_fifofull;
DEBUG_PUSH_INFO("send_ipiq3");
olimit = atomic_swap_int(&ip->ip_drain, level2);
while (ip->ip_windex - ip->ip_rindex > level2) {
KKASSERT(ip->ip_windex - ip->ip_rindex != MAXCPUFIFO - 1);
lwkt_process_ipiq_nested();
cpu_pause();
#ifdef _KERNEL_VIRTUAL
if (repeating++ > 10)
vkernel_yield();
#else
if (rdtsc() - tsc_base > tsc_frequency) {
++repeating;
if (repeating > 10) {
kprintf("send_ipiq %d->%d tgt not draining (%d) sniff=%p,%p\n",
gd->gd_cpuid, target->gd_cpuid, repeating,
target->gd_sample_pc, target->gd_sample_sp);
smp_sniff();
cpu_disable_intr();
ATOMIC_CPUMASK_ORBIT(target->gd_ipimask, gd->gd_cpuid);
cpu_send_ipiq(target->gd_cpuid);
cpu_enable_intr();
} else {
kprintf("send_ipiq %d->%d tgt not draining (%d)\n",
gd->gd_cpuid, target->gd_cpuid, repeating);
smp_sniff();
}
tsc_base = rdtsc();
}
#endif
}
atomic_swap_int(&ip->ip_drain, olimit);
DEBUG_POP_INFO();
#if defined(__x86_64__)
write_rflags(rflags);
#else
#error "no write_*flags"
#endif
}
rflags = read_rflags();
#ifndef _KERNEL_VIRTUAL
cpu_disable_intr();
#endif
windex = ip->ip_windex & MAXCPUFIFO_MASK;
ip->ip_info[windex].func = func;
ip->ip_info[windex].arg1 = arg1;
ip->ip_info[windex].arg2 = arg2;
cpu_sfence();
++ip->ip_windex;
ATOMIC_CPUMASK_ORBIT(target->gd_ipimask, gd->gd_cpuid);
if (atomic_swap_int(&target->gd_npoll, 1) == 0) {
logipiq(cpu_send, func, arg1, arg2, gd, target);
cpu_send_ipiq(target->gd_cpuid);
} else {
++ipiq_stat(gd).ipiq_avoided;
}
write_rflags(rflags);
--gd->gd_intr_nesting_level;
crit_exit();
logipiq(send_end, func, arg1, arg2, gd, target);
return(ip->ip_windex);
}
int
lwkt_send_ipiq3_passive(globaldata_t target, ipifunc3_t func,
void *arg1, int arg2)
{
lwkt_ipiq_t ip;
int windex;
struct globaldata *gd = mycpu;
KKASSERT(target != gd);
crit_enter_gd(gd);
++gd->gd_intr_nesting_level;
ip = &gd->gd_ipiq[target->gd_cpuid];
if (ip->ip_windex - ip->ip_rindex >= MAXCPUFIFO / 4) {
--gd->gd_intr_nesting_level;
crit_exit_gd(gd);
return lwkt_send_ipiq3(target, func, arg1, arg2);
}
logipiq(send_pasv, func, arg1, arg2, gd, target);
++ipiq_stat(gd).ipiq_count;
++ipiq_stat(gd).ipiq_passive;
windex = ip->ip_windex & MAXCPUFIFO_MASK;
ip->ip_info[windex].func = func;
ip->ip_info[windex].arg1 = arg1;
ip->ip_info[windex].arg2 = arg2;
cpu_sfence();
++ip->ip_windex;
ATOMIC_CPUMASK_ORBIT(target->gd_ipimask, gd->gd_cpuid);
--gd->gd_intr_nesting_level;
crit_exit();
logipiq(send_end, func, arg1, arg2, gd, target);
return(ip->ip_windex);
}
int
lwkt_send_ipiq3_bycpu(int dcpu, ipifunc3_t func, void *arg1, int arg2)
{
return(lwkt_send_ipiq3(globaldata_find(dcpu), func, arg1, arg2));
}
int
lwkt_send_ipiq3_mask(cpumask_t mask, ipifunc3_t func, void *arg1, int arg2)
{
int cpuid;
int count = 0;
cpumask_t amask;
CPUMASK_NANDMASK(mask, stopped_cpus);
CPUMASK_ASSBMASK(amask, mycpu->gd_cpuid);
CPUMASK_INVMASK(amask);
CPUMASK_ANDMASK(amask, mask);
while (CPUMASK_TESTNZERO(amask)) {
cpuid = BSFCPUMASK(amask);
lwkt_send_ipiq3(globaldata_find(cpuid), func, arg1, arg2);
CPUMASK_NANDBIT(amask, cpuid);
++count;
}
CPUMASK_ASSBMASK(amask, mycpu->gd_cpuid);
CPUMASK_ANDMASK(amask, mask);
while (CPUMASK_TESTNZERO(amask)) {
cpuid = BSFCPUMASK(amask);
lwkt_send_ipiq3(globaldata_find(cpuid), func, arg1, arg2);
CPUMASK_NANDBIT(amask, cpuid);
++count;
}
return(count);
}
void
lwkt_wait_ipiq(globaldata_t target, int seq)
{
lwkt_ipiq_t ip;
if (target != mycpu) {
ip = &mycpu->gd_ipiq[target->gd_cpuid];
if ((int)(ip->ip_xindex - seq) < 0) {
#if defined(__x86_64__)
unsigned long rflags = read_rflags();
#else
#error "no read_*flags"
#endif
int64_t time_tgt = tsc_get_target(1000000000LL);
int time_loops = 10;
int benice = 0;
#ifdef _KERNEL_VIRTUAL
int repeating = 0;
#endif
cpu_enable_intr();
DEBUG_PUSH_INFO("wait_ipiq");
while ((int)(ip->ip_xindex - seq) < 0) {
crit_enter();
lwkt_process_ipiq();
crit_exit();
#ifdef _KERNEL_VIRTUAL
if (repeating++ > 10)
vkernel_yield();
#endif
if ((benice & 255) == 0 && tsc_test_target(time_tgt) > 0) {
kprintf("LWKT_WAIT_IPIQ WARNING! %d wait %d (%d)\n",
mycpu->gd_cpuid, target->gd_cpuid,
ip->ip_xindex - seq);
if (--time_loops == 0)
panic("LWKT_WAIT_IPIQ");
time_tgt = tsc_get_target(1000000000LL);
}
++benice;
cpu_pause();
cpu_lfence();
}
DEBUG_POP_INFO();
#if defined(__x86_64__)
write_rflags(rflags);
#else
#error "no write_*flags"
#endif
}
}
}
void
lwkt_process_ipiq(void)
{
globaldata_t gd = mycpu;
globaldata_t sgd;
lwkt_ipiq_t ip;
cpumask_t mask;
int n;
++gd->gd_processing_ipiq;
again:
mask = gd->gd_ipimask;
cpu_ccfence();
while (CPUMASK_TESTNZERO(mask)) {
n = BSFCPUMASK(mask);
if (n != gd->gd_cpuid) {
sgd = globaldata_find(n);
ip = sgd->gd_ipiq;
if (ip != NULL) {
ip += gd->gd_cpuid;
while (lwkt_process_ipiq_core(sgd, ip, NULL, 0))
;
ATOMIC_CPUMASK_NANDBIT(gd->gd_ipimask, n);
if (ip->ip_rindex != ip->ip_windex)
ATOMIC_CPUMASK_ORBIT(gd->gd_ipimask, n);
}
}
CPUMASK_NANDBIT(mask, n);
}
if (lwkt_process_ipiq_core(gd, &gd->gd_cpusyncq, NULL, 0)) {
if (gd->gd_curthread->td_cscount == 0)
goto again;
}
--gd->gd_processing_ipiq;
}
void
lwkt_process_ipiq_frame(struct intrframe *frame)
{
globaldata_t gd = mycpu;
globaldata_t sgd;
lwkt_ipiq_t ip;
cpumask_t mask;
int n;
++gd->gd_processing_ipiq;
again:
mask = gd->gd_ipimask;
cpu_ccfence();
while (CPUMASK_TESTNZERO(mask)) {
n = BSFCPUMASK(mask);
if (n != gd->gd_cpuid) {
sgd = globaldata_find(n);
ip = sgd->gd_ipiq;
if (ip != NULL) {
ip += gd->gd_cpuid;
while (lwkt_process_ipiq_core(sgd, ip, frame, 0))
;
ATOMIC_CPUMASK_NANDBIT(gd->gd_ipimask, n);
if (ip->ip_rindex != ip->ip_windex)
ATOMIC_CPUMASK_ORBIT(gd->gd_ipimask, n);
}
}
CPUMASK_NANDBIT(mask, n);
}
if (gd->gd_cpusyncq.ip_rindex != gd->gd_cpusyncq.ip_windex) {
if (lwkt_process_ipiq_core(gd, &gd->gd_cpusyncq, frame, 0)) {
if (gd->gd_curthread->td_cscount == 0)
goto again;
}
}
--gd->gd_processing_ipiq;
}
static void
lwkt_process_ipiq_nested(void)
{
globaldata_t gd = mycpu;
globaldata_t sgd;
lwkt_ipiq_t ip;
cpumask_t mask;
int n;
int limit;
++gd->gd_processing_ipiq;
again:
mask = gd->gd_ipimask;
cpu_ccfence();
while (CPUMASK_TESTNZERO(mask)) {
n = BSFCPUMASK(mask);
if (n != gd->gd_cpuid) {
sgd = globaldata_find(n);
ip = sgd->gd_ipiq;
if (ip != NULL) {
ip += gd->gd_cpuid;
if ((limit = ip->ip_drain) != 0) {
lwkt_process_ipiq_core(sgd, ip, NULL, limit);
}
}
}
CPUMASK_NANDBIT(mask, n);
}
if (lwkt_process_ipiq_core(gd, &gd->gd_cpusyncq, NULL, 0)) {
if (gd->gd_curthread->td_cscount == 0)
goto again;
}
--gd->gd_processing_ipiq;
}
static int
lwkt_process_ipiq_core(globaldata_t sgd, lwkt_ipiq_t ip,
struct intrframe *frame, int limit)
{
globaldata_t mygd = mycpu;
int ri;
int wi;
ipifunc3_t copy_func;
void *copy_arg1;
int copy_arg2;
KKASSERT(curthread->td_critcount);
wi = ip->ip_windex;
cpu_lfence();
++mygd->gd_intr_nesting_level;
while (wi - (ri = ip->ip_rindex) > limit) {
ri &= MAXCPUFIFO_MASK;
copy_func = ip->ip_info[ri].func;
copy_arg1 = ip->ip_info[ri].arg1;
copy_arg2 = ip->ip_info[ri].arg2;
cpu_ccfence();
++ip->ip_rindex;
logipiq(receive, copy_func, copy_arg1, copy_arg2, sgd, mycpu);
#ifdef INVARIANTS
if (ipiq_debug && (ip->ip_rindex & 0xFFFFFF) == 0) {
kprintf("cpu %d ipifunc %p %p %d (frame %p)\n",
mycpu->gd_cpuid,
copy_func, copy_arg1, copy_arg2,
#if defined(__x86_64__)
(frame ? (void *)frame->if_rip : NULL));
#else
NULL);
#endif
}
#endif
copy_func(copy_arg1, copy_arg2, frame);
cpu_sfence();
ip->ip_xindex = ip->ip_rindex;
#ifdef PANIC_DEBUG
if (mycpu->gd_cpuid == panic_ipiq_cpu && panic_ipiq_count) {
if (--panic_ipiq_count == 0) {
#ifdef DDB
Debugger("PANIC_DEBUG");
#else
panic("PANIC_DEBUG");
#endif
}
}
#endif
}
--mygd->gd_intr_nesting_level;
return (ip->ip_rindex != ip->ip_windex);
}
static void
lwkt_sync_ipiq(void *arg)
{
volatile cpumask_t *cpumask = arg;
ATOMIC_CPUMASK_NANDBIT(*cpumask, mycpu->gd_cpuid);
if (CPUMASK_TESTZERO(*cpumask))
wakeup(cpumask);
}
void
lwkt_synchronize_ipiqs(const char *wmesg)
{
volatile cpumask_t other_cpumask;
other_cpumask = smp_active_mask;
CPUMASK_ANDMASK(other_cpumask, mycpu->gd_other_cpus);
lwkt_send_ipiq_mask(other_cpumask, lwkt_sync_ipiq,
__DEVOLATILE(void *, &other_cpumask));
while (CPUMASK_TESTNZERO(other_cpumask)) {
tsleep_interlock(&other_cpumask, 0);
if (CPUMASK_TESTNZERO(other_cpumask))
tsleep(&other_cpumask, PINTERLOCKED, wmesg, 0);
}
}
void
lwkt_cpusync_simple(cpumask_t mask, cpusync_func_t func, void *arg)
{
struct lwkt_cpusync cs;
lwkt_cpusync_init(&cs, mask, func, arg);
lwkt_cpusync_interlock(&cs);
lwkt_cpusync_deinterlock(&cs);
}
void
lwkt_cpusync_interlock(lwkt_cpusync_t cs)
{
globaldata_t gd = mycpu;
cpumask_t mask;
mask = cs->cs_mask;
CPUMASK_ANDMASK(mask, gd->gd_other_cpus);
CPUMASK_ANDMASK(mask, smp_active_mask);
CPUMASK_ASSZERO(cs->cs_mack);
crit_enter_id("cpusync");
if (CPUMASK_TESTNZERO(mask)) {
DEBUG_PUSH_INFO("cpusync_interlock");
++ipiq_stat(gd).ipiq_cscount;
++gd->gd_curthread->td_cscount;
lwkt_send_ipiq_mask(mask, (ipifunc1_t)lwkt_cpusync_remote1, cs);
logipiq2(sync_start, (long)CPUMASK_LOWMASK(mask));
while (CPUMASK_CMPMASKNEQ(cs->cs_mack, mask)) {
lwkt_process_ipiq();
cpu_pause();
#ifdef _KERNEL_VIRTUAL
vkernel_yield();
#endif
}
DEBUG_POP_INFO();
}
}
void
lwkt_cpusync_deinterlock(lwkt_cpusync_t cs)
{
globaldata_t gd = mycpu;
cpumask_t mask;
mask = cs->cs_mack;
cpu_ccfence();
CPUMASK_ASSZERO(cs->cs_mack);
cpu_ccfence();
if (cs->cs_func && CPUMASK_TESTBIT(cs->cs_mask, gd->gd_cpuid))
cs->cs_func(cs->cs_data);
if (CPUMASK_TESTNZERO(mask)) {
DEBUG_PUSH_INFO("cpusync_deinterlock");
while (CPUMASK_CMPMASKNEQ(cs->cs_mack, mask)) {
lwkt_process_ipiq();
cpu_pause();
#ifdef _KERNEL_VIRTUAL
vkernel_yield();
#endif
}
DEBUG_POP_INFO();
--gd->gd_curthread->td_cscount;
lwkt_process_ipiq();
logipiq2(sync_end, (long)CPUMASK_LOWMASK(mask));
}
crit_exit_id("cpusync");
}
void
lwkt_cpusync_quick(lwkt_cpusync_t cs)
{
globaldata_t gd = mycpu;
cpumask_t mask;
mask = cs->cs_mask;
CPUMASK_ANDMASK(mask, gd->gd_other_cpus);
CPUMASK_ANDMASK(mask, smp_active_mask);
CPUMASK_ASSZERO(cs->cs_mack);
crit_enter_id("cpusync");
if (CPUMASK_TESTNZERO(mask)) {
DEBUG_PUSH_INFO("cpusync_interlock");
++ipiq_stat(gd).ipiq_cscount;
++gd->gd_curthread->td_cscount;
lwkt_send_ipiq_mask(mask, (ipifunc1_t)lwkt_cpusync_remote2, cs);
logipiq2(sync_quick, (long)CPUMASK_LOWMASK(mask));
while (CPUMASK_CMPMASKNEQ(cs->cs_mack, mask)) {
lwkt_process_ipiq();
cpu_pause();
#ifdef _KERNEL_VIRTUAL
vkernel_yield();
#endif
}
DEBUG_POP_INFO();
--gd->gd_curthread->td_cscount;
lwkt_process_ipiq();
}
if (cs->cs_func && CPUMASK_TESTBIT(cs->cs_mask, gd->gd_cpuid))
cs->cs_func(cs->cs_data);
crit_exit_id("cpusync");
}
static void
lwkt_cpusync_remote1(lwkt_cpusync_t cs)
{
globaldata_t gd = mycpu;
ATOMIC_CPUMASK_ORBIT(cs->cs_mack, gd->gd_cpuid);
lwkt_cpusync_remote2(cs);
}
static void
lwkt_cpusync_remote2(lwkt_cpusync_t cs)
{
globaldata_t gd = mycpu;
if (CPUMASK_TESTMASK(cs->cs_mack, gd->gd_cpumask) == 0) {
if (cs->cs_func)
cs->cs_func(cs->cs_data);
ATOMIC_CPUMASK_ORBIT(cs->cs_mack, gd->gd_cpuid);
} else {
lwkt_ipiq_t ip;
int wi;
cpu_pause();
#ifdef _KERNEL_VIRTUAL
vkernel_yield();
#endif
cpu_lfence();
ip = &gd->gd_cpusyncq;
wi = ip->ip_windex & MAXCPUFIFO_MASK;
ip->ip_info[wi].func = (ipifunc3_t)(ipifunc1_t)lwkt_cpusync_remote2;
ip->ip_info[wi].arg1 = cs;
ip->ip_info[wi].arg2 = 0;
cpu_sfence();
KKASSERT(ip->ip_windex - ip->ip_rindex < MAXCPUFIFO);
++ip->ip_windex;
if (ipiq_debug && (ip->ip_windex & 0xFFFFFF) == 0) {
kprintf("cpu %d cm=%016jx %016jx f=%p\n",
gd->gd_cpuid,
(intmax_t)CPUMASK_LOWMASK(cs->cs_mask),
(intmax_t)CPUMASK_LOWMASK(cs->cs_mack),
cs->cs_func);
}
}
}
#define LWKT_IPIQ_NLATENCY 8
#define LWKT_IPIQ_NLATENCY_MASK (LWKT_IPIQ_NLATENCY - 1)
struct lwkt_ipiq_latency_log {
int idx;
int pad;
uint64_t latency[LWKT_IPIQ_NLATENCY];
};
static struct lwkt_ipiq_latency_log lwkt_ipiq_latency_logs[MAXCPU];
static uint64_t save_tsc;
static void
lwkt_ipiq_latency_testfunc(void *arg __unused)
{
uint64_t delta_tsc;
struct globaldata *gd;
struct lwkt_ipiq_latency_log *lat;
delta_tsc = rdtsc_ordered() - save_tsc;
delta_tsc = delta_tsc * 1000000000LU / tsc_frequency;
gd = mycpu;
lat = &lwkt_ipiq_latency_logs[gd->gd_cpuid];
lat->latency[lat->idx & LWKT_IPIQ_NLATENCY_MASK] = delta_tsc;
++lat->idx;
}
static int
lwkt_ipiq_latency_test(SYSCTL_HANDLER_ARGS)
{
struct globaldata *gd;
int cpu = 0, orig_cpu, error;
error = sysctl_handle_int(oidp, &cpu, arg2, req);
if (error || req->newptr == NULL)
return error;
if (cpu == 0)
return 0;
else if (cpu >= ncpus || cpu < 0)
return EINVAL;
orig_cpu = mycpuid;
lwkt_migratecpu(0);
gd = globaldata_find(cpu);
save_tsc = rdtsc_ordered();
lwkt_send_ipiq(gd, lwkt_ipiq_latency_testfunc, NULL);
lwkt_migratecpu(orig_cpu);
return 0;
}
SYSCTL_NODE(_debug, OID_AUTO, ipiq, CTLFLAG_RW, 0, "");
SYSCTL_PROC(_debug_ipiq, OID_AUTO, latency_test, CTLTYPE_INT | CTLFLAG_RW,
NULL, 0, lwkt_ipiq_latency_test, "I",
"ipi latency test, arg: remote cpuid");
static int
lwkt_ipiq_latency(SYSCTL_HANDLER_ARGS)
{
struct lwkt_ipiq_latency_log *latency = arg1;
uint64_t lat[LWKT_IPIQ_NLATENCY];
int i;
for (i = 0; i < LWKT_IPIQ_NLATENCY; ++i)
lat[i] = latency->latency[i];
return sysctl_handle_opaque(oidp, lat, sizeof(lat), req);
}
static void
lwkt_ipiq_latency_init(void *dummy __unused)
{
int cpu;
for (cpu = 0; cpu < ncpus; ++cpu) {
char name[32];
ksnprintf(name, sizeof(name), "latency%d", cpu);
SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_debug_ipiq),
OID_AUTO, name, CTLTYPE_OPAQUE | CTLFLAG_RD,
&lwkt_ipiq_latency_logs[cpu], 0, lwkt_ipiq_latency,
"LU", "7 latest ipi latency measurement results");
}
}
SYSINIT(lwkt_ipiq_latency, SI_SUB_CONFIGURE, SI_ORDER_ANY,
lwkt_ipiq_latency_init, NULL);