#include <sys/param.h>
#include <sys/types.h>
#include <sys/tzfile.h>
#include <sys/atomic.h>
#include <sys/disp.h>
#include <sys/kidmap.h>
#include <sys/time.h>
#include <sys/spl.h>
#include <sys/random.h>
#include <smbsrv/smb_kproto.h>
#include <smbsrv/smb_fsops.h>
#include <smbsrv/smbinfo.h>
#include <smbsrv/smb_xdr.h>
#include <smbsrv/smb_vops.h>
#include <smbsrv/smb_idmap.h>
#include <sys/sid.h>
#include <sys/priv_names.h>
#ifdef _FAKE_KERNEL
#define THR_TO_DID(t) ((kt_did_t)(uintptr_t)t)
#else
#define THR_TO_DID(t) (t->t_did)
#endif
static boolean_t smb_thread_continue_timedwait_locked(smb_thread_t *, int);
static void
smb_thread_entry_point(
smb_thread_t *thread)
{
ASSERT(thread->sth_magic == SMB_THREAD_MAGIC);
mutex_enter(&thread->sth_mtx);
ASSERT(thread->sth_state == SMB_THREAD_STATE_STARTING);
if (!thread->sth_kill) {
thread->sth_state = SMB_THREAD_STATE_RUNNING;
cv_signal(&thread->sth_cv);
mutex_exit(&thread->sth_mtx);
thread->sth_ep(thread, thread->sth_ep_arg);
mutex_enter(&thread->sth_mtx);
}
thread->sth_th = NULL;
thread->sth_state = SMB_THREAD_STATE_EXITING;
cv_broadcast(&thread->sth_cv);
mutex_exit(&thread->sth_mtx);
#ifdef _KERNEL
if (curthread->t_lwp != NULL) {
mutex_enter(&curproc->p_lock);
lwp_exit();
}
#endif
thread_exit();
}
void
smb_thread_init(
smb_thread_t *thread,
char *name,
smb_thread_ep_t ep,
void *ep_arg,
pri_t pri,
smb_server_t *sv)
{
ASSERT(thread->sth_magic != SMB_THREAD_MAGIC);
bzero(thread, sizeof (*thread));
(void) strlcpy(thread->sth_name, name, sizeof (thread->sth_name));
thread->sth_server = sv;
thread->sth_ep = ep;
thread->sth_ep_arg = ep_arg;
thread->sth_state = SMB_THREAD_STATE_EXITED;
thread->sth_pri = pri;
mutex_init(&thread->sth_mtx, NULL, MUTEX_DEFAULT, NULL);
cv_init(&thread->sth_cv, NULL, CV_DEFAULT, NULL);
thread->sth_magic = SMB_THREAD_MAGIC;
}
void
smb_thread_destroy(
smb_thread_t *thread)
{
ASSERT(thread->sth_magic == SMB_THREAD_MAGIC);
ASSERT(thread->sth_state == SMB_THREAD_STATE_EXITED);
thread->sth_magic = 0;
mutex_destroy(&thread->sth_mtx);
cv_destroy(&thread->sth_cv);
}
int
smb_thread_start(
smb_thread_t *sth)
{
kthread_t *t;
struct proc *procp;
smb_server_t *sv = sth->sth_server;
int rc;
ASSERT(sth->sth_magic == SMB_THREAD_MAGIC);
procp = (sv->sv_proc_p != NULL) ?
sv->sv_proc_p : curzone->zone_zsched;
mutex_enter(&sth->sth_mtx);
if (sth->sth_state != SMB_THREAD_STATE_EXITED) {
mutex_exit(&sth->sth_mtx);
return (-1);
}
sth->sth_state = SMB_THREAD_STATE_STARTING;
mutex_exit(&sth->sth_mtx);
#ifdef _KERNEL
if (sth->sth_pri < MINCLSYSPRI) {
t = lwp_kernel_create(procp, smb_thread_entry_point, sth,
TS_RUN, sth->sth_pri);
} else
#endif
{
t = thread_create(NULL, 0, smb_thread_entry_point, sth,
0, procp, TS_RUN, sth->sth_pri);
}
ASSERT(t != NULL);
mutex_enter(&sth->sth_mtx);
sth->sth_th = t;
sth->sth_did = THR_TO_DID(t);
while (sth->sth_state == SMB_THREAD_STATE_STARTING)
cv_wait(&sth->sth_cv, &sth->sth_mtx);
if (sth->sth_state == SMB_THREAD_STATE_RUNNING)
rc = 0;
else
rc = -1;
mutex_exit(&sth->sth_mtx);
return (rc);
}
void
smb_thread_stop(smb_thread_t *thread)
{
ASSERT(thread->sth_magic == SMB_THREAD_MAGIC);
mutex_enter(&thread->sth_mtx);
switch (thread->sth_state) {
case SMB_THREAD_STATE_RUNNING:
case SMB_THREAD_STATE_STARTING:
if (!thread->sth_kill) {
thread->sth_kill = B_TRUE;
cv_broadcast(&thread->sth_cv);
while (thread->sth_state != SMB_THREAD_STATE_EXITING)
cv_wait(&thread->sth_cv, &thread->sth_mtx);
mutex_exit(&thread->sth_mtx);
thread_join(thread->sth_did);
mutex_enter(&thread->sth_mtx);
thread->sth_state = SMB_THREAD_STATE_EXITED;
thread->sth_did = 0;
thread->sth_kill = B_FALSE;
cv_broadcast(&thread->sth_cv);
break;
}
case SMB_THREAD_STATE_EXITING:
if (thread->sth_kill) {
while (thread->sth_state != SMB_THREAD_STATE_EXITED)
cv_wait(&thread->sth_cv, &thread->sth_mtx);
} else {
thread->sth_state = SMB_THREAD_STATE_EXITED;
thread->sth_did = 0;
}
break;
case SMB_THREAD_STATE_EXITED:
break;
default:
ASSERT(0);
break;
}
mutex_exit(&thread->sth_mtx);
}
void
smb_thread_signal(smb_thread_t *thread)
{
ASSERT(thread->sth_magic == SMB_THREAD_MAGIC);
mutex_enter(&thread->sth_mtx);
switch (thread->sth_state) {
case SMB_THREAD_STATE_RUNNING:
cv_signal(&thread->sth_cv);
break;
default:
break;
}
mutex_exit(&thread->sth_mtx);
}
boolean_t
smb_thread_continue(smb_thread_t *thread)
{
boolean_t result;
ASSERT(thread->sth_magic == SMB_THREAD_MAGIC);
mutex_enter(&thread->sth_mtx);
result = smb_thread_continue_timedwait_locked(thread, 0);
mutex_exit(&thread->sth_mtx);
return (result);
}
boolean_t
smb_thread_continue_nowait(smb_thread_t *thread)
{
boolean_t result;
ASSERT(thread->sth_magic == SMB_THREAD_MAGIC);
mutex_enter(&thread->sth_mtx);
result = smb_thread_continue_timedwait_locked(thread, -1);
mutex_exit(&thread->sth_mtx);
return (result);
}
boolean_t
smb_thread_continue_timedwait(smb_thread_t *thread, int seconds)
{
boolean_t result;
ASSERT(thread->sth_magic == SMB_THREAD_MAGIC);
mutex_enter(&thread->sth_mtx);
result = smb_thread_continue_timedwait_locked(thread,
SEC_TO_TICK(seconds));
mutex_exit(&thread->sth_mtx);
return (result);
}
static boolean_t
smb_thread_continue_timedwait_locked(smb_thread_t *thread, int ticks)
{
boolean_t result;
if (ticks != -1 && !thread->sth_kill) {
if (ticks == 0) {
cv_wait(&thread->sth_cv, &thread->sth_mtx);
} else {
(void) cv_reltimedwait(&thread->sth_cv,
&thread->sth_mtx, (clock_t)ticks, TR_CLOCK_TICK);
}
}
result = (thread->sth_kill == 0);
return (result);
}