summaryrefslogtreecommitdiffstats
path: root/pk
diff options
context:
space:
mode:
authorGlenn Miles <milesg@us.ibm.com>2015-03-23 13:13:04 -0500
committerGlenn R. Miles <milesg@us.ibm.com>2015-03-23 14:48:23 -0500
commite3ee55f1932041cefe3e2ebba49207aa9805e51e (patch)
tree1ed53a284799f8d491b5eecd123acbd438845d12 /pk
parentd2dd0140d007bce8214fcf022041c0dd5b522dc2 (diff)
downloadtalos-sbe-e3ee55f1932041cefe3e2ebba49207aa9805e51e.tar.gz
talos-sbe-e3ee55f1932041cefe3e2ebba49207aa9805e51e.zip
Add efficient time scaling support
Also removed automatic rescheduling of periodic timers Change-Id: I6427a8d8ed5ca4b75389c3610a16dba10783a8ae Reviewed-on: http://gfw160.aus.stglabs.ibm.com:8080/gerrit/16537 Reviewed-by: Glenn R. Miles <milesg@us.ibm.com> Tested-by: Glenn R. Miles <milesg@us.ibm.com>
Diffstat (limited to 'pk')
-rw-r--r--pk/kernel/pk_api.h36
-rw-r--r--pk/kernel/pk_init.c49
-rw-r--r--pk/kernel/pk_semaphore_core.c4
-rw-r--r--pk/kernel/pk_thread_core.c2
-rw-r--r--pk/kernel/pk_timer_core.c80
-rw-r--r--pk/kernel/pk_timer_init.c1
-rw-r--r--pk/trace/pk_trace_core.c4
7 files changed, 77 insertions, 99 deletions
diff --git a/pk/kernel/pk_api.h b/pk/kernel/pk_api.h
index 51440275..3d0c4a3f 100644
--- a/pk/kernel/pk_api.h
+++ b/pk/kernel/pk_api.h
@@ -298,11 +298,22 @@
#define PK_TIMEBASE_FREQUENCY_HZ __pk_timebase_frequency_hz
+/// This is the unscaled timebase frequency in Hz.
+#ifdef APPCFG_USE_EXT_TIMEBASE
+#define PK_BASE_FREQ_HZ 25000000
+#else
+#define PK_BASE_FREQ_HZ 400000000
+#endif /* APPCFG_USE_EXT_TIMEBASE */
+
+/// Scale a time interval to be _closer_ to what was actually requested
+/// base on the actual timebase frequency.
+#define PK_INTERVAL_SCALE(interval) ((interval) + ((interval) >> __pk_timebase_rshift))
+
/// Convert a time in integral seconds to a time interval - overflows are
/// ignored. The application can redefine this macro.
#ifndef PK_SECONDS
-#define PK_SECONDS(s) ((PkInterval)(__pk_timebase_frequency_hz * (PkInterval)(s)))
+#define PK_SECONDS(s) ((PkInterval)(PK_BASE_FREQ_HZ * (PkInterval)(s)))
#endif
/// Convert a time in integral milliseconds to a time interval - overflows are
@@ -310,7 +321,7 @@
/// assumed. The application can redefine this macro.
#ifndef PK_MILLISECONDS
-#define PK_MILLISECONDS(m) ((PkInterval)(__pk_timebase_frequency_khz * (PkInterval)(m)))
+#define PK_MILLISECONDS(m) ((PkInterval)((PK_BASE_FREQ_HZ * (PkInterval)(m)) / 1000))
#endif
/// Convert a time in integral microseconds to a time interval - overflows are
@@ -318,7 +329,7 @@
/// assumed. The application can redefine this macro.
#ifndef PK_MICROSECONDS
-#define PK_MICROSECONDS(u) ((PkInterval)(__pk_timebase_frequency_mhz * (PkInterval)(u)))
+#define PK_MICROSECONDS(u) ((PkInterval)((PK_BASE_FREQ_HZ * (PkInterval)(u)) / 1000000))
#endif
/// Convert a time in integral nanoseconds to a time interval - overflows are
@@ -326,8 +337,7 @@
/// assumed. The application can redefine this macro.
#ifndef PK_NANOSECONDS
-#define PK_NANOSECONDS(n) \
- ((PkInterval)((__pk_timebase_frequency_mhz * (PkInterval)(n)) / 1000))
+#define PK_NANOSECONDS(n) ((PkInterval)((PK_BASE_FREQ_HZ * (PkInterval)(n)) / 1000000000))
#endif
@@ -437,6 +447,8 @@
/// The timebase frequency in Hz; A parameter to pk_initialize()
extern uint32_t __pk_timebase_frequency_hz;
+extern uint8_t __pk_timebase_rshift;
+
/// The timebase frequency in KHz
extern uint32_t __pk_timebase_frequency_khz;
@@ -579,12 +591,6 @@ typedef struct PkTimer {
/// The absolute timeout of the timer.
PkTimebase timeout;
- /// The timer period
- ///
- /// If not 0, then this is a periodic timer and it will be automatically
- /// rescheduled in absolute time from the previous timeout.
- PkInterval period;
-
/// The timer callback
///
/// For PK thread timers used to implement Sleep and semaphore pend
@@ -692,14 +698,8 @@ pk_timer_create_nonpreemptible(PkTimer *timer,
void *arg);
int
-pk_timer_schedule_absolute(PkTimer *timer,
- PkTimebase time,
- PkInterval period);
-
-int
pk_timer_schedule(PkTimer *timer,
- PkInterval interval,
- PkInterval period);
+ PkInterval interval);
int
pk_timer_cancel(PkTimer *timer);
diff --git a/pk/kernel/pk_init.c b/pk/kernel/pk_init.c
index 2ac06f33..0add4214 100644
--- a/pk/kernel/pk_init.c
+++ b/pk/kernel/pk_init.c
@@ -15,8 +15,46 @@
#include "pk_trace.h"
uint32_t __pk_timebase_frequency_hz;
-uint32_t __pk_timebase_frequency_khz;
-uint32_t __pk_timebase_frequency_mhz;
+
+/// The timebase frequency is passed into PK during initialization. It cannot
+/// be set statically because there is a requirement to support a frequency
+/// that can change from one IPL to the next. On the 405, scaling of time
+/// intervals is accomplished by doing a 32x32 bit multiplication which is
+/// supported by the ppc405 instruction set. PPE42 does not support 32x32 bit
+/// multiplication directly and some applications can not afford to use a
+/// function call to emulate the operation. Instead we scale the time
+/// interval by shifting the value X bits to the right and adding it to itself.
+/// This can scale the value by 2, 1.5, 1.25, 1.125, etc.
+///
+/// This is the right shift value.
+/// NOTE: shifting by 0 gives a 2x scale factor, shifting by 32 gives a 1x
+/// scale factor.
+uint8_t __pk_timebase_rshift = 32;
+
+void pk_set_timebase_rshift(uint32_t timebase_freq_hz)
+{
+ //Use 1.0 scale if less than halfway between 1.0 and 1.25
+ if(timebase_freq_hz <= (PK_BASE_FREQ_HZ + (PK_BASE_FREQ_HZ >> 3)))
+ {
+ __pk_timebase_rshift = 32;
+ }
+
+ //use 1.25 scale if less than halfway between 1.25 and 1.5
+ else if(timebase_freq_hz <= (PK_BASE_FREQ_HZ + (PK_BASE_FREQ_HZ >> 3) + (PK_BASE_FREQ_HZ >> 2)))
+ {
+ __pk_timebase_rshift = 2;
+ }
+ //use 1.5 scale if less than halfway between 1.5 and 2.0
+ else if(timebase_freq_hz <= (PK_BASE_FREQ_HZ + (PK_BASE_FREQ_HZ >> 2) + (PK_BASE_FREQ_HZ >> 1)))
+ {
+ __pk_timebase_rshift = 1;
+ }
+ //use 2.0 scale if greater than 1.5
+ else
+ {
+ __pk_timebase_rshift = 0;
+ }
+}
/// Initialize PK.
///
@@ -69,8 +107,6 @@ pk_initialize(PkAddress noncritical_stack,
}
__pk_timebase_frequency_hz = timebase_frequency_hz;
- __pk_timebase_frequency_khz = timebase_frequency_hz / 1000;
- __pk_timebase_frequency_mhz = timebase_frequency_hz / 1000000;
__pk_thread_machine_context_default = PK_THREAD_MACHINE_CONTEXT_DEFAULT;
@@ -98,12 +134,13 @@ 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,
- 0);
+ PK_TRACE_TIMER_PERIOD);
//set the trace timebase HZ
g_pk_trace_buf.hz = timebase_frequency_hz;
+ pk_set_timebase_rshift(timebase_frequency_hz);
+
//set the timebase ajdustment for trace synchronization
pk_trace_set_timebase(initial_timebase);
diff --git a/pk/kernel/pk_semaphore_core.c b/pk/kernel/pk_semaphore_core.c
index 9079ff01..d5e6d30c 100644
--- a/pk/kernel/pk_semaphore_core.c
+++ b/pk/kernel/pk_semaphore_core.c
@@ -147,6 +147,8 @@ pk_semaphore_pend(PkSemaphore *semaphore,
PkThreadPriority priority;
PkThread *thread;
PkTimer *timer = 0;
+ PkInterval scaled_timeout = PK_INTERVAL_SCALE(timeout);
+
int rc = PK_OK;
if (PK_ERROR_CHECK_API) {
@@ -183,7 +185,7 @@ pk_semaphore_pend(PkSemaphore *semaphore,
if (timeout != PK_WAIT_FOREVER) {
timer = &(thread->timer);
- timer->timeout = pk_timebase_get() + timeout;
+ timer->timeout = pk_timebase_get() + scaled_timeout;
__pk_timer_schedule(timer);
thread->flags |= PK_THREAD_FLAG_TIMER_PEND;
}
diff --git a/pk/kernel/pk_thread_core.c b/pk/kernel/pk_thread_core.c
index 539b3321..56e083d4 100644
--- a/pk/kernel/pk_thread_core.c
+++ b/pk/kernel/pk_thread_core.c
@@ -646,7 +646,7 @@ pk_sleep_absolute(PkTimebase time)
int
pk_sleep(PkInterval interval)
{
- return pk_sleep_absolute(pk_timebase_get() + interval);
+ return pk_sleep_absolute(pk_timebase_get() + PK_INTERVAL_SCALE(interval));
}
diff --git a/pk/kernel/pk_timer_core.c b/pk/kernel/pk_timer_core.c
index abc83bad..bc90a3e7 100644
--- a/pk/kernel/pk_timer_core.c
+++ b/pk/kernel/pk_timer_core.c
@@ -43,8 +43,8 @@
/// also be rescheduled in place.
///
/// When a timeout occurs the event list is scanned from the beginning, and
-/// any event that has timed out is rescheduled if necessary (periodic events)
-/// and its callback is processed. Since event and callback processing take
+/// any event that has timed out has its callback processed.
+/// Since event and callback processing take
/// time, the list is potentially scanned multiple times until there are no
/// more timed-out events in the list.
///
@@ -200,23 +200,14 @@ __pk_timer_handler()
if (timer->timeout <= now) {
- // The timer timed out. It is removed from the queue unless
- // it is a peridic timer that needs to be rescheduled. We do
- // rescheduling here in the critical section to correctly
- // handle timers whose callbacks may cancel the timer. The
- // timer is rescheduled in absolute time.
+ // The timer timed out. It is removed from the queue.
//
// The callback may be made with interrupt preemption enabled
// or disabled. However to mitigate kernel interrupt latency
// we go ahead and open up to interrupts after the callback if
// the callback itself was not preemptible.
- if (timer->period == 0) {
- pk_deque_delete(timer_deque);
- } else {
- timer->timeout += timer->period;
- tq->next_timeout = MIN(timer->timeout, tq->next_timeout);
- }
+ pk_deque_delete(timer_deque);
callback = timer->callback;
if (callback) {
@@ -254,22 +245,15 @@ __pk_timer_handler()
}
-/// Schedule a timer in absolute time.
+/// Schedule a timer for an interval relative to the current time.
///
/// \param timer The PkTimer to schedule.
///
-/// \param timeout The timer will be scheduled to time out at this absolute
-/// time. Note that if the \a timeout is less than the current time then the
-/// timer will be scheduled at a minimum timeout in the future and the
-/// callback will be executed in an interrupt context.
-///
-/// \param period If non-0, then when the timer times out it will rescheduled
-/// to time out again at the absolute time equal to the last timeout time plus
-/// the \a period. By convention a \a period of 0 indicates a one-shot
-/// timer that is not rescheduled.
+/// \param interval The timer will be scheduled to time out at the current
+/// time (pk_timebase_get()) plus this \a interval.
///
/// Once created with pk_timer_create() a timer can be \e scheduled, which
-/// queues the timer in the kernel time queue. It is not an error to call
+/// queues the timer in the kernel time queue. It is not an error to call \c
/// pk_timer_schedule() on a timer that is already scheduled in the time
/// queue - the timer is simply rescheduled with the new characteristics.
///
@@ -284,23 +268,19 @@ __pk_timer_handler()
/// interrupt context.
int
-pk_timer_schedule_absolute(PkTimer *timer,
- PkTimebase timeout,
- PkInterval period)
-
+pk_timer_schedule(PkTimer *timer,
+ PkInterval interval)
{
PkMachineContext ctx;
+ PkTimebase timeout = pk_timebase_get() + PK_INTERVAL_SCALE(interval);
pk_critical_section_enter(&ctx);
if (PK_ERROR_CHECK_API) {
PK_ERROR_IF(timer == 0, PK_INVALID_TIMER_AT_SCHEDULE);
-// PK_ERROR_IF(__pk_kernel_context_critical_interrupt(),
-// PK_ILLEGAL_CONTEXT_TIMER);
}
timer->timeout = timeout;
- timer->period = period;
__pk_timer_schedule(timer);
pk_critical_section_exit(&ctx);
@@ -309,44 +289,6 @@ pk_timer_schedule_absolute(PkTimer *timer,
}
-/// Schedule a timer for an interval relative to the current time.
-///
-/// \param timer The PkTimer to schedule.
-///
-/// \param interval The timer will be scheduled to time out at the current
-/// time (pk_timebase_get()) plus this \a interval.
-///
-/// \param period If non-0, then when the timer times out it will rescheduled
-/// to time out again at the absolute time equal to the last timeout time plus
-/// the \a period. By convention a \a period of 0 indicates a one-shot
-/// timer that is not rescheduled.
-///
-/// Once created with pk_timer_create() a timer can be \e scheduled, which
-/// queues the timer in the kernel time queue. It is not an error to call \c
-/// pk_timer_schedule() on a timer that is already scheduled in the time
-/// queue - the timer is simply rescheduled with the new characteristics.
-///
-/// Return values other than PK_OK (0) are errors; see \ref pk_errors
-///
-/// \retval 0 Successful completion
-///
-/// \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,
- PkInterval interval,
- PkInterval period)
-{
- return pk_timer_schedule_absolute(timer,
- pk_timebase_get() + interval,
- period);
-}
-
-
/// Cancel (dequeue) a timer.
///
/// \param timer The PkTimer to cancel.
diff --git a/pk/kernel/pk_timer_init.c b/pk/kernel/pk_timer_init.c
index 6851f3d3..9b8f0e1d 100644
--- a/pk/kernel/pk_timer_init.c
+++ b/pk/kernel/pk_timer_init.c
@@ -27,7 +27,6 @@ _pk_timer_create(PkTimer *timer,
pk_deque_element_create((PkDeque*)timer);
timer->timeout = 0;
- timer->period = 0;
timer->callback = callback;
timer->arg = arg;
timer->options = options;
diff --git a/pk/trace/pk_trace_core.c b/pk/trace/pk_trace_core.c
index 90a1a36c..007337bc 100644
--- a/pk/trace/pk_trace_core.c
+++ b/pk/trace/pk_trace_core.c
@@ -26,7 +26,6 @@ PkTimer g_pk_trace_timer = {
.deque.next = 0,
.deque.previous = 0,
.timeout = 0,
- .period = 0,
.callback = pk_trace_timer_callback,
.arg = 0,
.options = PK_TIMER_CALLBACK_PREEMPTIBLE,
@@ -102,8 +101,7 @@ void pk_trace_timer_callback(void* arg)
// restart the timer
pk_timer_schedule(&g_pk_trace_timer,
- PK_TRACE_TIMER_PERIOD,
- 0);
+ PK_TRACE_TIMER_PERIOD);
}
// Use this function to synchronize the timebase between multiple PPEs.
OpenPOWER on IntegriCloud