synch.c
来自「eCos操作系统源码」· C语言 代码 · 共 420 行
C
420 行
//==========================================================================//// ecos/synch.c//// eCos wrapper and synch functions////==========================================================================//####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 <cyg/io/eth/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 0x10static 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#endifstatic inline cyg_uint32spl_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_uint32cyg_splimp(SPLXARGS){ SPLXTRACE; return spl_any( SPL_IMP );}cyg_uint32cyg_splclock(SPLXARGS){ SPLXTRACE; return spl_any( SPL_CLOCK );}cyg_uint32cyg_splnet(SPLXARGS){ SPLXTRACE; return spl_any( SPL_NET );}cyg_uint32cyg_splhigh(SPLXARGS){ SPLXTRACE; // splhigh did SPLSOFTNET in the contrib, so this is the same return spl_any( SPL_SOFTNET );}cyg_uint32cyg_splsoftnet(SPLXARGS){ SPLXTRACE; return spl_any( SPL_SOFTNET );}cyg_uint32cyg_splinternal(SPLXARGS){ SPLXTRACE; return spl_any( SPL_INTERNAL );}//// Return to a previous interrupt state/level.//voidcyg_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 functionsvoidcyg_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 eventvoidcyg_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 256static 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 voiddo_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 voidshow_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
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?