#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/sysctl.h>
#include <sys/ktr.h>
#include <sys/kthread.h>
#include <machine/cpu.h>
#include <sys/lock.h>
#include <sys/spinlock.h>
#include <sys/thread2.h>
#include <sys/spinlock2.h>
#include <sys/mplock2.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 "opt_ddb.h"
#ifdef DDB
#include <ddb/ddb.h>
#endif
extern int lwkt_sched_debug;
#define LWKT_POOL_TOKENS 16384
#define LWKT_POOL_MASK (LWKT_POOL_TOKENS - 1)
struct lwkt_pool_token {
struct lwkt_token token;
} __cachealign;
static struct lwkt_pool_token pool_tokens[LWKT_POOL_TOKENS];
static struct spinlock tok_debug_spin =
SPINLOCK_INITIALIZER(&tok_debug_spin, "tok_debug_spin");
#define TOKEN_STRING "REF=%p TOK=%p TD=%p"
#define TOKEN_ARGS lwkt_tokref_t ref, lwkt_token_t tok, struct thread *td
#define CONTENDED_STRING TOKEN_STRING " (contention started)"
#define UNCONTENDED_STRING TOKEN_STRING " (contention stopped)"
#if !defined(KTR_TOKENS)
#define KTR_TOKENS KTR_ALL
#endif
KTR_INFO_MASTER(tokens);
KTR_INFO(KTR_TOKENS, tokens, fail, 0, TOKEN_STRING, TOKEN_ARGS);
KTR_INFO(KTR_TOKENS, tokens, succ, 1, TOKEN_STRING, TOKEN_ARGS);
#if 0
KTR_INFO(KTR_TOKENS, tokens, release, 2, TOKEN_STRING, TOKEN_ARGS);
KTR_INFO(KTR_TOKENS, tokens, remote, 3, TOKEN_STRING, TOKEN_ARGS);
KTR_INFO(KTR_TOKENS, tokens, reqremote, 4, TOKEN_STRING, TOKEN_ARGS);
KTR_INFO(KTR_TOKENS, tokens, reqfail, 5, TOKEN_STRING, TOKEN_ARGS);
KTR_INFO(KTR_TOKENS, tokens, drain, 6, TOKEN_STRING, TOKEN_ARGS);
KTR_INFO(KTR_TOKENS, tokens, contention_start, 7, CONTENDED_STRING, TOKEN_ARGS);
KTR_INFO(KTR_TOKENS, tokens, contention_stop, 7, UNCONTENDED_STRING, TOKEN_ARGS);
#endif
#define logtoken(name, ref) \
KTR_LOG(tokens_ ## name, ref, ref->tr_tok, curthread)
struct lwkt_token mp_token = LWKT_TOKEN_INITIALIZER(mp_token);
struct lwkt_token pmap_token = LWKT_TOKEN_INITIALIZER(pmap_token);
struct lwkt_token dev_token = LWKT_TOKEN_INITIALIZER(dev_token);
struct lwkt_token vm_token = LWKT_TOKEN_INITIALIZER(vm_token);
struct lwkt_token vmspace_token = LWKT_TOKEN_INITIALIZER(vmspace_token);
struct lwkt_token kvm_token = LWKT_TOKEN_INITIALIZER(kvm_token);
struct lwkt_token sigio_token = LWKT_TOKEN_INITIALIZER(sigio_token);
struct lwkt_token tty_token = LWKT_TOKEN_INITIALIZER(tty_token);
struct lwkt_token vnode_token = LWKT_TOKEN_INITIALIZER(vnode_token);
struct lwkt_token vga_token = LWKT_TOKEN_INITIALIZER(vga_token);
struct lwkt_token kbd_token = LWKT_TOKEN_INITIALIZER(kbd_token);
static int token_backoff_max __cachealign = 4096;
SYSCTL_INT(_lwkt, OID_AUTO, token_backoff_max, CTLFLAG_RW,
&token_backoff_max, 0, "Tokens exponential backoff");
static int token_window_shift __cachealign = 8;
SYSCTL_INT(_lwkt, OID_AUTO, token_window_shift, CTLFLAG_RW,
&token_window_shift, 0, "Tokens TSC windowing shift");
SYSCTL_LONG(_lwkt, OID_AUTO, mp_collisions, CTLFLAG_RW,
&mp_token.t_collisions, 0, "Collision counter of mp_token");
SYSCTL_LONG(_lwkt, OID_AUTO, pmap_collisions, CTLFLAG_RW,
&pmap_token.t_collisions, 0, "Collision counter of pmap_token");
SYSCTL_LONG(_lwkt, OID_AUTO, dev_collisions, CTLFLAG_RW,
&dev_token.t_collisions, 0, "Collision counter of dev_token");
SYSCTL_LONG(_lwkt, OID_AUTO, vm_collisions, CTLFLAG_RW,
&vm_token.t_collisions, 0, "Collision counter of vm_token");
SYSCTL_LONG(_lwkt, OID_AUTO, vmspace_collisions, CTLFLAG_RW,
&vmspace_token.t_collisions, 0, "Collision counter of vmspace_token");
SYSCTL_LONG(_lwkt, OID_AUTO, kvm_collisions, CTLFLAG_RW,
&kvm_token.t_collisions, 0, "Collision counter of kvm_token");
SYSCTL_LONG(_lwkt, OID_AUTO, sigio_collisions, CTLFLAG_RW,
&sigio_token.t_collisions, 0, "Collision counter of sigio_token");
SYSCTL_LONG(_lwkt, OID_AUTO, tty_collisions, CTLFLAG_RW,
&tty_token.t_collisions, 0, "Collision counter of tty_token");
SYSCTL_LONG(_lwkt, OID_AUTO, vnode_collisions, CTLFLAG_RW,
&vnode_token.t_collisions, 0, "Collision counter of vnode_token");
static int tokens_debug_output;
SYSCTL_INT(_lwkt, OID_AUTO, tokens_debug_output, CTLFLAG_RW,
&tokens_debug_output, 0, "Generate stack trace N times");
static int _lwkt_getalltokens_sorted(thread_t td);
void
cpu_get_initial_mplock(void)
{
KKASSERT(mp_token.t_ref == NULL);
if (lwkt_trytoken(&mp_token) == FALSE)
panic("cpu_get_initial_mplock");
}
#define POOL_HASH_PRIME1 66555444443333333ULL
#define POOL_HASH_PRIME2 989042931893ULL
static __inline
lwkt_token_t
_lwkt_token_pool_lookup(void *ptr)
{
uintptr_t hash1;
uintptr_t hash2;
hash1 = (uintptr_t)ptr + ((uintptr_t)ptr >> 18);
hash1 %= POOL_HASH_PRIME1;
hash2 = ((uintptr_t)ptr >> 8) + ((uintptr_t)ptr >> 24);
hash2 %= POOL_HASH_PRIME2;
return (&pool_tokens[(hash1 ^ hash2) & LWKT_POOL_MASK].token);
}
static __inline
void
_lwkt_tokref_init(lwkt_tokref_t ref, lwkt_token_t tok, thread_t td, long excl)
{
ref->tr_tok = tok;
ref->tr_count = excl;
ref->tr_owner = td;
}
static __inline
int
_lwkt_trytokref(lwkt_tokref_t ref, thread_t td, long mode)
{
lwkt_token_t tok;
lwkt_tokref_t oref;
long count;
tok = ref->tr_tok;
KASSERT(((mode & TOK_EXCLREQ) == 0 ||
td->td_gd->gd_intr_nesting_level == 0 ||
panic_cpu_gd == mycpu),
("Attempt to acquire token %p not already "
"held in hard code section", tok));
if (mode & TOK_EXCLUSIVE) {
count = tok->t_count;
for (;;) {
oref = tok->t_ref;
cpu_ccfence();
if ((count & ~TOK_EXCLREQ) == 0) {
if (atomic_fcmpset_long(&tok->t_count, &count,
(count & ~TOK_EXCLREQ) |
TOK_EXCLUSIVE)) {
KKASSERT(tok->t_ref == NULL);
tok->t_ref = ref;
return TRUE;
}
} else if ((count & TOK_EXCLUSIVE) &&
oref >= &td->td_toks_base &&
oref < td->td_toks_stop) {
atomic_add_long(&tok->t_count, TOK_INCR);
ref->tr_count &= ~TOK_EXCLUSIVE;
return TRUE;
} else if ((mode & TOK_EXCLREQ) &&
(count & TOK_EXCLREQ) == 0) {
atomic_set_long(&tok->t_count, TOK_EXCLREQ);
return FALSE;
} else {
cpu_pause();
return FALSE;
}
}
} else {
count = tok->t_count;
for (;;) {
oref = tok->t_ref;
cpu_ccfence();
if ((count & (TOK_EXCLUSIVE|mode)) == 0 ||
((count & TOK_EXCLUSIVE) == 0 &&
td->td_toks_stop != &td->td_toks_base + 1)
) {
if ((atomic_fetchadd_long(&tok->t_count, TOK_INCR) & TOK_EXCLUSIVE) == 0) {
return TRUE;
}
count = atomic_fetchadd_long(&tok->t_count,
-TOK_INCR);
count -= TOK_INCR;
} else if ((count & TOK_EXCLUSIVE) &&
oref >= &td->td_toks_base &&
oref < td->td_toks_stop) {
atomic_add_long(&tok->t_count, TOK_INCR);
return TRUE;
} else {
return FALSE;
}
}
}
}
static __inline
int
_lwkt_trytokref_spin(lwkt_tokref_t ref, thread_t td, long mode)
{
if (_lwkt_trytokref(ref, td, mode))
return TRUE;
if (mode & TOK_EXCLUSIVE) {
long expbackoff;
long loop;
expbackoff = 0;
while (expbackoff < 6 + token_backoff_max) {
expbackoff = (expbackoff + 1) * 3 / 2;
if ((rdtsc() >> token_window_shift) % ncpus != mycpuid) {
for (loop = expbackoff; loop; --loop)
cpu_pause();
}
if (_lwkt_trytokref(ref, td, mode))
return TRUE;
}
} else {
if ((rdtsc() >> token_window_shift) % ncpus == mycpuid) {
if (_lwkt_trytokref(ref, td, mode & ~TOK_EXCLREQ))
return TRUE;
} else {
if (_lwkt_trytokref(ref, td, mode))
return TRUE;
}
}
++mycpu->gd_cnt.v_lock_colls;
return FALSE;
}
static __inline
void
_lwkt_reltokref(lwkt_tokref_t ref, thread_t td)
{
lwkt_token_t tok;
long count;
tok = ref->tr_tok;
if (tok->t_ref == ref) {
#if 0
KKASSERT(count & TOK_EXCLUSIVE);
#endif
tok->t_ref = NULL;
atomic_clear_long(&tok->t_count, TOK_EXCLUSIVE);
} else {
count = atomic_fetchadd_long(&tok->t_count, -TOK_INCR);
KKASSERT(count & TOK_COUNTMASK);
}
}
int
lwkt_getalltokens(thread_t td, int spinning)
{
lwkt_tokref_t scan;
lwkt_token_t tok;
if (spinning)
return(_lwkt_getalltokens_sorted(td));
for (scan = &td->td_toks_base; scan < td->td_toks_stop; ++scan) {
tok = scan->tr_tok;
for (;;) {
if (scan == td->td_toks_stop - 1) {
if (_lwkt_trytokref_spin(scan, td, scan->tr_count))
break;
} else {
if (_lwkt_trytokref(scan, td, scan->tr_count))
break;
}
KASSERT(tok->t_desc,
("token %p is not initialized", tok));
td->td_gd->gd_cnt.v_lock_addr = tok;
td->td_gd->gd_cnt.v_lock_name[0] = 't';
strncpy(td->td_gd->gd_cnt.v_lock_name + 1,
tok->t_desc,
sizeof(td->td_gd->gd_cnt.v_lock_name) - 2);
if (lwkt_sched_debug > 0) {
--lwkt_sched_debug;
kprintf("toka %p %s %s\n",
tok, tok->t_desc, td->td_comm);
}
td->td_wmesg = tok->t_desc;
++tok->t_collisions;
while (--scan >= &td->td_toks_base)
_lwkt_reltokref(scan, td);
return(FALSE);
}
}
return (TRUE);
}
void
lwkt_relalltokens(thread_t td)
{
lwkt_tokref_t scan;
if (td->td_toks_have) {
scan = td->td_toks_have;
td->td_toks_have = NULL;
} else {
scan = td->td_toks_stop;
}
while (--scan >= &td->td_toks_base)
_lwkt_reltokref(scan, td);
}
static
int
_lwkt_getalltokens_sorted(thread_t td)
{
lwkt_tokref_t sort_array[LWKT_MAXTOKENS];
lwkt_tokref_t scan;
lwkt_token_t tok;
int i;
int j;
int n;
i = 0;
scan = &td->td_toks_base;
while (scan < td->td_toks_stop) {
for (j = 0; j < i; ++j) {
if (scan->tr_tok < sort_array[j]->tr_tok)
break;
}
if (j != i) {
bcopy(sort_array + j, sort_array + j + 1,
(i - j) * sizeof(lwkt_tokref_t));
}
sort_array[j] = scan;
++scan;
++i;
}
n = i;
for (i = 0; i < n; ++i) {
scan = sort_array[i];
tok = scan->tr_tok;
for (;;) {
if (scan == td->td_toks_stop - 1) {
if (_lwkt_trytokref_spin(scan, td, scan->tr_count))
break;
} else {
if (_lwkt_trytokref(scan, td, scan->tr_count))
break;
}
td->td_gd->gd_cnt.v_lock_addr = tok;
td->td_gd->gd_cnt.v_lock_name[0] = 't';
strncpy(td->td_gd->gd_cnt.v_lock_name + 1,
tok->t_desc,
sizeof(td->td_gd->gd_cnt.v_lock_name) - 2);
if (lwkt_sched_debug > 0) {
--lwkt_sched_debug;
kprintf("tokb %p %s %s\n",
tok, tok->t_desc, td->td_comm);
}
td->td_wmesg = tok->t_desc;
++tok->t_collisions;
while (--i >= 0) {
scan = sort_array[i];
_lwkt_reltokref(scan, td);
}
return(FALSE);
}
}
return (TRUE);
}
void
lwkt_gettoken(lwkt_token_t tok)
{
thread_t td = curthread;
lwkt_tokref_t ref;
ref = td->td_toks_stop;
KKASSERT(ref < &td->td_toks_end);
++td->td_toks_stop;
cpu_ccfence();
_lwkt_tokref_init(ref, tok, td, TOK_EXCLUSIVE|TOK_EXCLREQ);
#ifdef DEBUG_LOCKS
lwkt_tokref_t tk;
int found = 0;
for (tk = &td->td_toks_base; tk < ref; tk++) {
if (tk->tr_tok != tok)
continue;
found++;
if (tk->tr_count & TOK_EXCLUSIVE)
goto good;
}
KASSERT((found == 0), ("Token %p s/x livelock", tok));
good:
#endif
if (_lwkt_trytokref_spin(ref, td, TOK_EXCLUSIVE|TOK_EXCLREQ))
return;
td->td_wmesg = tok->t_desc;
++tok->t_collisions;
logtoken(fail, ref);
td->td_toks_have = td->td_toks_stop - 1;
if (tokens_debug_output > 0) {
--tokens_debug_output;
spin_lock(&tok_debug_spin);
kprintf("Excl Token %p thread %p %s %s\n",
tok, td, tok->t_desc, td->td_comm);
print_backtrace(6);
kprintf("\n");
spin_unlock(&tok_debug_spin);
}
atomic_set_int(&td->td_mpflags, TDF_MP_DIDYIELD);
lwkt_switch();
logtoken(succ, ref);
KKASSERT(tok->t_ref == ref);
}
void
lwkt_gettoken_shared(lwkt_token_t tok)
{
thread_t td = curthread;
lwkt_tokref_t ref;
ref = td->td_toks_stop;
KKASSERT(ref < &td->td_toks_end);
++td->td_toks_stop;
cpu_ccfence();
_lwkt_tokref_init(ref, tok, td, TOK_EXCLREQ);
#ifdef DEBUG_LOCKS
if ((tok >= &pool_tokens[0].token) &&
(tok < &pool_tokens[LWKT_POOL_TOKENS].token))
kprintf("Warning! Taking pool token %p in shared mode\n", tok);
#endif
if (_lwkt_trytokref_spin(ref, td, TOK_EXCLREQ))
return;
td->td_wmesg = tok->t_desc;
++tok->t_collisions;
logtoken(fail, ref);
td->td_toks_have = td->td_toks_stop - 1;
if (tokens_debug_output > 0) {
--tokens_debug_output;
spin_lock(&tok_debug_spin);
kprintf("Shar Token %p thread %p %s %s\n",
tok, td, tok->t_desc, td->td_comm);
print_backtrace(6);
kprintf("\n");
spin_unlock(&tok_debug_spin);
}
atomic_set_int(&td->td_mpflags, TDF_MP_DIDYIELD);
lwkt_switch();
logtoken(succ, ref);
}
int
lwkt_trytoken(lwkt_token_t tok)
{
thread_t td = curthread;
lwkt_tokref_t ref;
ref = td->td_toks_stop;
KKASSERT(ref < &td->td_toks_end);
++td->td_toks_stop;
cpu_ccfence();
_lwkt_tokref_init(ref, tok, td, TOK_EXCLUSIVE|TOK_EXCLREQ);
if (_lwkt_trytokref(ref, td, TOK_EXCLUSIVE))
return TRUE;
cpu_ccfence();
--td->td_toks_stop;
++tok->t_collisions;
return FALSE;
}
lwkt_token_t
lwkt_getpooltoken(void *ptr)
{
lwkt_token_t tok;
tok = _lwkt_token_pool_lookup(ptr);
lwkt_gettoken(tok);
return (tok);
}
void
lwkt_reltoken(lwkt_token_t tok)
{
thread_t td = curthread;
lwkt_tokref_t ref;
ref = td->td_toks_stop - 1;
if (__predict_false(ref < &td->td_toks_base || ref->tr_tok != tok)) {
kprintf("LWKT_RELTOKEN ASSERTION td %p tok %p ref %p/%p\n",
td, tok, &td->td_toks_base, ref);
kprintf("REF CONTENT: tok=%p count=%016lx owner=%p\n",
ref->tr_tok, ref->tr_count, ref->tr_owner);
if (ref < &td->td_toks_base) {
kprintf("lwkt_reltoken: no tokens to release\n");
} else {
kprintf("lwkt_reltoken: release wants %s and got %s\n",
tok->t_desc, ref->tr_tok->t_desc);
}
panic("lwkt_reltoken: illegal release");
}
_lwkt_reltokref(ref, td);
cpu_sfence();
td->td_toks_stop = ref;
}
void
lwkt_relpooltoken(void *ptr)
{
lwkt_token_t tok = _lwkt_token_pool_lookup(ptr);
lwkt_reltoken(tok);
}
int
lwkt_cnttoken(lwkt_token_t tok, thread_t td)
{
lwkt_tokref_t scan;
int count = 0;
for (scan = &td->td_toks_base; scan < td->td_toks_stop; ++scan) {
if (scan->tr_tok == tok)
++count;
}
return(count);
}
void
lwkt_token_pool_init(void)
{
int i;
for (i = 0; i < LWKT_POOL_TOKENS; ++i)
lwkt_token_init(&pool_tokens[i].token, "pool");
}
lwkt_token_t
lwkt_token_pool_lookup(void *ptr)
{
return (_lwkt_token_pool_lookup(ptr));
}
void
lwkt_token_init(lwkt_token_t tok, const char *desc)
{
tok->t_count = 0;
tok->t_ref = NULL;
tok->t_collisions = 0;
tok->t_desc = desc;
}
void
lwkt_token_uninit(lwkt_token_t tok)
{
}
void
lwkt_token_swap(void)
{
lwkt_tokref_t ref1, ref2;
lwkt_token_t tok1, tok2;
long count1, count2;
thread_t td = curthread;
crit_enter();
ref1 = td->td_toks_stop - 1;
ref2 = td->td_toks_stop - 2;
KKASSERT(ref1 >= &td->td_toks_base);
KKASSERT(ref2 >= &td->td_toks_base);
tok1 = ref1->tr_tok;
tok2 = ref2->tr_tok;
count1 = ref1->tr_count;
count2 = ref2->tr_count;
if (tok1 != tok2) {
ref1->tr_tok = tok2;
ref1->tr_count = count2;
ref2->tr_tok = tok1;
ref2->tr_count = count1;
if (tok1->t_ref == ref1)
tok1->t_ref = ref2;
if (tok2->t_ref == ref2)
tok2->t_ref = ref1;
}
crit_exit();
}
#ifdef DDB
DB_SHOW_COMMAND(tokens, db_tok_all)
{
struct lwkt_token *tok, **ptr;
struct lwkt_token *toklist[16] = {
&mp_token,
&pmap_token,
&dev_token,
&vm_token,
&vmspace_token,
&kvm_token,
&sigio_token,
&tty_token,
&vnode_token,
NULL
};
ptr = toklist;
for (tok = *ptr; tok; tok = *(++ptr)) {
db_printf("tok=%p tr_owner=%p t_colissions=%ld t_desc=%s\n", tok,
(tok->t_ref ? tok->t_ref->tr_owner : NULL),
tok->t_collisions, tok->t_desc);
}
}
#endif