/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include "iscsi_thread.h"
static void iscsi_threads_entry(void *arg);
/*
* iscsi_thread_create - Creates the needed resources to handle a thread
*/
iscsi_thread_t *
iscsi_thread_create(dev_info_t *dip, char *name,
iscsi_thread_ep_t entry_point, void *arg)
{
iscsi_thread_t *thread;
thread = kmem_zalloc(sizeof (iscsi_thread_t), KM_SLEEP);
if (thread != NULL) {
thread->tq = ddi_taskq_create(dip, name, 1,
TASKQ_DEFAULTPRI, 0);
if (thread->tq != NULL) {
thread->signature = SIG_ISCSI_THREAD;
thread->dip = dip;
thread->entry_point = entry_point;
thread->arg = arg;
thread->state = ISCSI_THREAD_STATE_STOPPED;
thread->sign.bitmap = 0;
mutex_init(&thread->mgnt.mtx, NULL, MUTEX_DRIVER, NULL);
mutex_init(&thread->sign.mtx, NULL, MUTEX_DRIVER, NULL);
cv_init(&thread->sign.cdv, NULL, CV_DRIVER, NULL);
} else {
kmem_free(thread, sizeof (iscsi_thread_t));
thread = NULL;
}
}
return (thread);
}
/*
* iscsi_thread_destroy - Releases the needed resources to handle a thread
*/
void
iscsi_thread_destroy(
iscsi_thread_t *thread
)
{
ASSERT(thread != NULL);
ASSERT(thread->signature == SIG_ISCSI_THREAD);
mutex_enter(&thread->mgnt.mtx);
switch (thread->state) {
case ISCSI_THREAD_STATE_STARTED:
/* A kill signal is sent first. */
thread->state = ISCSI_THREAD_STATE_DESTROYING;
mutex_enter(&thread->sign.mtx);
if (!(thread->sign.bitmap & ISCSI_THREAD_SIGNAL_KILL)) {
thread->sign.bitmap |= ISCSI_THREAD_SIGNAL_KILL;
cv_signal(&thread->sign.cdv);
}
mutex_exit(&thread->sign.mtx);
ddi_taskq_wait(thread->tq);
break;
case ISCSI_THREAD_STATE_STOPPED:
/* Switch the state and wait for the thread to exit. */
thread->state = ISCSI_THREAD_STATE_DESTROYING;
break;
default:
ASSERT(0);
break;
}
mutex_exit(&thread->mgnt.mtx);
ddi_taskq_destroy(thread->tq);
cv_destroy(&thread->sign.cdv);
mutex_destroy(&thread->sign.mtx);
mutex_destroy(&thread->mgnt.mtx);
thread->signature = (uint32_t)~SIG_ISCSI_THREAD;
kmem_free(thread, sizeof (iscsi_thread_t));
}
/*
* iscsi_thread_start - Starts the thread given as an entry parameter
*/
boolean_t
iscsi_thread_start(
iscsi_thread_t *thread
)
{
boolean_t ret = B_FALSE;
ASSERT(thread != NULL);
ASSERT(thread->signature == SIG_ISCSI_THREAD);
mutex_enter(&thread->mgnt.mtx);
switch (thread->state) {
case ISCSI_THREAD_STATE_STARTED:
mutex_enter(&thread->sign.mtx);
thread->state = ISCSI_THREAD_STATE_STOPPING;
if (!(thread->sign.bitmap & ISCSI_THREAD_SIGNAL_KILL)) {
thread->sign.bitmap |= ISCSI_THREAD_SIGNAL_KILL;
cv_signal(&thread->sign.cdv);
}
mutex_exit(&thread->sign.mtx);
ddi_taskq_wait(thread->tq);
thread->state = ISCSI_THREAD_STATE_STOPPED;
/* FALLTHRU */
case ISCSI_THREAD_STATE_STOPPED:
thread->sign.bitmap = 0;
thread->state = ISCSI_THREAD_STATE_STARTING;
if (ddi_taskq_dispatch(thread->tq, iscsi_threads_entry,
thread, DDI_SLEEP) == DDI_SUCCESS) {
/*
* The dispatch succeeded.
*/
thread->state = ISCSI_THREAD_STATE_STARTED;
ret = B_TRUE;
}
break;
default:
ASSERT(0);
break;
}
mutex_exit(&thread->mgnt.mtx);
return (ret);
}
/*
* iscsi_thread_stop -
*/
boolean_t
iscsi_thread_stop(
iscsi_thread_t *thread
)
{
boolean_t ret = B_FALSE;
ASSERT(thread != NULL);
ASSERT(thread->signature == SIG_ISCSI_THREAD);
mutex_enter(&thread->mgnt.mtx);
switch (thread->state) {
case ISCSI_THREAD_STATE_STARTED:
mutex_enter(&thread->sign.mtx);
thread->state = ISCSI_THREAD_STATE_STOPPING;
if (!(thread->sign.bitmap & ISCSI_THREAD_SIGNAL_KILL)) {
thread->sign.bitmap |= ISCSI_THREAD_SIGNAL_KILL;
cv_signal(&thread->sign.cdv);
}
mutex_exit(&thread->sign.mtx);
ddi_taskq_wait(thread->tq);
thread->state = ISCSI_THREAD_STATE_STOPPED;
ret = B_TRUE;
break;
case ISCSI_THREAD_STATE_STOPPED:
ret = B_TRUE;
break;
default:
ASSERT(0);
break;
}
mutex_exit(&thread->mgnt.mtx);
return (ret);
}
/*
* iscsi_thread_send_kill -
*/
void
iscsi_thread_send_kill(
iscsi_thread_t *thread
)
{
ASSERT(thread != NULL);
ASSERT(thread->signature == SIG_ISCSI_THREAD);
mutex_enter(&thread->mgnt.mtx);
switch (thread->state) {
case ISCSI_THREAD_STATE_STARTED:
mutex_enter(&thread->sign.mtx);
if (!(thread->sign.bitmap & ISCSI_THREAD_SIGNAL_KILL)) {
thread->sign.bitmap |= ISCSI_THREAD_SIGNAL_KILL;
cv_signal(&thread->sign.cdv);
}
mutex_exit(&thread->sign.mtx);
break;
default:
ASSERT(0);
break;
}
mutex_exit(&thread->mgnt.mtx);
}
/*
* iscsi_thread_send_wakeup -
*/
boolean_t
iscsi_thread_send_wakeup(
iscsi_thread_t *thread
)
{
boolean_t ret = B_FALSE;
ASSERT(thread != NULL);
ASSERT(thread->signature == SIG_ISCSI_THREAD);
mutex_enter(&thread->mgnt.mtx);
switch (thread->state) {
case ISCSI_THREAD_STATE_STARTED:
mutex_enter(&thread->sign.mtx);
if (!(thread->sign.bitmap & ISCSI_THREAD_SIGNAL_WAKEUP)) {
thread->sign.bitmap |= ISCSI_THREAD_SIGNAL_WAKEUP;
cv_signal(&thread->sign.cdv);
}
mutex_exit(&thread->sign.mtx);
ret = B_TRUE;
break;
default:
break;
}
mutex_exit(&thread->mgnt.mtx);
return (ret);
}
/*
* iscsi_thread_check_signals -
*/
uint32_t
iscsi_thread_check_signals(
iscsi_thread_t *thread
)
{
uint32_t bitmap;
ASSERT(thread != NULL);
ASSERT(thread->signature == SIG_ISCSI_THREAD);
/* Acquire the mutex before anychecking. */
mutex_enter(&thread->sign.mtx);
bitmap = thread->sign.bitmap;
mutex_exit(&thread->sign.mtx);
return (bitmap);
}
/*
* iscsi_thread_wait -
*/
int
iscsi_thread_wait(
iscsi_thread_t *thread,
clock_t timeout
)
{
int rtn = 1;
ASSERT(thread != NULL);
ASSERT(thread->signature == SIG_ISCSI_THREAD);
/* Acquire the mutex before anychecking. */
mutex_enter(&thread->sign.mtx);
/* Check the signals. */
if (thread->sign.bitmap & ISCSI_THREAD_SIGNAL_KILL) {
goto signal_kill;
} else if (thread->sign.bitmap & ISCSI_THREAD_SIGNAL_WAKEUP) {
goto signal_wakeup;
} else if (timeout == 0) {
goto iscsi_thread_sleep_exit;
}
if (timeout == -1) {
cv_wait(&thread->sign.cdv, &thread->sign.mtx);
} else {
rtn = cv_reltimedwait(&thread->sign.cdv, &thread->sign.mtx,
timeout, TR_CLOCK_TICK);
}
/* Check the signals. */
if (thread->sign.bitmap & ISCSI_THREAD_SIGNAL_KILL) {
goto signal_kill;
} else if (thread->sign.bitmap & ISCSI_THREAD_SIGNAL_WAKEUP) {
goto signal_wakeup;
}
iscsi_thread_sleep_exit:
mutex_exit(&thread->sign.mtx);
return (rtn);
signal_kill:
mutex_exit(&thread->sign.mtx);
return (0);
signal_wakeup:
thread->sign.bitmap &= ~ISCSI_THREAD_SIGNAL_WAKEUP;
mutex_exit(&thread->sign.mtx);
return (1);
}
/*
* iscsi_threads_entry - Common entry point for all threads
*/
static
void
iscsi_threads_entry(
void *arg
)
{
iscsi_thread_t *thread;
thread = (iscsi_thread_t *)arg;
ASSERT(thread != NULL);
ASSERT(thread->signature == SIG_ISCSI_THREAD);
(thread->entry_point)(thread, thread->arg);
}