Mercurial > flash_v2
diff packages/net/tcpip/current/src/ecos/support.c @ 110:84e4bde58b26 ecos-sw-2000-07-14
Merge from eCos master repository on 2000-07-14-22:00:02-BST
| author | jlarmour |
|---|---|
| date | Mon, 17 Jul 2000 14:42:27 +0000 |
| parents | 5a0cc6c243a9 |
| children | 02ea4320376c |
line wrap: on
line diff
--- a/packages/net/tcpip/current/src/ecos/support.c +++ b/packages/net/tcpip/current/src/ecos/support.c @@ -102,143 +102,177 @@ cyg_panic(const char *msg, ...) cyg_test_exit(); // FIXME } - -// ------------------------------------------------------------------------ -#define SPLSOFTNET_IS_A_MUTEX 1 -// ------------------------------------------------------------------------ -#ifdef SPLSOFTNET_IS_A_MUTEX +//---------------------------- splx() emulation ------------------------------ +// +// This variable (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. +// +static cyg_uint32 spl_state; +#define SPL_STATE_IMP 0x01 +#define SPL_STATE_NET 0x02 +#define SPL_STATE_CLOCK 0x04 +#define SPL_STATE_SOFTNET 0x08 -#define MUTEX 0x50F750F7 -#define HOLD MUTEX+1 - -static cyg_mutex_t mutex; -static volatile cyg_handle_t owner; +static cyg_mutex_t softnet_mutex; +static volatile cyg_handle_t softnet_thread; #define SPLINIT() CYG_MACRO_START \ - cyg_mutex_init( &mutex ); \ - owner = 0; \ + cyg_mutex_init( &softnet_mutex ); \ + softnet_thread = 0; \ CYG_MACRO_END -// use the scheduler lock for SPLX() and a mutex for softnet - -static inline cyg_uint32 -cyg_splin_softnet(void) +// +// This function is called in order to protect internal data structures +// short-term, primarily so that interrupt processing does not interfere +// with them. +// +// Simply protecting against interrupts (DSRs) should suffice. +// +cyg_uint32 +#ifdef CYGIMPL_TRACE_SPLX +cyg_splimp(const char *file, const int line) +#else +cyg_splimp(void) +#endif { - if ( 0 == cyg_scheduler_read_lock() ) { - cyg_handle_t self = cyg_thread_self(); - if ( self != owner ) { - cyg_mutex_lock( &mutex ); // Perfectly OK for this to wait - owner = self; - return MUTEX; // release when restoring here - } - return HOLD; // do not release when restoring to this value + cyg_uint32 old_ints; + cyg_scheduler_lock(); +#ifdef CYGIMPL_TRACE_SPLX + do_sched_event(__FUNCTION__, file, line, cyg_scheduler_read_lock()); +#endif + old_ints = spl_state; + spl_state |= SPL_STATE_IMP; + if (old_ints & SPL_STATE_IMP) { + // Already at this state/level, no need to retake scheduler lock + cyg_scheduler_unlock(); } - cyg_scheduler_lock(); - return cyg_scheduler_read_lock() - 1; -} - -static inline cyg_uint32 -cyg_splin(void) -{ - cyg_scheduler_lock(); - return cyg_scheduler_read_lock() - 1; + return old_ints; } -static inline void -cyg_splout(cyg_uint32 orig) -{ - if ( MUTEX == orig ) { - CYG_ASSERT( cyg_thread_self() == owner, "Not owner release!" ); - cyg_scheduler_lock(); - owner = 0; - cyg_mutex_unlock( &mutex ); - cyg_scheduler_unlock(); - return; - } - if ( HOLD == orig ) { - CYG_ASSERT( cyg_thread_self() == owner, "Not owner hold!" ); - return; - } - CYG_ASSERT( 0 == (0xffff0000 & orig), "Scary splx value" ); - while ( cyg_scheduler_read_lock() > orig ) - cyg_scheduler_unlock(); -} - -#define SPL_ENTER(x) (x) = cyg_splin() -#define SPL_SOFTNET(x) (x) = cyg_splin_softnet() -#define SPL_EXIT(x) cyg_splout((x)) - +// +// This function 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). +// +// Simply protecting against interrupts (DSRs) should suffice. +// +cyg_uint32 +#ifdef CYGIMPL_TRACE_SPLX +cyg_splclock(const char *file, const int line) #else -#if 1 - -// use the scheduler lock for SPLX() -static inline cyg_uint32 -cyg_splin(void) -{ - cyg_scheduler_lock(); - return cyg_scheduler_read_lock() - 1; -} - -static inline void -cyg_splout(cyg_uint32 orig) -{ - CYG_ASSERT( 0 == (0xffff0000 & orig), "Scary splx value" ); - while ( cyg_scheduler_read_lock() > orig ) - cyg_scheduler_unlock(); -} - -#define SPL_ENTER(x) (x) = cyg_splin() -#define SPL_SOFTNET(x) (x) = cyg_splin() -#define SPL_EXIT(x) cyg_splout((x)) - -#else - -// Old interrupt based version -#define SPL_ENTER(x) HAL_DISABLE_INTERRUPTS((x)) -#define SPL_SOFTNET(x) HAL_DISABLE_INTERRUPTS((x)) -#define SPL_EXIT(x) HAL_RESTORE_INTERRUPTS((x)) - +cyg_splclock(void) #endif -#endif -// ------------------------------------------------------------------------ - -cyg_uint32 -cyg_splimp(void) { cyg_uint32 old_ints; - SPL_ENTER(old_ints); + cyg_scheduler_lock(); +#ifdef CYGIMPL_TRACE_SPLX + do_sched_event(__FUNCTION__, file, line, cyg_scheduler_read_lock()); +#endif + old_ints = spl_state; + spl_state |= SPL_STATE_CLOCK; + if (old_ints & SPL_STATE_CLOCK) { + // Already at this state/level, no need to retake scheduler lock + cyg_scheduler_unlock(); + } return old_ints; } cyg_uint32 +#ifdef CYGIMPL_TRACE_SPLX +cyg_splnet(const char *file, const int line) +#else cyg_splnet(void) +#endif { cyg_uint32 old_ints; - SPL_ENTER(old_ints); + cyg_scheduler_lock(); +#ifdef CYGIMPL_TRACE_SPLX + do_sched_event(__FUNCTION__, file, line, cyg_scheduler_read_lock()); +#endif + old_ints = spl_state; + spl_state |= SPL_STATE_NET; + if (old_ints & SPL_STATE_NET) { + // Already at this state/level, no need to retake scheduler lock + cyg_scheduler_unlock(); + } return old_ints; } +// +// Prevent all other stack processing, including interrupts (DSRs), etc. +// cyg_uint32 -cyg_splclock(void) +#ifdef CYGIMPL_TRACE_SPLX +cyg_splsoftnet(const char *file, const int line) +#else +cyg_splsoftnet(void) +#endif { cyg_uint32 old_ints; - SPL_ENTER(old_ints); + cyg_scheduler_lock(); +#ifdef CYGIMPL_TRACE_SPLX + do_sched_event(__FUNCTION__, file, line, cyg_scheduler_read_lock()); +#endif + if (spl_state & SPL_STATE_SOFTNET) { + if (softnet_thread == cyg_thread_self()) { + // Do nothing + old_ints = spl_state; + spl_state |= SPL_STATE_SOFTNET; + cyg_scheduler_unlock(); + return old_ints; + } + } + cyg_scheduler_unlock(); + cyg_mutex_lock(&softnet_mutex); + cyg_scheduler_lock(); + softnet_thread = cyg_thread_self(); + old_ints = spl_state; + spl_state |= SPL_STATE_SOFTNET; return old_ints; } -cyg_uint32 -cyg_splsoftnet(void) +// +// Return to a previous interrupt state/level. +// +void +#ifdef CYGIMPL_TRACE_SPLX +cyg_splx(cyg_uint32 old_state, const char *file, const int line) +#else +cyg_splx(cyg_uint32 old_state) +#endif { - cyg_uint32 old_ints; - SPL_SOFTNET(old_ints); - return old_ints; + cyg_uint32 new_state = spl_state; + cyg_scheduler_lock(); // Extra security while messing about +#ifdef CYGIMPL_TRACE_SPLX + do_sched_event(__FUNCTION__, file, line, cyg_scheduler_read_lock()); +#endif + if ((spl_state & SPL_STATE_SOFTNET) && !(old_state & SPL_STATE_SOFTNET)) { + new_state &= ~SPL_STATE_SOFTNET; + softnet_thread = 0; + cyg_mutex_unlock(&softnet_mutex); + cyg_scheduler_unlock(); + } + if ((spl_state & SPL_STATE_NET) && !(old_state & SPL_STATE_NET)) { + new_state &= ~SPL_STATE_NET; + cyg_scheduler_unlock(); + } + if ((spl_state & SPL_STATE_CLOCK) && !(old_state & SPL_STATE_CLOCK)) { + new_state &= ~SPL_STATE_CLOCK; + cyg_scheduler_unlock(); + } + if ((spl_state & SPL_STATE_IMP) && !(old_state & SPL_STATE_IMP)) { + new_state &= ~SPL_STATE_IMP; + cyg_scheduler_unlock(); + } + spl_state = new_state; + cyg_scheduler_unlock(); } - -void -cyg_splx(cyg_uint32 old_lev) -{ - SPL_EXIT(old_lev); -} +//---------------------------- splx() emulation ------------------------------ void setsoftnet(void) @@ -405,7 +439,6 @@ cyg_kmem_init(void) mclrefcnt = net_clusters_refcnt; } - void cyg_kmem_print_stats( void ) { cyg_mempool_info info; @@ -557,6 +590,7 @@ ovbcopy(const void *s, void *d, size_t l memcpy(d, s, len); } +//------------------ tsleep() and wakeup() emulation --------------------------- // // Structure used to keep track of 'tsleep' style events // @@ -600,15 +634,13 @@ cyg_tsleep(void *chan, int pri, char *wm int i, res = 0; struct wakeup_event *ev; cyg_tick_count_t sleep_time; -#ifdef SPLSOFTNET_IS_A_MUTEX - int olock; // this does the same as safe_lock() but keeping the old state + int olock; // current state of scheduler lock - so it can be replaced + cyg_handle_t self = cyg_thread_self(); + cyg_scheduler_lock(); // ...around. olock = cyg_scheduler_read_lock(); if ( olock > 1 ) cyg_scheduler_unlock(); -#else - cyg_scheduler_safe_lock(); // Ensure safe scan -#endif for (i = 0, ev = wakeup_list; i < CYGPKG_NET_NUM_WAKEUP_EVENTS; i++, ev++) { if (ev->chan == 0) { @@ -619,40 +651,40 @@ cyg_tsleep(void *chan, int pri, char *wm if (i == CYGPKG_NET_NUM_WAKEUP_EVENTS) { panic("no sleep slots"); } - CYG_ASSERT( 1 == cyg_scheduler_read_lock(), "Sleep won't!" ); + if ( 1 != cyg_scheduler_read_lock()) { + panic("Tsleep - called with scheduler locked\n"); + } + + // Then we must release the 'softnet' mutex when we wait - if we have it + if ( self == softnet_thread ) { + softnet_thread = 0; + spl_state &= ~SPL_STATE_SOFTNET; + cyg_mutex_unlock( &softnet_mutex ); + } else { + self = 0; // Flag no need to reclaim + } -#ifdef SPLSOFTNET_IS_A_MUTEX - { // Then we must release the mutex when we wait - if we have it - cyg_handle_t self = cyg_thread_self(); - if ( self == owner ) { - owner = 0; - cyg_mutex_unlock( &mutex ); - } else { - self = 0; // Flag no need to reclaim + // This part actually does the wait: + cyg_scheduler_unlock(); + if (timo) { + sleep_time = cyg_current_time() + timo; + if (!cyg_semaphore_timed_wait(&ev->sem, sleep_time)) { + res = ETIMEDOUT; + ev->chan = 0; // Free slot } -#endif + } else { + cyg_semaphore_wait(&ev->sem); + } - // This part actually does the wait: + if ( self ) { // return to previous state + cyg_mutex_lock( &softnet_mutex ); // this might wait + cyg_scheduler_lock(); + softnet_thread = self; // got it now... + spl_state |= SPL_STATE_SOFTNET; cyg_scheduler_unlock(); - if (timo) { - sleep_time = cyg_current_time() + timo; - if (!cyg_semaphore_timed_wait(&ev->sem, sleep_time)) { - res = ETIMEDOUT; - ev->chan = 0; // Free slot - } - } else { - cyg_semaphore_wait(&ev->sem); - } - -#ifdef SPLSOFTNET_IS_A_MUTEX - if ( self ) { // return to previous state - cyg_mutex_lock( &mutex ); // this might wait - owner = self; // got it now... - } - if ( olock > 1 ) - cyg_scheduler_lock(); } -#endif + if ( olock > 1 ) + cyg_scheduler_lock(); return res; } @@ -669,6 +701,7 @@ cyg_timeout_init(void) cyg_semaphore_init(&ev->sem, 0); } } +//------------------ tsleep() and wakeup() emulation --------------------------- // // Network software interrupt handler @@ -680,10 +713,11 @@ static void cyg_netint(cyg_addrword_t param) { cyg_flag_value_t curisr; + int s; while (true) { curisr = cyg_flag_wait(&netint_flags, NETISR_ANY, CYG_FLAG_WAITMODE_OR|CYG_FLAG_WAITMODE_CLR); - cyg_scheduler_lock(); // This code should not be preempted + s = splsoftnet(); // Prevent any overlapping "stack" processing #ifdef INET if (curisr & (1 << NETISR_ARP)) { // Pending ARP requests @@ -700,7 +734,7 @@ cyg_netint(cyg_addrword_t param) ip6intr(); } #endif - cyg_scheduler_unlock(); + splx(s); } } @@ -719,6 +753,9 @@ cyg_net_init(void) static int _init = false; cyg_netdevtab_entry_t *t; +#ifdef CYGIMPL_TRACE_SPLX + show_sched_events(); +#endif if (_init) return; cyg_do_net_init(); // Just forces the linking in of the initializer/constructor // Initialize interrupt "flags" @@ -765,3 +802,71 @@ cyg_net_init(void) // Done _init = true; } + +#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; + } +} + +void +_cyg_scheduler_lock(char *file, int line) +{ + cyg_scheduler_lock(); + do_sched_event(__FUNCTION__, file, line, cyg_scheduler_read_lock()); +} + +void +_cyg_scheduler_safe_lock(char *file, int line) +{ + cyg_scheduler_safe_lock(); + do_sched_event(__FUNCTION__, file, line, cyg_scheduler_read_lock()); +} + +void +_cyg_scheduler_unlock(char *file, int line) +{ + cyg_scheduler_unlock(); + do_sched_event(__FUNCTION__, file, line, cyg_scheduler_read_lock()); +} +#endif // CYGIMPL_TRACE_SPLX
