diff options
Diffstat (limited to 'rtemsbsd/rtems/rtems-bsd-synch.c')
-rw-r--r-- | rtemsbsd/rtems/rtems-bsd-synch.c | 312 |
1 files changed, 312 insertions, 0 deletions
diff --git a/rtemsbsd/rtems/rtems-bsd-synch.c b/rtemsbsd/rtems/rtems-bsd-synch.c new file mode 100644 index 00000000..40e08e1c --- /dev/null +++ b/rtemsbsd/rtems/rtems-bsd-synch.c @@ -0,0 +1,312 @@ +/** + * @file + * + * @ingroup rtems_bsd_rtems + * + * @brief TODO. + */ + +/* + * Copyright (c) 2009, 2010 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * 82178 Puchheim + * Germany + * <rtems@embedded-brains.de> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * FIXME: This seems to be a completely broken implementation. + */ + +#include <freebsd/machine/rtems-bsd-config.h> + +#include <rtems/score/statesimpl.h> +#include <rtems/score/threaddispatch.h> +#include <rtems/score/thread.h> +#include <rtems/score/threadqimpl.h> + +#include <freebsd/sys/param.h> +#include <freebsd/sys/types.h> +#include <freebsd/sys/systm.h> +#include <freebsd/sys/kernel.h> +#include <freebsd/sys/ktr.h> +#include <freebsd/sys/lock.h> +#include <freebsd/sys/mutex.h> +#include <freebsd/sys/proc.h> +#include <freebsd/machine/pcpu.h> + +#define STATES_WAITING_FOR_SLEEP 0x40000 + +static int pause_wchan; + +typedef struct +{ + Chain_Node node; + void *ident; + Thread_queue_Control queue; +}sleep_queue_control_t; + +sleep_queue_control_t sleep_queue[BSD_MAXIMUM_SLEEP_QUEUES]; //this memory allocation could use _Workspace_Allocate once inside RTEMS tree +Chain_Control sleep_queue_inactive_nodes; //chain of inactive nodes +Chain_Control sleep_queue_active_nodes; //chain of active nodes + +void +sleepinit(void) +{ + int ii; + + /* initialize the sleep queue */ + for( ii = 0; ii < BSD_MAXIMUM_SLEEP_QUEUES; ii++ ) + { + sleep_queue[ii].ident = NULL; + /* + * Initialize the queue we use to block for signals + */ + _Thread_queue_Initialize( + &sleep_queue[ii].queue, + THREAD_QUEUE_DISCIPLINE_PRIORITY, + STATES_WAITING_FOR_SLEEP | STATES_INTERRUPTIBLE_BY_SIGNAL, + EAGAIN + ); + } + //initialize active chain + _Chain_Initialize_empty( &sleep_queue_active_nodes ); + //initialize inactive chain + _Chain_Initialize( &sleep_queue_inactive_nodes, sleep_queue, BSD_MAXIMUM_SLEEP_QUEUES, sizeof( sleep_queue_control_t )); +} + +sleep_queue_control_t* +sleep_queue_lookup(void *ident) +{ + int ii; + + /* initialize the sleep queue */ + for( ii = 0; ii < BSD_MAXIMUM_SLEEP_QUEUES; ii++ ) + { + if( sleep_queue[ii].ident == ident ) + { + return &sleep_queue[ii]; + } + } + return NULL; +} + +sleep_queue_control_t* +sleep_queue_get(void *ident) +{ + sleep_queue_control_t *sq; + + sq = sleep_queue_lookup( ident ); + if (sq == NULL) + { + KASSERT(!_Chain_Is_empty( &inactive_nodes ), ("sleep_queue_get")); + //get a control from the inactive chain + sq = ( sleep_queue_control_t * )_Chain_Get( &sleep_queue_inactive_nodes ); + sq->ident = ident; + _Chain_Append( &sleep_queue_active_nodes, &sq->node ); + } + return sq; +} + +/* + * Block the current thread until it is awakened from its sleep queue + * or it times out while waiting. + */ +int +sleep_queue_timedwait(void *wchan, int pri, int timeout, int catch) +{ + sleep_queue_control_t *sq; + Thread_Control *executing; + ISR_Level level; + + _Thread_Disable_dispatch(); + + sq = sleep_queue_get( wchan ); + + executing = _Thread_Executing; + if( timeout ) + { + executing->Wait.return_code = EWOULDBLOCK; + } + else
+ { + executing->Wait.return_code = 0; + } + _ISR_Disable( level ); + _Thread_queue_Enter_critical_section( &sq->queue ); + if( catch ) + { + sq->queue.state |= STATES_INTERRUPTIBLE_BY_SIGNAL; + } + else + { + sq->queue.state &= ~STATES_INTERRUPTIBLE_BY_SIGNAL; + } + executing->Wait.queue = &sq->queue; + _ISR_Enable( level ); + + _Thread_queue_Enqueue( &sq->queue, executing, timeout ); + _Thread_Enable_dispatch(); + return _Thread_Executing->Wait.return_code; +} + +/* + * General sleep call. Suspends the current thread until a wakeup is + * performed on the specified identifier. The thread will then be made + * runnable with the specified priority. Sleeps at most timo/hz seconds + * (0 means no timeout). If pri includes PCATCH flag, signals are checked + * before and after sleeping, else signals are not checked. Returns 0 if + * awakened, EWOULDBLOCK if the timeout expires. If PCATCH is set and a + * signal needs to be delivered, ERESTART is returned if the current system + * call should be restarted if possible, and EINTR is returned if the system + * call should be interrupted by the signal (return EINTR). + * + * The lock argument is unlocked before the caller is suspended, and + * re-locked before _sleep() returns. If priority includes the PDROP + * flag the lock is not re-locked before returning. + */ +int +_sleep(void *ident, struct lock_object *lock, int priority, const char *wmesg, int timo) +{ + struct thread *td; + struct lock_class *class; + int catch, flags, lock_state, pri, rval; + + td = curthread; +#ifdef KTRACE + if (KTRPOINT(td, KTR_CSW)) + ktrcsw(1, 0); +#endif + KASSERT(timo != 0 || mtx_owned(&Giant) || lock != NULL, + ("sleeping without a lock")); + KASSERT(ident != NULL && TD_IS_RUNNING(td), ("msleep")); + if (priority & PDROP) + KASSERT(lock != NULL && lock != &Giant.lock_object, + ("PDROP requires a non-Giant lock")); + if (lock != NULL) + class = LOCK_CLASS(lock); + else + class = NULL; + + if (cold) { + /* + * During autoconfiguration, just return; + * don't run any other threads or panic below, + * in case this is the idle thread and already asleep. + * XXX: this used to do "s = splhigh(); splx(safepri); + * splx(s);" to give interrupts a chance, but there is + * no way to give interrupts a chance now. + */ + if (lock != NULL && priority & PDROP) + class->lc_unlock(lock); + return (0); + } + catch = priority & PCATCH; + pri = priority & PRIMASK; + + if (lock == &Giant.lock_object) + mtx_assert(&Giant, MA_OWNED); + DROP_GIANT(); + if (lock != NULL && lock != &Giant.lock_object && + !(class->lc_flags & LC_SLEEPABLE)) { + lock_state = class->lc_unlock(lock); + } else + /* GCC needs to follow the Yellow Brick Road */ + lock_state = -1; + + if (lock != NULL && class->lc_flags & LC_SLEEPABLE) { + lock_state = class->lc_unlock(lock); + } + + rval = sleep_queue_timedwait(ident, pri, timo, catch); + +#ifdef KTRACE + if (KTRPOINT(td, KTR_CSW)) + ktrcsw(0, 0); +#endif + PICKUP_GIANT(); + if (lock != NULL && lock != &Giant.lock_object && !(priority & PDROP)) { + class->lc_lock(lock, lock_state); + } + return (rval); +} + +/* + * pause() is like tsleep() except that the intention is to not be + * explicitly woken up by another thread. Instead, the current thread + * simply wishes to sleep until the timeout expires. It is + * implemented using a dummy wait channel. + */ +int +pause(const char *wmesg, int timo) +{ + + KASSERT(timo != 0, ("pause: timeout required")); + return (tsleep(&pause_wchan, 0, wmesg, timo)); +} + +/* + * Make all threads sleeping on the specified identifier runnable. + */ +void +wakeup(void *ident) +{ + sleep_queue_control_t *sq; + Thread_Control *the_thread; + + sq = sleep_queue_lookup( ident ); + if (sq == NULL) + { + return (0); + } + + while ( (the_thread = _Thread_queue_Dequeue(&sq->queue)) ) + { + } + return 0; +} + +/* + * Make a thread sleeping on the specified identifier runnable. + * May wake more than one thread if a target thread is currently + * swapped out. + */ +void +wakeup_one(void *ident) +{ + sleep_queue_control_t *sq; + Thread_Control *the_thread; + + sq = sleep_queue_lookup( ident ); + if (sq == NULL) + { + return (0); + } + the_thread = _Thread_queue_Dequeue(&sq->queue); + return 0; + +} + |