#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/serialize.h>
#include <sys/sysctl.h>
#include <sys/ktr.h>
#include <sys/kthread.h>
#include <machine/cpu.h>
#include <machine/cpufunc.h>
#include <machine/specialreg.h>
#include <machine/clock.h>
#include <sys/lock.h>
#ifndef SLZ_ADAPTIVE_SPINMAX
#define SLZ_ADAPTIVE_SPINMAX 4096
#endif
#define SLZ_KTR_STRING "slz=%p"
#define SLZ_KTR_ARGS lwkt_serialize_t slz
#ifndef KTR_SERIALIZER
#define KTR_SERIALIZER KTR_ALL
#endif
KTR_INFO_MASTER(slz);
KTR_INFO(KTR_SERIALIZER, slz, enter_beg, 0, SLZ_KTR_STRING, SLZ_KTR_ARGS);
KTR_INFO(KTR_SERIALIZER, slz, sleep_beg, 1, SLZ_KTR_STRING, SLZ_KTR_ARGS);
KTR_INFO(KTR_SERIALIZER, slz, sleep_end, 2, SLZ_KTR_STRING, SLZ_KTR_ARGS);
KTR_INFO(KTR_SERIALIZER, slz, exit_end, 3, SLZ_KTR_STRING, SLZ_KTR_ARGS);
KTR_INFO(KTR_SERIALIZER, slz, wakeup_beg, 4, SLZ_KTR_STRING, SLZ_KTR_ARGS);
KTR_INFO(KTR_SERIALIZER, slz, wakeup_end, 5, SLZ_KTR_STRING, SLZ_KTR_ARGS);
KTR_INFO(KTR_SERIALIZER, slz, try, 6, SLZ_KTR_STRING, SLZ_KTR_ARGS);
KTR_INFO(KTR_SERIALIZER, slz, tryfail, 7, SLZ_KTR_STRING, SLZ_KTR_ARGS);
KTR_INFO(KTR_SERIALIZER, slz, tryok, 8, SLZ_KTR_STRING, SLZ_KTR_ARGS);
KTR_INFO(KTR_SERIALIZER, slz, enter_end, 9, SLZ_KTR_STRING, SLZ_KTR_ARGS);
KTR_INFO(KTR_SERIALIZER, slz, exit_beg, 10, SLZ_KTR_STRING, SLZ_KTR_ARGS);
KTR_INFO(KTR_SERIALIZER, slz, adapt_beg, 11, SLZ_KTR_STRING, SLZ_KTR_ARGS);
KTR_INFO(KTR_SERIALIZER, slz, adapt_end, 12, SLZ_KTR_STRING, SLZ_KTR_ARGS);
KTR_INFO(KTR_SERIALIZER, slz, adapt_spinend, 13, "slz=%p try=%d",
lwkt_serialize_t slz, int try);
KTR_INFO(KTR_SERIALIZER, slz, adapt_sleepb, 14, SLZ_KTR_STRING, SLZ_KTR_ARGS);
KTR_INFO(KTR_SERIALIZER, slz, adapt_sleepe, 15, SLZ_KTR_STRING, SLZ_KTR_ARGS);
#define logslz(name, slz) KTR_LOG(slz_ ## name, slz)
#define logslz_spinend(slz, try) KTR_LOG(slz_adapt_spinend, slz, try)
static void lwkt_serialize_sleep(void *info);
static void lwkt_serialize_wakeup(void *info);
void
lwkt_serialize_init(lwkt_serialize_t s)
{
atomic_intr_init(&s->interlock);
s->last_td = (void *)-4;
}
void
lwkt_serialize_enter(lwkt_serialize_t s)
{
ASSERT_NOT_SERIALIZED(s);
logslz(enter_beg, s);
atomic_intr_cond_enter(&s->interlock, lwkt_serialize_sleep, s);
logslz(enter_end, s);
s->last_td = curthread;
}
int
lwkt_serialize_try(lwkt_serialize_t s)
{
int error;
ASSERT_NOT_SERIALIZED(s);
logslz(try, s);
if ((error = atomic_intr_cond_try(&s->interlock)) == 0) {
s->last_td = curthread;
logslz(tryok, s);
return(1);
}
logslz(tryfail, s);
return (0);
}
void
lwkt_serialize_exit(lwkt_serialize_t s)
{
ASSERT_SERIALIZED(s);
s->last_td = (void *)-2;
logslz(exit_beg, s);
atomic_intr_cond_exit(&s->interlock, lwkt_serialize_wakeup, s);
logslz(exit_end, s);
}
void
lwkt_serialize_handler_disable(lwkt_serialize_t s)
{
atomic_intr_handler_disable(&s->interlock);
}
void
lwkt_serialize_handler_enable(lwkt_serialize_t s)
{
atomic_intr_handler_enable(&s->interlock);
}
void
lwkt_serialize_handler_call(lwkt_serialize_t s, void (*func)(void *, void *),
void *arg, void *frame)
{
if (atomic_intr_handler_is_enabled(&s->interlock) == 0) {
logslz(enter_beg, s);
atomic_intr_cond_enter(&s->interlock, lwkt_serialize_sleep, s);
logslz(enter_end, s);
s->last_td = curthread;
if (atomic_intr_handler_is_enabled(&s->interlock) == 0)
func(arg, frame);
ASSERT_SERIALIZED(s);
s->last_td = (void *)-2;
logslz(exit_beg, s);
atomic_intr_cond_exit(&s->interlock, lwkt_serialize_wakeup, s);
logslz(exit_end, s);
}
}
int
lwkt_serialize_handler_try(lwkt_serialize_t s, void (*func)(void *, void *),
void *arg, void *frame)
{
if (atomic_intr_handler_is_enabled(&s->interlock) == 0) {
logslz(try, s);
if (atomic_intr_cond_try(&s->interlock) == 0) {
s->last_td = curthread;
logslz(tryok, s);
func(arg, frame);
ASSERT_SERIALIZED(s);
s->last_td = (void *)-2;
logslz(exit_beg, s);
atomic_intr_cond_exit(&s->interlock, lwkt_serialize_wakeup, s);
logslz(exit_end, s);
return(0);
}
}
logslz(tryfail, s);
return(1);
}
static void
lwkt_serialize_sleep(void *info)
{
lwkt_serialize_t s = info;
tsleep_interlock(s, 0);
if (atomic_intr_cond_test(&s->interlock) != 0) {
logslz(sleep_beg, s);
tsleep(s, PINTERLOCKED, "slize", 0);
logslz(sleep_end, s);
}
}
void
lwkt_serialize_adaptive_enter(lwkt_serialize_t s)
{
int try;
ASSERT_NOT_SERIALIZED(s);
logslz(adapt_beg, s);
if (atomic_intr_cond_try(&s->interlock) == 0) {
s->last_td = curthread;
logslz(adapt_end, s);
return;
}
restart:
for (try = SLZ_ADAPTIVE_SPINMAX; try; --try) {
if (atomic_intr_cond_test(&s->interlock) == 0 &&
atomic_intr_cond_try(&s->interlock) == 0) {
s->last_td = curthread;
logslz_spinend(s, try);
return;
}
}
atomic_intr_cond_inc(&s->interlock);
tsleep_interlock(s, 0);
if (atomic_intr_cond_try(&s->interlock) == 0) {
atomic_intr_cond_dec(&s->interlock);
s->last_td = curthread;
logslz_spinend(s, 0);
return;
} else {
logslz(adapt_sleepb, s);
tsleep(s, PINTERLOCKED, "aslize", 0);
logslz(adapt_sleepe, s);
atomic_intr_cond_dec(&s->interlock);
goto restart;
}
}
static void
lwkt_serialize_wakeup(void *info)
{
logslz(wakeup_beg, info);
wakeup(info);
logslz(wakeup_end, info);
}