/* * 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); }