Mercurial > ecos-v3_0-branch
diff packages/net/tcpip/current/src/ecos/synch.c @ 121:6bd9d475ed4b ecos-sw-2000-09-04
Merge from eCos master repository on 2000-09-04-20:50:20-BST
| author | jlarmour |
|---|---|
| date | Mon, 04 Sep 2000 20:24:12 +0000 |
| parents | |
| children | e0c0827131d1 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/net/tcpip/current/src/ecos/synch.c @@ -0,0 +1,442 @@ +//========================================================================== +// +// ecos/synch.c +// +// eCos wrapper and synch functions +// +//========================================================================== +//####COPYRIGHTBEGIN#### +// +// ------------------------------------------- +// The contents of this file are subject to the Red Hat eCos Public License +// Version 1.1 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://www.redhat.com/ +// +// Software distributed under the License is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +// License for the specific language governing rights and limitations under +// the License. +// +// The Original Code is eCos - Embedded Configurable Operating System, +// released September 30, 1998. +// +// The Initial Developer of the Original Code is Red Hat. +// Portions created by Red Hat are +// Copyright (C) 1998, 1999, 2000 Red Hat, Inc. +// All Rights Reserved. +// ------------------------------------------- +// +//####COPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD or other sources, +// and are covered by the appropriate copyright disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): gthomas, hmt +// Contributors: gthomas, hmt +// Date: 2000-01-10 +// Purpose: +// Description: +// +// +//####DESCRIPTIONEND#### +// +//========================================================================== + + +// Synch routines, etc., used by network code + +#include <sys/param.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/kernel.h> +#include <sys/domain.h> +#include <sys/protosw.h> +#include <sys/sockio.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <net/if.h> +#include <net/route.h> +#include <net/netisr.h> +#include <netinet/in.h> +#include <netinet/in_var.h> +#include <arpa/inet.h> + +#include <machine/cpu.h> + +#include <pkgconf/net.h> + +#include <cyg/infra/diag.h> +#include <cyg/hal/hal_intr.h> +#include <cyg/kernel/kapi.h> + +#include <cyg/infra/cyg_ass.h> + +#include <netdev.h> + +//---------------------------- splx() emulation ------------------------------ +// This contains both the SPLX stuff and tsleep/wakeup - because those must +// be SPLX aware. They release the SPLX lock when sleeping, and reclaim it +// (if needs be) at wakeup. +// +// The variable spl_state (and the associated bit patterns) is used to keep +// track of the "splx()" level. This is an artifact of the original stack, +// based on the BSD interrupt world (interrupts and processing could be +// masked based on a level value, supported by hardware). This is not very +// real-time, so the emulation uses proper eCos tools and techniques to +// accomplish the same result. The key here is in the analysis of the +// various "levels", why they are used, etc. +// +// SPL_IMP is called in order to protect internal data structures +// short-term, primarily so that interrupt processing does not interfere +// with them. +// +// SPL_CLOCK is called in order to ensure that a timestamp is valid i.e. no +// time passes while the stamp is being taken (since it is a potentially +// non-idempotent data structure). +// +// SPL_SOFTNET is used to prevent all other stack processing, including +// interrupts (DSRs), etc. +// +// SPL_INTERNAL is used when running the pseudo-DSR in timeout.c - this +// runs what should really be the network interface device's DSR, and any +// timeout routines that are scheduled. (They are broken out into a thread +// to isolate the network locking from the rest of the system) +// +// NB a thread in thi state can tsleep(); see below. Tsleep releases and +// reclaims the locks and so on. This necessary because of the possible +// conflict where +// I splsoft +// I tsleep +// He runs, he is lower priority +// He splsofts +// He or something else awakens me +// I want to run, but he has splsoft, so I wait +// He runs and releases splsoft +// I awaken and go. + +static volatile cyg_uint32 spl_state = 0; +#define SPL_IMP 0x01 +#define SPL_NET 0x02 +#define SPL_CLOCK 0x04 +#define SPL_SOFTNET 0x08 +#define SPL_INTERNAL 0x10 + +static cyg_mutex_t splx_mutex; +static volatile cyg_handle_t splx_thread; + + +#ifdef CYGIMPL_TRACE_SPLX +#define SPLXARGS const char *file, const int line +#define SPLXMOREARGS , const char *file, const int line +#define SPLXTRACE do_sched_event(__FUNCTION__, file, line, spl_state) +#else +#define SPLXARGS void +#define SPLXMOREARGS +#define SPLXTRACE +#endif + + +static inline cyg_uint32 +spl_any( cyg_uint32 which ) +{ + cyg_uint32 old_spl = spl_state; + if ( cyg_thread_self() != splx_thread ) { + cyg_mutex_lock( &splx_mutex ); + old_spl = 0; // Free when we unlock this context + CYG_ASSERT( 0 == splx_thread, "Thread still owned" ); + CYG_ASSERT( 0 == spl_state, "spl still set" ); + splx_thread = cyg_thread_self(); + } + CYG_ASSERT( splx_mutex.locked, "spl_any: mutex not locked" ); + CYG_ASSERT( (cyg_handle_t)splx_mutex.owner == cyg_thread_self(), + "spl_any: mutex not mine" ); + spl_state |= which; + return old_spl; +} + + +cyg_uint32 +cyg_splimp(SPLXARGS) +{ + SPLXTRACE; + return spl_any( SPL_IMP ); +} + +cyg_uint32 +cyg_splclock(SPLXARGS) +{ + SPLXTRACE; + return spl_any( SPL_CLOCK ); +} + +cyg_uint32 +cyg_splnet(SPLXARGS) +{ + SPLXTRACE; + return spl_any( SPL_NET ); +} + +cyg_uint32 +cyg_splhigh(SPLXARGS) +{ + SPLXTRACE; + // splhigh did SPLSOFTNET in the contrib, so this is the same + return spl_any( SPL_SOFTNET ); +} + +cyg_uint32 +cyg_splsoftnet(SPLXARGS) +{ + SPLXTRACE; + return spl_any( SPL_SOFTNET ); +} + +cyg_uint32 +cyg_splinternal(SPLXARGS) +{ + SPLXTRACE; + return spl_any( SPL_INTERNAL ); +} + + +// +// Return to a previous interrupt state/level. +// +void +cyg_splx(cyg_uint32 old_state SPLXMOREARGS) +{ + SPLXTRACE; + + CYG_ASSERT( 0 != spl_state, "No state set" ); + CYG_ASSERT( splx_mutex.locked, "splx: mutex not locked" ); + CYG_ASSERT( (cyg_handle_t)splx_mutex.owner == cyg_thread_self(), + "splx: mutex not mine" ); + + spl_state &= old_state; + + if ( 0 == spl_state ) { + splx_thread = 0; + cyg_mutex_unlock( &splx_mutex ); + } +} + +//------------------ tsleep() and wakeup() emulation --------------------------- +// +// Structure used to keep track of 'tsleep' style events +// +struct wakeup_event { + void *chan; + cyg_sem_t sem; +}; +static struct wakeup_event wakeup_list[CYGPKG_NET_NUM_WAKEUP_EVENTS]; + + +// Called to initialize structures used by timeout functions +void +cyg_tsleep_init(void) +{ + int i; + struct wakeup_event *ev; + // Create list of "wakeup event" semaphores + for (i = 0, ev = wakeup_list; i < CYGPKG_NET_NUM_WAKEUP_EVENTS; i++, ev++) { + ev->chan = 0; + cyg_semaphore_init(&ev->sem, 0); + } + // Initialize the mutex and thread id: + cyg_mutex_init( &splx_mutex ); + splx_thread = 0; +} + + +// +// Signal an event +void +cyg_wakeup(void *chan) +{ + int i; + struct wakeup_event *ev; + cyg_scheduler_lock(); // Ensure scan is safe + // NB this is broadcast semantics because a sleeper/wakee holds the + // slot until they exit. This avoids a race condition whereby the + // semaphore can get an extra post - and then the slot is freed, so the + // sem wait returns immediately, AOK, so the slot wasn't freed. + for (i = 0, ev = wakeup_list; i < CYGPKG_NET_NUM_WAKEUP_EVENTS; i++, ev++) + if (ev->chan == chan) + cyg_semaphore_post(&ev->sem); + + cyg_scheduler_unlock(); +} + +// ------------------------------------------------------------------------ +// Wait for an event with timeout +// tsleep(event, priority, state, timeout) +// event - the thing to wait for +// priority - unused +// state - a descriptive message +// timeout - max time (in ticks) to wait +// returns: +// 0 - event was "signalled" +// ETIMEDOUT - timeout occurred +// EINTR - thread broken out of sleep +// +int +cyg_tsleep(void *chan, int pri, char *wmesg, int timo) +{ + int i, res = 0; + struct wakeup_event *ev; + cyg_tick_count_t sleep_time; + cyg_handle_t self = cyg_thread_self(); + int old_splflags = 0; // no flags held + + cyg_scheduler_lock(); + + // Safely find a free slot: + for (i = 0, ev = wakeup_list; i < CYGPKG_NET_NUM_WAKEUP_EVENTS; i++, ev++) { + if (ev->chan == 0) { + ev->chan = chan; + break; + } + } + CYG_ASSERT( i < CYGPKG_NET_NUM_WAKEUP_EVENTS, "no sleep slots" ); + CYG_ASSERT( 1 == cyg_scheduler_read_lock(), + "Tsleep - called with scheduler locked" ); + // Defensive: + if ( i >= CYGPKG_NET_NUM_WAKEUP_EVENTS ) { + cyg_scheduler_unlock(); + return ETIMEDOUT; + } + + // If we are the owner, then we must release the mutex when + // we wait. + if ( self == splx_thread ) { + old_splflags = spl_state; // Keep them for restoration + CYG_ASSERT( spl_state, "spl_state not set" ); + // Also want to assert that the mutex is locked... + CYG_ASSERT( splx_mutex.locked, "Splx mutex not locked" ); + CYG_ASSERT( (cyg_handle_t)splx_mutex.owner == self, "Splx mutex not mine" ); + splx_thread = 0; + spl_state = 0; + cyg_mutex_unlock( &splx_mutex ); + } + + // Re-initialize the semaphore - it might have counted up arbitrarily + // in the time between a prior sleeper being signalled and them + // actually running. + cyg_semaphore_init(&ev->sem, 0); + + // This part actually does the wait: + // As of the new kernel, we can do this without unlocking the scheduler + if (timo) { + sleep_time = cyg_current_time() + timo; + if (!cyg_semaphore_timed_wait(&ev->sem, sleep_time)) { + if( cyg_current_time() >= sleep_time ) + res = ETIMEDOUT; + else + res = EINTR; + } + } else { + if (!cyg_semaphore_wait(&ev->sem) ) { + res = EINTR; + } + } + + ev->chan = 0; // Free the slot - the wakeup call cannot do this. + + if ( old_splflags ) { // restore to previous state + // As of the new kernel, we can do this with the scheduler locked + cyg_mutex_lock( &splx_mutex ); // this might wait + CYG_ASSERT( 0 == splx_thread, "Splx thread set in tsleep" ); + CYG_ASSERT( 0 == spl_state, "spl_state set in tsleep" ); + splx_thread = self; // got it now... + spl_state = old_splflags; + } + + cyg_scheduler_unlock(); + return res; +} + + + +// ------------------------------------------------------------------------ +// DEBUGGING ROUTINES +#ifdef CYGIMPL_TRACE_SPLX +#undef cyg_scheduler_lock +#undef cyg_scheduler_safe_lock +#undef cyg_scheduler_unlock + +#define MAX_SCHED_EVENTS 256 +static struct _sched_event { + char *fun, *file; + int line, lock; +} sched_event[MAX_SCHED_EVENTS]; +static int next_sched_event = 0; +static int total_sched_events = 0; + +static void +do_sched_event(char *fun, char *file, int line, int lock) +{ + struct _sched_event *se = &sched_event[next_sched_event]; + if (++next_sched_event == MAX_SCHED_EVENTS) { + next_sched_event = 0; + } + se->fun = fun; + se->file = file; + se->line = line; + se->lock = lock; + total_sched_events++; +} + +static void +show_sched_events(void) +{ + int i; + struct _sched_event *se; + if (total_sched_events < MAX_SCHED_EVENTS) { + i = 0; + } else { + i = next_sched_event + 1; + if (i == MAX_SCHED_EVENTS) i = 0; + } + diag_printf("%d total scheduler events\n", total_sched_events); + while (i != next_sched_event) { + se = &sched_event[i]; + diag_printf("%s - lock: %d, called from %s.%d\n", se->fun, se->lock, se->file, se->line); + if (++i == MAX_SCHED_EVENTS) i = 0; + } +} + +#define SPLX_TRACE_DATA() cyg_scheduler_read_lock() + +void +_cyg_scheduler_lock(char *file, int line) +{ + cyg_scheduler_lock(); + do_sched_event(__FUNCTION__, file, line, SPLX_TRACE_DATA()); +} + +void +_cyg_scheduler_safe_lock(char *file, int line) +{ + cyg_scheduler_safe_lock(); + do_sched_event(__FUNCTION__, file, line, SPLX_TRACE_DATA()); +} + +void +_cyg_scheduler_unlock(char *file, int line) +{ + cyg_scheduler_unlock(); + do_sched_event(__FUNCTION__, file, line, SPLX_TRACE_DATA()); +} +#endif // CYGIMPL_TRACE_SPLX + +// EOF synch.c
