summaryrefslogtreecommitdiffstats
path: root/pk/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'pk/kernel')
-rw-r--r--pk/kernel/pk_api.h111
-rw-r--r--pk/kernel/pk_bh_core.c29
-rw-r--r--pk/kernel/pk_init.c39
-rw-r--r--pk/kernel/pk_kernel.h36
-rw-r--r--pk/kernel/pk_semaphore_core.c22
-rw-r--r--pk/kernel/pk_thread.h56
-rw-r--r--pk/kernel/pk_thread_core.c424
-rw-r--r--pk/kernel/pk_thread_init.c9
-rw-r--r--pk/kernel/pk_thread_util.c291
-rw-r--r--pk/kernel/pk_timer_core.c85
-rw-r--r--pk/kernel/pk_timer_init.c91
-rw-r--r--pk/kernel/pkkernelfiles.mk4
12 files changed, 559 insertions, 638 deletions
diff --git a/pk/kernel/pk_api.h b/pk/kernel/pk_api.h
index 3d0c4a3f..7e1c9702 100644
--- a/pk/kernel/pk_api.h
+++ b/pk/kernel/pk_api.h
@@ -236,7 +236,7 @@
///
/// 2 : (\b Default - Currently Unimplemented) In addition to prepatterning,
/// stack utilization is computed at the exit of context switches and
-/// noncritical interrupt processing. The maximum utilization is stored in
+/// interrupt processing. The maximum utilization is stored in
/// the thread data structure. The kernel will panic if stack overflow is
/// detected. Stack utilization is not computed for the idle thread.
@@ -263,7 +263,7 @@
/// pk_app_cfg.h.
///
/// The PK_START_THREADS_HOOK runs as a pseudo-interrupt handler on the
-/// noncritical interrupt stack, with noncritical interrupts disabled.
+/// kernel stack, with external interrupts disabled.
#ifndef PK_START_THREADS_HOOK
#define PK_START_THREADS_HOOK do {} while (0)
@@ -363,32 +363,11 @@
//Kernel trace macros
#if !PK_KERNEL_TRACE_ENABLE
-
-#define PK_TRACE_THREAD_SLEEP(priority)
-#define PK_TRACE_THREAD_WAKEUP(priority)
-#define PK_TRACE_THREAD_SEMAPHORE_PEND(priority)
-#define PK_TRACE_THREAD_SEMAPHORE_POST(priority)
-#define PK_TRACE_THREAD_SEMAPHORE_TIMEOUT(priority)
-#define PK_TRACE_THREAD_SUSPENDED(priority)
-#define PK_TRACE_THREAD_DELETED(priority)
-#define PK_TRACE_THREAD_COMPLETED(priority)
-#define PK_TRACE_THREAD_MAPPED_RUNNABLE(priority)
-#define PK_TRACE_THREAD_MAPPED_SEMAPHORE_PEND(priority)
-#define PK_TRACE_THREAD_MAPPED_SLEEPING(priority)
-
+#define PK_KERN_TRACE(...)
+#define PK_KERN_TRACE_ASM16(...)
#else
-
-#define PK_TRACE_THREAD_SLEEP(priority) PKTRACE("THREAD_SLEEP(%d)", priority)
-#define PK_TRACE_THREAD_WAKEUP(priority) PKTRACE("THREAD_WAKEUP(%d)", priority)
-#define PK_TRACE_THREAD_SEMAPHORE_PEND(priority) PKTRACE("SEMAPHORE_PEND(%d)", priority)
-#define PK_TRACE_THREAD_SEMAPHORE_POST(priority) PKTRACE("SEMAPHORE_POST(%d)", priority)
-#define PK_TRACE_THREAD_SEMAPHORE_TIMEOUT(priority) PKTRACE("SEMAPHORE_TIMEOUT(%d)", priority)
-#define PK_TRACE_THREAD_SUSPENDED(priority) PKTRACE("THREAD_SUSPENDED(%d)", priority)
-#define PK_TRACE_THREAD_DELETED(priority) PKTRACE("THREAD_DELETED(%d)", priority)
-#define PK_TRACE_THREAD_COMPLETED(priority) PKTRACE("THREAD_COMPLETED(%d)", priority)
-#define PK_TRACE_THREAD_MAPPED_RUNNABLE(priority) PKTRACE("THREAD_MAPPED_RUNNABLE(%d)", priority)
-#define PK_TRACE_THREAD_MAPPED_SEMAPHORE_PEND(priority) PKTRACE("THREAD_MAPPED_SEMAPHORE_PEND(%d)", priority)
-#define PK_TRACE_THREAD_MAPPED_SLEEPING(priority) PKTRACE("THREAD_MAPPED_SLEEPING(%d)", priority)
+#define PK_KERN_TRACE(...) PK_TRACE(__VA_ARGS__)
+#define PK_KERN_TRACE_ASM16(...) PK_TRACE_ASM16(__VA_ARGS__)
#endif /* PK_KERNEL_TRACE_ENABLE */
@@ -486,7 +465,7 @@ typedef struct {
} PkSemaphore;
-/// Compile-time initialize an PkSemaphore structure
+/// Compile-time initialize a PkSemaphore structure
///
/// This low-level macro creates a structure initializatin of an PkSemaphore
/// structure. This can be used for example to create compile-time initialized
@@ -603,23 +582,8 @@ typedef struct PkTimer {
/// field is initialized to a pointer to the thread.
void *arg;
- /// Options for timer processing; See \ref pk_timer_options
- uint8_t options;
-
} PkTimer;
-/// \defgroup pk_timer_options PK Timer Options
-/// @{
-
-/// Allow interrupt preemption during the callback
-///
-/// This is the normal mode for PkTimer objects scheduled by PK kernal
-/// mechanisms. The timer callbacks effectively run as if inside a
-/// highest-priority thread, allowing other interrupts to preempt them.
-#define PK_TIMER_CALLBACK_PREEMPTIBLE 0x1
-
-/// @}
-
// Threads
@@ -663,11 +627,33 @@ typedef struct {
} PkThread;
+typedef void (*PkBhHandler)(void *);
+
+#define PK_BH_HANDLER(handler) void handler(void *)
+
+typedef struct {
+
+ /// The bottom half queue management pointers
+ ///
+ /// This pointer container is defined as the first element of the
+ /// structure to allow the PkBottomHalf to be cast to a PkDeque and
+ /// vice-versa.
+ PkDeque deque;
+
+ /// The bottom half handler
+ PkBhHandler bh_handler;
+
+ /// Private data passed to the handler.
+ void *arg;
+
+} PkBottomHalf;
+
+
// Initialization APIs
int
-pk_initialize(PkAddress noncritical_stack,
- size_t noncritical_stack_size,
+pk_initialize(PkAddress kernel_stack,
+ size_t kernel_stack_size,
PkTimebase initial_timebase,
uint32_t timebase_frequency_hz);
@@ -677,13 +663,6 @@ pk_initialize(PkAddress noncritical_stack,
PkTimebase
pk_timebase_get(void);
-// Interrupt preemption APIs
-
-int
-pk_interrupt_preemption_enable(void);
-
-int
-pk_interrupt_preemption_disable(void);
// Timer APIs
@@ -692,10 +671,6 @@ pk_timer_create(PkTimer *timer,
PkTimerCallback callback,
void *arg);
-int
-pk_timer_create_nonpreemptible(PkTimer *timer,
- PkTimerCallback callback,
- void *arg);
int
pk_timer_schedule(PkTimer *timer,
@@ -735,9 +710,6 @@ int
pk_complete(void);
int
-pk_sleep_absolute(PkTimebase time);
-
-int
pk_sleep(PkInterval interval);
int
@@ -927,6 +899,27 @@ pk_deque_delete(PkDeque *element)
element->next = 0;
}
+// Bottom Half APIs
+
+extern PkDeque _pk_bh_queue;
+
+static inline void
+pk_bh_schedule(PkBottomHalf *bottom_half)
+{
+ pk_deque_push_back(&_pk_bh_queue, (PkDeque *)bottom_half);
+}
+
+#define PK_BH_INIT(_handler, _arg) \
+{\
+ .deque = PK_DEQUE_ELEMENT_INIT(), \
+ .bh_handler = _handler, \
+ .arg = _arg \
+}
+
+#define PK_BH_STATIC_CREATE(bh_name, handler, arg) \
+PkBottomHalf bh_name = PK_BH_INIT(handler, arg)
+
+
//Trace function prototypes
void pk_trace_tiny(uint32_t i_parm);
void pk_trace_big(uint32_t i_hash_and_count,
diff --git a/pk/kernel/pk_bh_core.c b/pk/kernel/pk_bh_core.c
new file mode 100644
index 00000000..8a6181cb
--- /dev/null
+++ b/pk/kernel/pk_bh_core.c
@@ -0,0 +1,29 @@
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2015
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file pk_bh_core.c
+/// \brief PK bottom half APIs
+///
+/// The entry points in this file are considered 'core' routines that will
+/// always be present at runtime in any PK application.
+
+#include "pk.h"
+
+/// Statically initialize the bottom half queue
+PK_DEQUE_SENTINEL_STATIC_CREATE(_pk_bh_queue);
+
+void _pk_process_bh(void)
+{
+ PkBottomHalf *bh;
+ while((bh = (PkBottomHalf*)pk_deque_pop_front(&_pk_bh_queue)) != 0)
+ {
+ bh->bh_handler(bh->arg);
+ }
+ return;
+}
+
+
+#undef __PK_THREAD_CORE_C__
diff --git a/pk/kernel/pk_init.c b/pk/kernel/pk_init.c
index 0add4214..4c7cd138 100644
--- a/pk/kernel/pk_init.c
+++ b/pk/kernel/pk_init.c
@@ -58,17 +58,12 @@ void pk_set_timebase_rshift(uint32_t timebase_freq_hz)
/// Initialize PK.
///
-/// \param noncritical_stack A stack area for noncritical interrupt handlers.
+/// \param kernel_stack A stack area for interrupt and bottom-half handlers.
///
-/// \param noncritical_stack_size The size (in bytes) of the stack area for
-/// noncritical interrupt handlers.
+/// \param kernel_stack_size The size (in bytes) of the stack area for
+/// interrupt and bottom-half handlers.
///
-/// \param critical_stack A stack area for critical interrupt handlers.
-///
-/// \param critical_stack_size The size (in bytes) of the stack area for
-/// critical interrupt handlers.
-///
-/// \param initial_timebase The initial value of the PK timebase. If this
+/// \param initial_timebase The initial value of the PK timebase.
/// argument is given as the special value \c PK_TIMEBASE_CONTINUE, then the
/// timebase is not reset.
///
@@ -93,16 +88,16 @@ void pk_set_timebase_rshift(uint32_t timebase_freq_hz)
// reset everything at initialization.
int
-pk_initialize(PkAddress noncritical_stack,
- size_t noncritical_stack_size,
+pk_initialize(PkAddress kernel_stack,
+ size_t kernel_stack_size,
PkTimebase initial_timebase,
uint32_t timebase_frequency_hz)
{
int rc;
if (PK_ERROR_CHECK_API) {
- PK_ERROR_IF((noncritical_stack == 0) ||
- (noncritical_stack_size == 0),
+ PK_ERROR_IF((kernel_stack == 0) ||
+ (kernel_stack_size == 0),
PK_INVALID_ARGUMENT_INIT);
}
@@ -110,13 +105,13 @@ pk_initialize(PkAddress noncritical_stack,
__pk_thread_machine_context_default = PK_THREAD_MACHINE_CONTEXT_DEFAULT;
- rc = __pk_stack_init(&noncritical_stack, &noncritical_stack_size);
+ rc = __pk_stack_init(&kernel_stack, &kernel_stack_size);
if (rc) {
return rc;
}
- __pk_noncritical_stack = noncritical_stack;
- __pk_noncritical_stack_size = noncritical_stack_size;
+ __pk_kernel_stack = kernel_stack;
+ __pk_kernel_stack_size = kernel_stack_size;
#if PK_TIMER_SUPPORT
@@ -131,19 +126,21 @@ pk_initialize(PkAddress noncritical_stack,
extern PkTimer g_pk_trace_timer;
extern PkTraceBuffer g_pk_trace_buf;
- // Schedule the timer that puts a 64bit timestamp in the trace buffer
- // periodically. This allows us to use 32bit timestamps.
- pk_timer_schedule(&g_pk_trace_timer,
- PK_TRACE_TIMER_PERIOD);
-
//set the trace timebase HZ
g_pk_trace_buf.hz = timebase_frequency_hz;
+ //set the shift adjustment to get us closer to the true
+ //timebase frequency (versus what was hardcoded)
pk_set_timebase_rshift(timebase_frequency_hz);
//set the timebase ajdustment for trace synchronization
pk_trace_set_timebase(initial_timebase);
+ // Schedule the timer that puts a 64bit timestamp in the trace buffer
+ // periodically. This allows us to use 32bit timestamps.
+ pk_timer_schedule(&g_pk_trace_timer,
+ PK_TRACE_TIMER_PERIOD);
+
#endif /* PK_TRACE_SUPPORT */
#endif /* PK_TIMER_SUPPORT */
diff --git a/pk/kernel/pk_kernel.h b/pk/kernel/pk_kernel.h
index 7e88b828..85b028a2 100644
--- a/pk/kernel/pk_kernel.h
+++ b/pk/kernel/pk_kernel.h
@@ -33,33 +33,19 @@
#ifndef __ASSEMBLER__
-/// This is the stack pointer saved when switching from a thread or
-/// non-critical interrupt context to a full-mode critical interrupt context.
-
-UNLESS__PK_CORE_C__(extern)
-volatile
-PkAddress __pk_saved_sp_critical;
-
-/// The critical interrupt stack; constant once defined by the call of
-/// pk_initialize().
-
-UNLESS__PK_CORE_C__(extern)
-volatile
-PkAddress __pk_critical_stack;
-
-/// This is the stack pointer saved when switching from a thread context to a
-/// full-mode non-critical interrupt context.
+/// This is the stack pointer saved when switching from a thread context to an
+/// interrupt context.
UNLESS__PK_CORE_C__(extern)
volatile
-PkAddress __pk_saved_sp_noncritical;
+PkAddress __pk_saved_sp;
-/// The non-critical interrupt stack; constant once defined by the call of
+/// The kernel stack; constant once defined by the call of
/// pk_initialize().
UNLESS__PK_CORE_C__(extern)
volatile
-PkAddress __pk_noncritical_stack;
+PkAddress __pk_kernel_stack;
/// This is the run queue - the queue of mapped runnable tasks.
UNLESS__PK_CORE_C__(extern)
@@ -68,7 +54,7 @@ PkThreadQueue __pk_run_queue;
/// This flag is set by \c __pk_schedule() if a new highest-priority thread
/// becomes runnable during an interrupt handler. The context switch will
-/// take place at the end of non-critical interrupt processing, and the
+/// take place at the end of interrupt processing, and the
/// interrupt handling code will clear the flag.
UNLESS__PK_CORE_C__(extern)
@@ -146,17 +132,11 @@ volatile
PkMachineContext __pk_thread_machine_context_default;
-/// The size of the noncritical stack (bytes).
-
-UNLESS__PK_CORE_C__(extern)
-volatile
-size_t __pk_noncritical_stack_size;
-
-/// The size of the critical stack (bytes).
+/// The size of the kernel stack (bytes).
UNLESS__PK_CORE_C__(extern)
volatile
-size_t __pk_critical_stack_size;
+size_t __pk_kernel_stack_size;
/// This table maps priorities to threads, and contains PK_THREADS + 1
/// entries. The final entry is for the idle thread and will always be null
diff --git a/pk/kernel/pk_semaphore_core.c b/pk/kernel/pk_semaphore_core.c
index d5e6d30c..0e1e34d4 100644
--- a/pk/kernel/pk_semaphore_core.c
+++ b/pk/kernel/pk_semaphore_core.c
@@ -30,9 +30,6 @@
///
/// \retval 0 Successful completion
///
-/// \retval -PK_ILLEGAL_CONTEXT The API was called from a critical interrupt
-/// context.
-///
/// \retval -PK_INVALID_SEMAPHORE_AT_POST The \a semaphore is a null (0) pointer.
///
/// \retval -PK_SEMAPHORE_OVERFLOW The \a max_count argument supplied when
@@ -58,7 +55,7 @@ pk_semaphore_post(PkSemaphore *semaphore)
__pk_thread_queue_delete(&(semaphore->pending_threads), priority);
__pk_thread_queue_insert(&__pk_run_queue, priority);
- PK_TRACE_THREAD_SEMAPHORE_POST(priority);
+ PK_KERN_TRACE("THREAD_SEMAPHORE_POST(%d)", priority);
__pk_schedule();
@@ -127,9 +124,6 @@ pk_semaphore_post(PkSemaphore *semaphore)
///
/// The following return codes are error codes:
///
-/// \retval -PK_ILLEGAL_CONTEXT The API was called from a critical interrupt
-/// context.
-///
/// \retval -PK_INVALID_SEMAPHORE_AT_PEND The \a semaphore is a null (0)
/// pointer.
///
@@ -181,7 +175,7 @@ pk_semaphore_pend(PkSemaphore *semaphore,
thread->semaphore = semaphore;
thread->flags |= PK_THREAD_FLAG_SEMAPHORE_PEND;
- PK_TRACE_THREAD_SEMAPHORE_PEND(priority);
+ PK_KERN_TRACE("THREAD_SEMAPHORE_PEND(%d)", priority);
if (timeout != PK_WAIT_FOREVER) {
timer = &(thread->timer);
@@ -231,9 +225,6 @@ pk_semaphore_pend(PkSemaphore *semaphore,
///
/// \retval 0 Successful completion
///
-/// \retval -PK_ILLEGAL_CONTEXT The API was called from a critical interrupt
-/// context.
-///
/// \retval -PK_INVALID_SEMAPHORE_AT_RELEASE The \a semaphore is a null (0)
/// pointer.
@@ -271,9 +262,7 @@ pk_semaphore_release_all(PkSemaphore* semaphore)
/// parameter to the null pointer (0) if this information is not required.
///
/// The information returned by this API can only be guaranteed consistent if
-/// the API is called from a critical section. Since the
-/// implementation of this API does not require a critical section, it is not
-/// an error to call this API from a critical interrupt context.
+/// the API is called from a critical section.
///
/// Return values other than PK_OK (0) are errors; see \ref pk_errors
///
@@ -306,7 +295,7 @@ pk_semaphore_info_get(PkSemaphore* semaphore,
/// An simple interrupt handler that posts to a semaphore.
///
/// To implement basic event-driven blocking of a thread, install
-/// pk_semaphore_post_handler() as the handler for a non-critical interrupt
+/// pk_semaphore_post_handler() as the handler for an interrupt
/// and provide a pointer to the semaphore as the \a arg argument in
/// pk_irq_handler_set(). The semaphore should be initialized with
/// pk_semaphore_create(&sem, 0, 1). This handler simply disables (masks)
@@ -319,12 +308,11 @@ pk_semaphore_info_get(PkSemaphore* semaphore,
/// condition in the device before re-enabling the interrupt.
#if 0
void
-pk_semaphore_post_handler_full(void *arg, PkIrqId irq, int priority)
+pk_semaphore_post_handler(void *arg, PkIrqId irq, int priority)
{
pk_irq_disable(irq);
pk_irq_status_clear(irq);
pk_semaphore_post((PkSemaphore *)arg);
}
-PK_IRQ_FAST2FULL(pk_semaphore_post_handler, pk_semaphore_post_handler_full);
#endif
diff --git a/pk/kernel/pk_thread.h b/pk/kernel/pk_thread.h
new file mode 100644
index 00000000..acc32525
--- /dev/null
+++ b/pk/kernel/pk_thread.h
@@ -0,0 +1,56 @@
+#ifndef __PK_THREAD_H__
+#define __PK_THREAD_H__
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2015
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file pk_thread.h
+/// \brief Contains private declarations and definitions needed for threads
+///
+
+void
+__pk_thread_map(PkThread* thread);
+
+void
+__pk_thread_unmap(PkThread *thread);
+
+
+// Interrupts must be disabled at entry.
+
+static inline int
+__pk_thread_is_active(PkThread *thread)
+{
+ return ((thread->state != PK_THREAD_STATE_COMPLETED) &&
+ (thread->state != PK_THREAD_STATE_DELETED));
+}
+
+
+// Interrupts must be disabled at entry.
+
+static inline int
+__pk_thread_is_mapped(PkThread *thread)
+{
+ return (thread->state == PK_THREAD_STATE_MAPPED);
+}
+
+
+// Interrupts must be disabled at entry. This is only called on mapped threads.
+
+static inline int
+__pk_thread_is_runnable(PkThread *thread)
+{
+ return __pk_thread_queue_member(&__pk_run_queue, thread->priority);
+}
+
+
+// Interrupts must be disabled at entry.
+
+static inline PkThread*
+__pk_thread_at_priority(PkThreadPriority priority)
+{
+ return (PkThread*)__pk_priority_map[priority];
+}
+
+#endif /* __PK_THREAD_H__ */
diff --git a/pk/kernel/pk_thread_core.c b/pk/kernel/pk_thread_core.c
index 56e083d4..2966eb9b 100644
--- a/pk/kernel/pk_thread_core.c
+++ b/pk/kernel/pk_thread_core.c
@@ -11,52 +11,12 @@
/// always be present at runtime in any PK application that enables threads.
#include "pk.h"
+#include "pk_thread.h"
#define __PK_THREAD_CORE_C__
-// This routine is only used locally. Noncritical interrupts must be disabled
-// at entry.
-
-static inline int
-__pk_thread_is_active(PkThread *thread)
-{
- return ((thread->state != PK_THREAD_STATE_COMPLETED) &&
- (thread->state != PK_THREAD_STATE_DELETED));
-}
-
-
-// This routine is only used locally. Noncritical interrupts must be disabled
-// at entry.
-
-static inline int
-__pk_thread_is_mapped(PkThread *thread)
-{
- return (thread->state == PK_THREAD_STATE_MAPPED);
-}
-
-
-// This routine is only used locally. Noncritical interrupts must be disabled
-// at entry. This is only called on mapped threads.
-
-static inline int
-__pk_thread_is_runnable(PkThread *thread)
-{
- return __pk_thread_queue_member(&__pk_run_queue, thread->priority);
-}
-
-
-// This routine is only used locally. Noncritical interrupts must be disabled
-// at entry.
-
-static inline PkThread*
-__pk_thread_at_priority(PkThreadPriority priority)
-{
- return (PkThread*)__pk_priority_map[priority];
-}
-
-
-// This routine is only used locally. Noncritical interrupts must be disabled
+// This routine is only used locally. Interrupts must be disabled
// at entry. The caller must also have checked that the priority is free.
// This routine is only called on threads known to be in a suspended state,
// either PK_THREAD_STATE_SUSPENDED_RUNNABLE or
@@ -96,17 +56,17 @@ __pk_thread_map(PkThread* thread)
if (PK_KERNEL_TRACE_ENABLE) {
if (__pk_thread_is_runnable(thread)) {
- PK_TRACE_THREAD_MAPPED_RUNNABLE(priority);
+ PK_KERN_TRACE("THREAD_MAPPED_RUNNABLE(%d)", priority);
} else if (thread->flags & PK_THREAD_FLAG_SEMAPHORE_PEND) {
- PK_TRACE_THREAD_MAPPED_SEMAPHORE_PEND(priority);
+ PK_KERN_TRACE("THREAD_MAPPED_SEMAPHORE_PEND(%d)", priority);
} else {
- PK_TRACE_THREAD_MAPPED_SLEEPING(priority);
+ PK_KERN_TRACE("THREAD_MAPPED_SLEEPING(%d)", priority);
}
}
}
-// This routine is only used locally. Noncritical interrupts must be disabled
+// This routine is only used locally. Interrupts must be disabled
// at entry. This routine is only ever called on threads in the
// PK_THREAD_STATE_MAPPED. Unmapping a thread removes it from the priority
// map, the run queue and any semaphore pend, but does not cancel any
@@ -214,9 +174,9 @@ __pk_thread_delete(PkThread *thread, PkThreadState final_state)
if (PK_KERNEL_TRACE_ENABLE) {
if (final_state == PK_THREAD_STATE_DELETED) {
- PK_TRACE_THREAD_DELETED(thread->priority);
+ PK_KERN_TRACE("THREAD_DELETED(%d)", thread->priority);
} else {
- PK_TRACE_THREAD_COMPLETED(thread->priority);
+ PK_KERN_TRACE("THREAD_COMPLETED(%d)", thread->priority);
}
}
@@ -243,17 +203,17 @@ __pk_thread_delete(PkThread *thread, PkThreadState final_state)
// pk_semaphore_release_all(), cancelling any semaphore timeouts is deferred
// until the thread runs again.
//
-// __pk_thread_timeout() is currenly the only timer interrupt called from a
-// critical section.
-//
// Note that we do not create trace events for unmapped threads since the trace
// tag only encodes the priority, which may be in use by a mapped thread.
void
__pk_thread_timeout(void *arg)
{
+ PkMachineContext ctx;
PkThread *thread = (PkThread *)arg;
+ pk_critical_section_enter(&ctx);
+
switch (thread->state) {
case PK_THREAD_STATE_MAPPED:
@@ -275,19 +235,21 @@ __pk_thread_timeout(void *arg)
default:
PK_PANIC(PK_THREAD_TIMEOUT_STATE);
}
+
+ pk_critical_section_exit(&ctx);
}
// This routine serves as a container for the PK_START_THREADS_HOOK and
// actually starts threads. The helper routine __pk_call_pk_start_threads()
// arranges this routine to be called with interrupts disabled while running
-// on the noncritical interrupt stack.
+// on the kernel stack.
//
// The reason for this roundabout is that we want to be able to run a hook
// routine (transparent to the application) that can hand over every last byte
// of free memory to "malloc()" - including the stack of main(). Since we
// always need to run on some stack, we chose to run the hook on the kernel
-// noncritical interrupt stack. However to do this safely we need to make sure
+// stack. However to do this safely we need to make sure
// that no interrupts will happen during this time. When __pk_thread_resume()
// is finally called all stack-based context is lost but it doesn't matter at
// that point - it's a one-way street into thread execution.
@@ -367,9 +329,6 @@ pk_start_threads(void)
/// \retval 0 Successful completion, including calls on a \a thread that is
/// already mapped.
///
-/// \retval -PK_ILLEGAL_CONTEXT_THREAD The API was called
-/// from a critical interrupt context.
-///
/// \retval -PK_INVALID_THREAD_AT_RESUME1 The \a thread is a null (0) pointer.
///
/// \retval -PK_INVALID_THREAD_AT_RESUME2 The \a thread is not active,
@@ -440,9 +399,6 @@ pk_thread_resume(PkThread *thread)
/// \retval 0 Successful completion, including calls on a \a thread that is
/// already suspended.
///
-/// \retval -PK_ILLEGAL_CONTEXT_THREAD The API was called from a critical
-/// interrupt context.
-///
/// \retval -PK_INVALID_THREAD_AT_SUSPEND1 The \a thread is a null (0) pointer
///
/// \retval -PK_INVALID_THREAD_AT_SUSPEND2 The \a thread is not active,
@@ -467,7 +423,7 @@ pk_thread_suspend(PkThread *thread)
if (__pk_thread_is_mapped(thread)) {
- PK_TRACE_THREAD_SUSPENDED(thread->priority);
+ PK_KERN_TRACE("THREAD_SUSPENDED(%d)", thread->priority);
__pk_thread_unmap(thread);
__pk_schedule();
}
@@ -497,8 +453,6 @@ pk_thread_suspend(PkThread *thread)
/// \retval 0 Successful completion, including calls on a \a thread that has
/// completed or had already been deleted.
///
-/// \retval -PK_ILLEGAL_CONTEXT_THREAD The API was called from a critical
-/// interrupt context.
///
/// \retval -PK_INVALID_THREAD_AT_DELETE The \a thread is a null (0) pointer.
@@ -552,67 +506,6 @@ pk_complete(void)
return PK_OK;
}
-
-/// Sleep a thread until an absolute time
-///
-/// \param time An absolute time as measured by the PK timebase
-///
-/// Threads can use this API to sleep until an absolute time. Sleeping threads
-/// are not scheduled, although they maintain their priorities. This differs
-/// from thread suspension, where the suspended thread relinquishes its
-/// priority. When the sleep timer times out the thread becomes runnable
-/// again, and will run as soon as it becomes the highest-priority mapped
-/// runnable thread.
-///
-/// Sleeping threads may also be later suspended. In this case the Sleep timer
-/// continues to run, and if it times out before the thread is resumed the
-/// thread will be immediately runnable when it is resumed.
-///
-/// See the PK specification for a full discussion of how PK handles
-/// scheduling events at absolute times "in the past". Briefly stated, if the
-/// \a time is in the past, the thread will Sleep for the briefest possible
-/// period supported by the hardware.
-///
-/// Return values other than PK_OK (0) are errors; see \ref pk_errors
-///
-/// \retval 0 Successful completion.
-///
-/// \retval -PK_ILLEGAL_CONTEXT_THREAD The API was not called from a thread
-/// context.
-
-// Note: Casting __pk_current_thread removes the 'volatile' attribute.
-
-int
-pk_sleep_absolute(PkTimebase time)
-{
- PkMachineContext ctx;
- PkThread *current;
-
- if (PK_ERROR_CHECK_API) {
- PK_ERROR_UNLESS_THREAD_CONTEXT();
- }
-
- pk_critical_section_enter(&ctx);
-
- current = (PkThread *)__pk_current_thread;
-
- current->timer.timeout = time;
- __pk_timer_schedule(&(current->timer));
-
- current->flags |= PK_THREAD_FLAG_TIMER_PEND;
-
- PK_TRACE_THREAD_SLEEP(current->priority);
-
- __pk_thread_queue_delete(&__pk_run_queue, current->priority);
- __pk_schedule();
-
- current->flags &= ~(PK_THREAD_FLAG_TIMER_PEND | PK_THREAD_FLAG_TIMED_OUT);
-
- pk_critical_section_exit(&ctx);
-
- return PK_OK;
-}
-
/// Sleep a thread for an interval relative to the current time.
///
/// \param interval A time interval relative to the current timebase.
@@ -646,294 +539,35 @@ pk_sleep_absolute(PkTimebase time)
int
pk_sleep(PkInterval interval)
{
- return pk_sleep_absolute(pk_timebase_get() + PK_INTERVAL_SCALE(interval));
-}
-
-
-/// Get information about a thread.
-///
-/// \param thread A pointer to the PkThread to query
-///
-/// \param state The value returned through this pointer is the current state
-/// of the thread; See \ref pk_thread_states. The caller can set this
-/// parameter to the null pointer (0) if this information is not required.
-///
-/// \param priority The value returned through this pointer is the current
-/// priority of the thread. The caller can set this parameter to the null
-/// pointer (0) if this information is not required.
-///
-/// \param runnable The value returned through this pointer is 1 if the thread
-/// is in state PK_THREAD_STATE_MAPPED and is currently in the run queue
-/// (i.e., neither blocked on a semaphore nor sleeping), otherwise 0. The
-/// caller can set this parameter to the null pointer (0) if this information
-/// is not required.
-///
-/// The information returned by this API can only be guaranteed consistent if
-/// the API is called from a critical section. Since the
-/// implementation of this API does not enforce a critical section, it is not
-/// an error to call this API from a critical interrupt context.
-///
-/// Return values other than PK_OK (0) are errors; see \ref pk_errors
-///
-/// \retval 0 Successful completion
-///
-/// \retval -PK_INVALID_THREAD_AT_INFO The \a thread is a null (0) pointer.
-
-int
-pk_thread_info_get(PkThread *thread,
- PkThreadState *state,
- PkThreadPriority *priority,
- int *runnable)
-{
- if (PK_ERROR_CHECK_API) {
- PK_ERROR_IF(thread == 0, PK_INVALID_THREAD_AT_INFO);
- }
-
- if (state) {
- *state = thread->state;
- }
- if (priority) {
- *priority = thread->priority;
- }
- if (runnable) {
- *runnable = ((thread->state == PK_THREAD_STATE_MAPPED) &&
- __pk_thread_queue_member(&__pk_run_queue,
- thread->priority));
- }
- return PK_OK;
-}
-
-
-/// Change the priority of a thread.
-///
-/// \param thread The thread whose priority will be changed
-///
-/// \param new_priority The new priority of the thread
-///
-/// \param old_priority The value returned through this pointer is the
-/// old priority of the thread prior to the change. The caller can set
-/// this parameter to the null pointer (0) if this information is not
-/// required.
-///
-/// Thread priorities can be changed by the \c pk_thread_priority_change()
-/// API. This call will fail if the thread pointer is invalid or if the thread
-/// is mapped and the new priority is currently in use. The call will succeed
-/// even if the \a thread is suspended, completed or deleted. The
-/// application-level scheduling algorithm is completely responsible for the
-/// correctness of the application in the event of suspended, completed or
-/// deleted threads.
-///
-/// Return values other than PK_OK (0) are errors; see \ref pk_errors
-///
-/// \retval 0 Successful completion, including the redundant case of
-/// attempting to change the priority of the thread to its current priority.
-///
-/// \retval -PK_ILLEGAL_CONTEXT_THREAD the API was called from a critical
-/// interrupt context.
-///
-/// \retval -PK_INVALID_THREAD_AT_CHANGE The \a thread is null (0) or
-/// otherwise invalid.
-///
-/// \retval -PK_INVALID_ARGUMENT_THREAD_CHANGE The \a new_priority is invalid.
-///
-/// \retval -PK_PRIORITY_IN_USE_AT_CHANGE The \a thread is mapped and the \a
-/// new_priority is currently in use by another thread.
-
-int
-pk_thread_priority_change(PkThread *thread,
- PkThreadPriority new_priority,
- PkThreadPriority *old_priority)
-{
+ PkTimebase time;
PkMachineContext ctx;
- PkThreadPriority priority;
-
- if (PK_ERROR_CHECK_API) {
- PK_ERROR_IF(thread == 0, PK_INVALID_THREAD_AT_CHANGE);
- PK_ERROR_IF(new_priority > PK_THREADS,
- PK_INVALID_ARGUMENT_THREAD_CHANGE);
- }
-
- pk_critical_section_enter(&ctx);
-
- priority = thread->priority;
-
- if (priority != new_priority) {
-
- if (!__pk_thread_is_mapped(thread)) {
-
- thread->priority = new_priority;
-
- } else {
-
- if (PK_ERROR_CHECK_API) {
- PK_ERROR_IF_CRITICAL(__pk_priority_map[new_priority] != 0,
- PK_PRIORITY_IN_USE_AT_CHANGE,
- &ctx);
- }
-
- __pk_thread_unmap(thread);
- thread->priority = new_priority;
- __pk_thread_map(thread);
- __pk_schedule();
- }
- }
-
- if (old_priority) {
- *old_priority = priority;
- }
-
- pk_critical_section_exit(&ctx);
-
- return PK_OK;
-}
-
-
-/// Return a pointer to the thread (if any) mapped at a given priority.
-///
-/// \param priority The thread priority of interest
-///
-/// \param thread The value returned through this pointer is a pointer to the
-/// thread currently mapped at the given priority level. If no thread is
-/// mapped, or if the \a priority is the priority of the idle thread, the
-/// pointer returned will be null (0).
-///
-/// The information returned by this API can only be guaranteed consistent if
-/// the API is called from a critical section. Since the
-/// implementation of this API does not require a critical section, it is not
-/// an error to call this API from a critical interrupt context.
-///
-/// Return values other than PK_OK (0) are errors; see \ref pk_errors
-///
-/// \retval 0 Successful completion.
-///
-/// \retval -PK_INVALID_ARGUMENT_THREAD_PRIORITY The \a priority is invalid
-/// or the \a thread parameter is null (0).
+ PkThread *current;
-int
-pk_thread_at_priority(PkThreadPriority priority,
- PkThread **thread)
-{
if (PK_ERROR_CHECK_API) {
- PK_ERROR_IF((priority > PK_THREADS) || (thread == 0),
- PK_INVALID_ARGUMENT_THREAD_PRIORITY);
+ PK_ERROR_UNLESS_THREAD_CONTEXT();
}
- *thread = __pk_thread_at_priority(priority);
+ time = pk_timebase_get() + PK_INTERVAL_SCALE(interval);
- return PK_OK;
-}
-
-
-/// Swap thread priorities
-///
-/// \param thread_a A pointer to an initialized PkThread
-///
-/// \param thread_b A pointer to an initialized PkThread
-///
-/// This API swaps the priorities of \a thread_a and \a thread_b. The API is
-/// provided to support general and efficient application-directed scheduling
-/// algorithms. The requirements on the \a thread_a and \a thread_b arguments
-/// are that they are valid pointers to initialized PkThread structures, that
-/// the current thread priorities of both threads are legal, and that if a
-/// thread is currently mapped, that the new thread priority is not otherwise
-/// in use.
-///
-/// The API does not require either thread to be mapped, or even to be active.
-/// It is legal for one or both of the swap partners to be suspended, deleted
-/// or completed threads. The application is completely responsible for the
-/// correctness of scheduling algorithms that might operate on inactive or
-/// suspended threads.
-///
-/// The API does not change the mapped status of a thread. A thread will be
-/// mapped after the call of pk_thread_priority_swap() if and only if it was
-/// mapped prior to the call. If the new priority of a mapped thread is
-/// currently in use (by a thread other than the swap partner), then the
-/// PK_PRIORITY_IN_USE_AT_SWAP error is signalled and the swap does not take
-/// place. This could only happen if the swap partner is not currently mapped.
-///
-/// It is legal for a thread to swap its own priority with another thread. The
-/// degenerate case that \a thread_a and \a thread_b are equal is also legal -
-/// but has no effect.
-///
-/// Return values other than PK_OK (0) are errors; see \ref pk_errors
-///
-/// \retval 0 Successful completion, including the redundant cases that do not
-/// actually change priorities, or the cases that assign new priorities to
-/// suspended, completed or deleted threads.
-///
-/// \retval -PK_ILLEGAL_CONTEXT_THREAD the API was called from a critical
-/// interrupt context.
-///
-/// \retval -PK_INVALID_THREAD_AT_SWAP1 One or both of \a thread_a and
-/// \a thread_b is null (0) or otherwise invalid,
-///
-/// \retval -PK_INVALID_THREAD_AT_SWAP2 the priorities of One or both of
-/// \a thread_a and \a thread_b are invalid.
-///
-/// \retval -PK_INVALID_ARGUMENT One or both of the priorities
-/// of \a thread_a and \a thread_b is invalid.
-///
-/// \retval -PK_PRIORITY_IN_USE_AT_SWAP Returned if a thread is mapped and the
-/// new thread priority is currently in use by another thread (other than the
-/// swap partner).
-
-int
-pk_thread_priority_swap(PkThread* thread_a, PkThread* thread_b)
-{
- PkMachineContext ctx;
- PkThreadPriority priority_a, priority_b;
- int mapped_a, mapped_b;
+ pk_critical_section_enter(&ctx);
- if (PK_ERROR_CHECK_API) {
- PK_ERROR_IF((thread_a == 0) || (thread_b == 0),
- PK_INVALID_THREAD_AT_SWAP1);
- }
+ current = (PkThread *)__pk_current_thread;
- pk_critical_section_enter(&ctx);
+ current->timer.timeout = time;
+ __pk_timer_schedule(&(current->timer));
- if (thread_a != thread_b) {
+ current->flags |= PK_THREAD_FLAG_TIMER_PEND;
- mapped_a = __pk_thread_is_mapped(thread_a);
- mapped_b = __pk_thread_is_mapped(thread_b);
- priority_a = thread_a->priority;
- priority_b = thread_b->priority;
+ PK_KERN_TRACE("THREAD_SLEEP(%d)", current->priority);
- if (PK_ERROR_CHECK_API) {
- int priority_in_use;
- PK_ERROR_IF_CRITICAL((priority_a > PK_THREADS) ||
- (priority_b > PK_THREADS),
- PK_INVALID_THREAD_AT_SWAP2,
- &ctx);
- priority_in_use =
- (mapped_a && !mapped_b &&
- (__pk_thread_at_priority(priority_b) != 0)) ||
- (!mapped_a && mapped_b &&
- (__pk_thread_at_priority(priority_a) != 0));
- PK_ERROR_IF_CRITICAL(priority_in_use,
- PK_PRIORITY_IN_USE_AT_SWAP, &ctx);
- }
+ __pk_thread_queue_delete(&__pk_run_queue, current->priority);
+ __pk_schedule();
- if (mapped_a) {
- __pk_thread_unmap(thread_a);
- }
- if (mapped_b) {
- __pk_thread_unmap(thread_b);
- }
- thread_a->priority = priority_b;
- thread_b->priority = priority_a;
- if (mapped_a) {
- __pk_thread_map(thread_a);
- }
- if (mapped_b) {
- __pk_thread_map(thread_b);
- }
- __pk_schedule();
- }
+ current->flags &= ~(PK_THREAD_FLAG_TIMER_PEND | PK_THREAD_FLAG_TIMED_OUT);
pk_critical_section_exit(&ctx);
return PK_OK;
}
-
#undef __PK_THREAD_CORE_C__
diff --git a/pk/kernel/pk_thread_init.c b/pk/kernel/pk_thread_init.c
index 686f3512..97bce4c4 100644
--- a/pk/kernel/pk_thread_init.c
+++ b/pk/kernel/pk_thread_init.c
@@ -53,9 +53,6 @@
///
/// \retval -PK_INVALID_THREAD_AT_CREATE The \a thread is a null (0) pointer.
///
-/// \retval -PK_ILLEGAL_CONTEXT The API was called from a critical interrupt
-/// context.
-///
/// \retval -PK_INVALID_ARGUMENT_THREAD1 the \a thread_routine is null (0)
///
/// \retval -PK_INVALID_ARGUMENT_THREAD2 the \a priority is invalid,
@@ -113,9 +110,9 @@ pk_thread_create(PkThread *thread,
thread->state = PK_THREAD_STATE_SUSPENDED_RUNNABLE;
thread->flags = 0;
- pk_timer_create_nonpreemptible(&(thread->timer),
- __pk_thread_timeout,
- (void *)thread);
+ pk_timer_create(&(thread->timer),
+ __pk_thread_timeout,
+ (void *)thread);
__pk_thread_context_initialize(thread, thread_routine, arg);
diff --git a/pk/kernel/pk_thread_util.c b/pk/kernel/pk_thread_util.c
new file mode 100644
index 00000000..bf2e21b7
--- /dev/null
+++ b/pk/kernel/pk_thread_util.c
@@ -0,0 +1,291 @@
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2014
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file pk_thread_util.c
+/// \brief PK thread utility APIs
+///
+/// The entry points in this file are considered extra routines that will
+/// only be included in a PK application that enables threads and uses at
+/// least one of these interfaces.
+
+#include "pk.h"
+#include "pk_thread.h"
+
+/// Get information about a thread.
+///
+/// \param thread A pointer to the PkThread to query
+///
+/// \param state The value returned through this pointer is the current state
+/// of the thread; See \ref pk_thread_states. The caller can set this
+/// parameter to the null pointer (0) if this information is not required.
+///
+/// \param priority The value returned through this pointer is the current
+/// priority of the thread. The caller can set this parameter to the null
+/// pointer (0) if this information is not required.
+///
+/// \param runnable The value returned through this pointer is 1 if the thread
+/// is in state PK_THREAD_STATE_MAPPED and is currently in the run queue
+/// (i.e., neither blocked on a semaphore nor sleeping), otherwise 0. The
+/// caller can set this parameter to the null pointer (0) if this information
+/// is not required.
+///
+/// The information returned by this API can only be guaranteed consistent if
+/// the API is called from a critical section.
+///
+/// Return values other than PK_OK (0) are errors; see \ref pk_errors
+///
+/// \retval 0 Successful completion
+///
+/// \retval -PK_INVALID_THREAD_AT_INFO The \a thread is a null (0) pointer.
+
+int
+pk_thread_info_get(PkThread *thread,
+ PkThreadState *state,
+ PkThreadPriority *priority,
+ int *runnable)
+{
+ if (PK_ERROR_CHECK_API) {
+ PK_ERROR_IF(thread == 0, PK_INVALID_THREAD_AT_INFO);
+ }
+
+ if (state) {
+ *state = thread->state;
+ }
+ if (priority) {
+ *priority = thread->priority;
+ }
+ if (runnable) {
+ *runnable = ((thread->state == PK_THREAD_STATE_MAPPED) &&
+ __pk_thread_queue_member(&__pk_run_queue,
+ thread->priority));
+ }
+ return PK_OK;
+}
+
+
+/// Change the priority of a thread.
+///
+/// \param thread The thread whose priority will be changed
+///
+/// \param new_priority The new priority of the thread
+///
+/// \param old_priority The value returned through this pointer is the
+/// old priority of the thread prior to the change. The caller can set
+/// this parameter to the null pointer (0) if this information is not
+/// required.
+///
+/// Thread priorities can be changed by the \c pk_thread_priority_change()
+/// API. This call will fail if the thread pointer is invalid or if the thread
+/// is mapped and the new priority is currently in use. The call will succeed
+/// even if the \a thread is suspended, completed or deleted. The
+/// application-level scheduling algorithm is completely responsible for the
+/// correctness of the application in the event of suspended, completed or
+/// deleted threads.
+///
+/// Return values other than PK_OK (0) are errors; see \ref pk_errors
+///
+/// \retval 0 Successful completion, including the redundant case of
+/// attempting to change the priority of the thread to its current priority.
+///
+/// \retval -PK_INVALID_THREAD_AT_CHANGE The \a thread is null (0) or
+/// otherwise invalid.
+///
+/// \retval -PK_INVALID_ARGUMENT_THREAD_CHANGE The \a new_priority is invalid.
+///
+/// \retval -PK_PRIORITY_IN_USE_AT_CHANGE The \a thread is mapped and the \a
+/// new_priority is currently in use by another thread.
+
+int
+pk_thread_priority_change(PkThread *thread,
+ PkThreadPriority new_priority,
+ PkThreadPriority *old_priority)
+{
+ PkMachineContext ctx;
+ PkThreadPriority priority;
+
+ if (PK_ERROR_CHECK_API) {
+ PK_ERROR_IF(thread == 0, PK_INVALID_THREAD_AT_CHANGE);
+ PK_ERROR_IF(new_priority > PK_THREADS,
+ PK_INVALID_ARGUMENT_THREAD_CHANGE);
+ }
+
+ pk_critical_section_enter(&ctx);
+
+ priority = thread->priority;
+
+ if (priority != new_priority) {
+
+ if (!__pk_thread_is_mapped(thread)) {
+
+ thread->priority = new_priority;
+
+ } else {
+
+ if (PK_ERROR_CHECK_API) {
+ PK_ERROR_IF_CRITICAL(__pk_priority_map[new_priority] != 0,
+ PK_PRIORITY_IN_USE_AT_CHANGE,
+ &ctx);
+ }
+
+ __pk_thread_unmap(thread);
+ thread->priority = new_priority;
+ __pk_thread_map(thread);
+ __pk_schedule();
+ }
+ }
+
+ if (old_priority) {
+ *old_priority = priority;
+ }
+
+ pk_critical_section_exit(&ctx);
+
+ return PK_OK;
+}
+
+
+/// Return a pointer to the thread (if any) mapped at a given priority.
+///
+/// \param priority The thread priority of interest
+///
+/// \param thread The value returned through this pointer is a pointer to the
+/// thread currently mapped at the given priority level. If no thread is
+/// mapped, or if the \a priority is the priority of the idle thread, the
+/// pointer returned will be null (0).
+///
+/// The information returned by this API can only be guaranteed consistent if
+/// the API is called from a critical section.
+///
+/// Return values other than PK_OK (0) are errors; see \ref pk_errors
+///
+/// \retval 0 Successful completion.
+///
+/// \retval -PK_INVALID_ARGUMENT_THREAD_PRIORITY The \a priority is invalid
+/// or the \a thread parameter is null (0).
+
+int
+pk_thread_at_priority(PkThreadPriority priority,
+ PkThread **thread)
+{
+ if (PK_ERROR_CHECK_API) {
+ PK_ERROR_IF((priority > PK_THREADS) || (thread == 0),
+ PK_INVALID_ARGUMENT_THREAD_PRIORITY);
+ }
+
+ *thread = __pk_thread_at_priority(priority);
+
+ return PK_OK;
+}
+
+
+/// Swap thread priorities
+///
+/// \param thread_a A pointer to an initialized PkThread
+///
+/// \param thread_b A pointer to an initialized PkThread
+///
+/// This API swaps the priorities of \a thread_a and \a thread_b. The API is
+/// provided to support general and efficient application-directed scheduling
+/// algorithms. The requirements on the \a thread_a and \a thread_b arguments
+/// are that they are valid pointers to initialized PkThread structures, that
+/// the current thread priorities of both threads are legal, and that if a
+/// thread is currently mapped, that the new thread priority is not otherwise
+/// in use.
+///
+/// The API does not require either thread to be mapped, or even to be active.
+/// It is legal for one or both of the swap partners to be suspended, deleted
+/// or completed threads. The application is completely responsible for the
+/// correctness of scheduling algorithms that might operate on inactive or
+/// suspended threads.
+///
+/// The API does not change the mapped status of a thread. A thread will be
+/// mapped after the call of pk_thread_priority_swap() if and only if it was
+/// mapped prior to the call. If the new priority of a mapped thread is
+/// currently in use (by a thread other than the swap partner), then the
+/// PK_PRIORITY_IN_USE_AT_SWAP error is signalled and the swap does not take
+/// place. This could only happen if the swap partner is not currently mapped.
+///
+/// It is legal for a thread to swap its own priority with another thread. The
+/// degenerate case that \a thread_a and \a thread_b are equal is also legal -
+/// but has no effect.
+///
+/// Return values other than PK_OK (0) are errors; see \ref pk_errors
+///
+/// \retval 0 Successful completion, including the redundant cases that do not
+/// actually change priorities, or the cases that assign new priorities to
+/// suspended, completed or deleted threads.
+///
+/// \retval -PK_INVALID_THREAD_AT_SWAP1 One or both of \a thread_a and
+/// \a thread_b is null (0) or otherwise invalid,
+///
+/// \retval -PK_INVALID_THREAD_AT_SWAP2 the priorities of One or both of
+/// \a thread_a and \a thread_b are invalid.
+///
+/// \retval -PK_INVALID_ARGUMENT One or both of the priorities
+/// of \a thread_a and \a thread_b is invalid.
+///
+/// \retval -PK_PRIORITY_IN_USE_AT_SWAP Returned if a thread is mapped and the
+/// new thread priority is currently in use by another thread (other than the
+/// swap partner).
+
+int
+pk_thread_priority_swap(PkThread* thread_a, PkThread* thread_b)
+{
+ PkMachineContext ctx;
+ PkThreadPriority priority_a, priority_b;
+ int mapped_a, mapped_b;
+
+ if (PK_ERROR_CHECK_API) {
+ PK_ERROR_IF((thread_a == 0) || (thread_b == 0),
+ PK_INVALID_THREAD_AT_SWAP1);
+ }
+
+ pk_critical_section_enter(&ctx);
+
+ if (thread_a != thread_b) {
+
+ mapped_a = __pk_thread_is_mapped(thread_a);
+ mapped_b = __pk_thread_is_mapped(thread_b);
+ priority_a = thread_a->priority;
+ priority_b = thread_b->priority;
+
+ if (PK_ERROR_CHECK_API) {
+ int priority_in_use;
+ PK_ERROR_IF_CRITICAL((priority_a > PK_THREADS) ||
+ (priority_b > PK_THREADS),
+ PK_INVALID_THREAD_AT_SWAP2,
+ &ctx);
+ priority_in_use =
+ (mapped_a && !mapped_b &&
+ (__pk_thread_at_priority(priority_b) != 0)) ||
+ (!mapped_a && mapped_b &&
+ (__pk_thread_at_priority(priority_a) != 0));
+ PK_ERROR_IF_CRITICAL(priority_in_use,
+ PK_PRIORITY_IN_USE_AT_SWAP, &ctx);
+ }
+
+ if (mapped_a) {
+ __pk_thread_unmap(thread_a);
+ }
+ if (mapped_b) {
+ __pk_thread_unmap(thread_b);
+ }
+ thread_a->priority = priority_b;
+ thread_b->priority = priority_a;
+ if (mapped_a) {
+ __pk_thread_map(thread_a);
+ }
+ if (mapped_b) {
+ __pk_thread_map(thread_b);
+ }
+ __pk_schedule();
+ }
+
+ pk_critical_section_exit(&ctx);
+
+ return PK_OK;
+}
+
diff --git a/pk/kernel/pk_timer_core.c b/pk/kernel/pk_timer_core.c
index bc90a3e7..59b9d628 100644
--- a/pk/kernel/pk_timer_core.c
+++ b/pk/kernel/pk_timer_core.c
@@ -16,7 +16,7 @@
/// opens up the possibility of scheduling events "in the past". PK
/// uniformly handles this case by scheduling "past" events to occur 1
/// timebase tick in the future, so that timer callbacks are always run in the
-/// expected noncritical interrupt context.
+/// expected interrupt context.
///
/// PK implements the time queue as a simple unordered list of events, plus a
/// dedicated variable that holds the earliest timeout of any event in the
@@ -65,7 +65,15 @@
#include "pk.h"
-// This routine is only used in this file, and will always be called in
+// Declare the timer bottom half handler
+static PK_BH_HANDLER(__pk_timer_bh_handler);
+
+// Define the timer bottom half handler that the interrupt handler will
+// schedule
+PK_BH_STATIC_CREATE(pk_timer_bh, __pk_timer_bh_handler, 0);
+
+
+// This routine is only used in this file, and will always be called in a
// critical section.
static inline int
@@ -78,7 +86,7 @@ timer_active(PkTimer* timer)
// This is the kernel version of pk_timer_cancel().
//
// This routine is used here and by thread and semaphore routines.
-// Noncritical interrupts must be disabled at entry.
+// External interrupts must be disabled at entry.
//
// If the timer is active, then there is a special case if we are going to
// delete the 'cursor' - that is the timer that __pk_timer_handler() is going
@@ -114,7 +122,7 @@ __pk_timer_cancel(PkTimer *timer)
// This is the kernel version of pk_timer_schedule().
//
// This routine is used here and by thread and semaphore routines.
-// Noncritical interrupts must be disabled at entry.
+// interrupts must be disabled at entry.
//
// Unless the timer is already active it is enqueued in the doubly-linked
// timer list by inserting the timer at the end of the queue. Then the
@@ -144,8 +152,7 @@ __pk_timer_schedule(PkTimer* timer)
// deletions and other factors, there may not actually be a timer in the queue
// that has timed out - but it doesn't matter (other than for efficiency).
//
-// Noncritical interrupts are (must be) disabled at entry, and this invariant
-// is checked. This routine must not be entered reentrantly.
+// This routine must not be entered reentrantly.
//
// First, time out any timers that have expired. Timers in the queue are
// unordered, so we have to check every one. Since passing through the
@@ -158,9 +165,8 @@ __pk_timer_schedule(PkTimer* timer)
// On each pass through the loop tq->next_timeout computes the minimum timeout
// of events remaining in the queue. This is the only part of the kernel that
// searches a list of indefinite length. Kernel interrupt latency is mitigated
-// by running callbacks with interrupts disabled either during or after the
-// call for timed out events, and also after every check for events that have
-// not timed out.
+// by running this function as a bottom half. As such, interrupts are only
+// disabled when explicitly requested.
//
// Because interrupt preemption is enabled during processing, and preempting
// handlers may invoke time queue operations, we need to establish a pointer
@@ -171,9 +177,10 @@ __pk_timer_schedule(PkTimer* timer)
// The main loop iterates on the PkDeque form of the time queue, casting each
// element back up to the PkTimer as it is processed.
-void
-__pk_timer_handler()
+static void
+__pk_timer_bh_handler(void* arg)
{
+ PkMachineContext ctx;
PkTimeQueue* tq;
PkTimebase now;
PkTimer* timer;
@@ -182,20 +189,28 @@ __pk_timer_handler()
tq = &__pk_time_queue;
+ // Check if we entered the function while it was running in another context.
if (PK_ERROR_CHECK_KERNEL) {
if (tq->cursor != 0) {
PK_PANIC(PK_TIMER_HANDLER_INVARIANT);
}
}
- while ((now = pk_timebase_get()) >= tq->next_timeout) {
+ pk_critical_section_enter(&ctx);
+ while ((now = pk_timebase_get()) >= tq->next_timeout) {
tq->next_timeout = PK_TIMEBASE_MAX;
timer_deque = ((PkDeque*)tq)->next;
+ // Iterate through the entire timer list, calling the callback of
+ // timed-out elements and finding the timer that will timeout next,
+ // which is stored in tq->next_timeout.
while (timer_deque != (PkDeque*)tq) {
timer = (PkTimer*)timer_deque;
+
+ // Setting this to a non-zero value indicates we are in the middle
+ // of processing the time queue.
tq->cursor = timer_deque->next;
if (timer->timeout <= now) {
@@ -209,42 +224,49 @@ __pk_timer_handler()
pk_deque_delete(timer_deque);
+ pk_critical_section_exit(&ctx);
+
callback = timer->callback;
if (callback) {
- if (timer->options & PK_TIMER_CALLBACK_PREEMPTIBLE) {
- pk_interrupt_preemption_enable();
- callback(timer->arg);
- } else {
- callback(timer->arg);
- pk_interrupt_preemption_enable();
- }
+ callback(timer->arg);
}
- pk_interrupt_preemption_disable();
} else {
// This timer has not timed out. Its timeout will simply
- // participate in the computation of the next timeout. For
- // interrupt latency reasons we always allow a period of
- // interrupt preemption.
-
+ // participate in the computation of the next timeout.
tq->next_timeout = MIN(timer->timeout, tq->next_timeout);
- pk_interrupt_preemption_enable();
- pk_interrupt_preemption_disable();
+ pk_critical_section_exit(&ctx);
}
timer_deque = tq->cursor;
+ pk_critical_section_enter(&ctx);
}
+
+ // Time has passed since we checked the time. Loop back
+ // to check the time again and see if enough time has passed
+ // that the next timer has timed out too.
}
+ pk_critical_section_exit(&ctx);
+
+ // This marks that we are no longer processing the time queue
tq->cursor = 0;
// Finally, reschedule the next timeout
-
__pk_schedule_hardware_timeout(tq->next_timeout);
}
+void
+__pk_timer_handler(void)
+{
+ //schedule the timer bottom half handler which
+ //is preemptible.
+ pk_bh_schedule(&pk_timer_bh);
+}
+
+
/// Schedule a timer for an interval relative to the current time.
///
/// \param timer The PkTimer to schedule.
@@ -264,8 +286,6 @@ __pk_timer_handler()
/// \retval -PK_INVALID_TIMER_AT_SCHEDULE A a null (0) pointer was provided as
/// the \a timer argument.
///
-/// \retval -PK_ILLEGAL_CONTEXT_TIMER The call was made from a critical
-/// interrupt context.
int
pk_timer_schedule(PkTimer *timer,
@@ -313,9 +333,6 @@ pk_timer_schedule(PkTimer *timer,
///
/// \retval -PK_INVALID_TIMER_AT_CANCEL The \a timer is a null (0) pointer.
///
-/// \retval -PK_ILLEGAL_CONTEXT_TIMER The call was made from a critical
-/// interrupt context.
-///
int
pk_timer_cancel(PkTimer *timer)
@@ -353,9 +370,7 @@ pk_timer_cancel(PkTimer *timer)
/// null pointer (0) if this information is not required.
///
/// The information returned by this API can only be guaranteed consistent if
-/// the API is called from a critical section. Since the
-/// implementation of this API does not require a critical section, it is not
-/// an error to call this API from a critical interrupt context.
+/// the API is called from a critical section.
///
/// Return values other than PK_OK (0) are errors; see \ref pk_errors
///
diff --git a/pk/kernel/pk_timer_init.c b/pk/kernel/pk_timer_init.c
index 9b8f0e1d..457f78c4 100644
--- a/pk/kernel/pk_timer_init.c
+++ b/pk/kernel/pk_timer_init.c
@@ -13,29 +13,8 @@
#include "pk.h"
-// Implementation of timer creation
-static int
-_pk_timer_create(PkTimer *timer,
- PkTimerCallback callback,
- void *arg,
- int options)
-{
- if (PK_ERROR_CHECK_API) {
- PK_ERROR_IF((timer == 0), PK_INVALID_TIMER_AT_CREATE);
- }
-
- pk_deque_element_create((PkDeque*)timer);
- timer->timeout = 0;
- timer->callback = callback;
- timer->arg = arg;
- timer->options = options;
-
- return PK_OK;
-}
-
-
-/// Create (initialize) a preemptible timer.
+/// Create (initialize) a timer.
///
/// \param timer The PkTimer to initialize.
///
@@ -48,10 +27,10 @@ _pk_timer_create(PkTimer *timer,
/// timer in the kernel time queue. Timers can be cancelled by a call of
/// pk_timer_cancel().
///
-/// Timers created with pk_timer_create() are always run as noncritical
-/// interrupt handlers with interrupt preemption enabled. Timer callbacks are
-/// free to enter critical sections of any priorioty if required, but must
-/// always exit with noncritical interrupts enabled.
+/// Timers created with pk_timer_create() are always run as
+/// bottom-half handlers with interrupt preemption enabled. Timer callbacks are
+/// free to enter critical sections if required, but must
+/// always exit with interrupts enabled.
///
/// Caution: PK has no way to know if an PkTimer structure provided to
/// pk_timer_create() is safe to use as a timer, and will silently modify
@@ -63,59 +42,21 @@ _pk_timer_create(PkTimer *timer,
///
/// \retval -PK_INVALID_TIMER_AT_CREATE The \a timer is a null (0) pointer.
-int
+int
pk_timer_create(PkTimer *timer,
- PkTimerCallback callback,
- void *arg)
+ PkTimerCallback callback,
+ void *arg)
{
- return _pk_timer_create(timer, callback, arg,
- PK_TIMER_CALLBACK_PREEMPTIBLE);
-}
-
+ if (PK_ERROR_CHECK_API) {
+ PK_ERROR_IF((timer == 0), PK_INVALID_TIMER_AT_CREATE);
+ }
-/// Create (initialize) a nonpreemptible timer.
-///
-/// \param timer The PkTimer to initialize.
-///
-/// \param callback The timer callback
-///
-/// \param arg Private data provided to the callback.
-///
-/// Once created with pk_timer_create_preemptible() a timer can be scheduled
-/// with pk_timer_schedule() or pk_timer_schedule_absolute(), which queues
-/// the timer in the kernel time queue. Timers can be cancelled by a call of
-/// pk_timer_cancel().
-///
-/// Timers created with pk_timer_create_nonpreemptible() are always run as
-/// noncritical interrupt handlers with interrupt preemption disabled. Timer
-/// callbacks are free to later enable preemption if desired, but must always
-/// exit with noncritical interrupts disabled.
-///
-/// \note The use of pk_timer_create_nonpreemptible() should be rare, and the
-/// timer callbacks should be short and sweet to avoid long interrupt
-/// latencies for other interrupts. This API was initially introduced for use
-/// by the PK kernel itself when scheduling thread-timer callbacks to avoid
-/// potential race conditions with other interrupts that may modify thread
-/// state or the state of the time queue. Applications may also require this
-/// facility to guarantee a consistent state in the event that other
-/// interrupts may cancel the timer.
-///
-/// Caution: PK has no way to know if an PkTimer structure provided to
-/// pk_timer_create() is safe to use as a timer, and will silently modify
-/// whatever memory is provided.
-///
-/// Return values other then PK_OK (0) are errors; see \ref pk_errors
-///
-/// \retval 0 Successful completion
-///
-/// \retval -PK_INVALID_TIMER_AT_CREATE The \a timer is a null (0) pointer.
+ pk_deque_element_create((PkDeque*)timer);
+ timer->timeout = 0;
+ timer->callback = callback;
+ timer->arg = arg;
-int
-pk_timer_create_nonpreemptible(PkTimer *timer,
- PkTimerCallback callback,
- void *arg)
-{
- return _pk_timer_create(timer, callback, arg, 0);
+ return PK_OK;
}
diff --git a/pk/kernel/pkkernelfiles.mk b/pk/kernel/pkkernelfiles.mk
index 958e4ddf..bb1c310b 100644
--- a/pk/kernel/pkkernelfiles.mk
+++ b/pk/kernel/pkkernelfiles.mk
@@ -21,11 +21,11 @@
##########################################################################
# Object Files
##########################################################################
-PK-C-SOURCES = pk_core.c pk_init.c pk_stack_init.c
+PK-C-SOURCES = pk_core.c pk_init.c pk_stack_init.c pk_bh_core.c
PK-TIMER-C-SOURCES += pk_timer_core.c pk_timer_init.c
-PK-THREAD-C-SOURCES += pk_thread_init.c pk_thread_core.c \
+PK-THREAD-C-SOURCES += pk_thread_init.c pk_thread_core.c pk_thread_util.c \
pk_semaphore_init.c pk_semaphore_core.c
PK_OBJECTS += $(PK-C-SOURCES:.c=.o)
OpenPOWER on IntegriCloud