@@ -178,6 +178,16 @@ int odp_queue_deq_multi(odp_queue_t queue, odp_buffer_t buf[], int num);
*/
odp_queue_type_t odp_queue_type(odp_queue_t queue);
+/**
+ * Queue schedule type
+ *
+ * @param queue Queue handle
+ *
+ * @return Queue schedule synchronisation type
+ */
+odp_schedule_sync_t odp_queue_sched_type(odp_queue_t queue);
+
+
#ifdef __cplusplus
}
#endif
@@ -19,94 +19,122 @@ extern "C" {
#endif
+#include <odp_std_types.h>
#include <odp_buffer.h>
#include <odp_queue.h>
+#define ODP_SCHED_WAIT 0 /**< Wait infinitely */
+#define ODP_SCHED_NO_WAIT 1 /**< Do not wait */
+
+
/**
- * Schedule once
+ * Schedule wait time
*
- * Schedules all queues created with ODP_QUEUE_TYPE_SCHED type. Returns
- * next highest priority buffer which is available for the calling thread.
- * Outputs the source queue. Returns ODP_BUFFER_INVALID if no buffer
- * was available.
+ * Converts nanoseconds to wait values for other schedule functions.
*
- * @param from Queue pointer for outputing the queue where the buffer was
- * dequeued from. Ignored if NULL.
+ * @param ns Nanoseconds
*
- * @return Next highest priority buffer, or ODP_BUFFER_INVALID
+ * @return Value for the wait parameter in schedule functions
*/
-odp_buffer_t odp_schedule_once(odp_queue_t *from);
+uint64_t odp_schedule_wait_time(uint64_t ns);
/**
* Schedule
*
- * Like odp_schedule_once(), but blocks until a buffer is available.
+ * Schedules all queues created with ODP_QUEUE_TYPE_SCHED type. Returns
+ * next highest priority buffer which is available for the calling thread.
+ * Outputs the source queue of the buffer. If there's no buffer available, waits
+ * for a buffer according to the wait parameter setting. Returns
+ * ODP_BUFFER_INVALID if reaches end of the wait period.
*
- * @param from Queue pointer for outputing the queue where the buffer was
- * dequeued from. Ignored if NULL.
+ * @param from Output parameter for the source queue (where the buffer was
+ * dequeued from). Ignored if NULL.
+ * @param wait Minimum time to wait for a buffer. Waits infinitely, if set to
+ * ODP_SCHED_WAIT. Does not wait, if set to ODP_SCHED_NO_WAIT.
+ * Use odp_schedule_wait_time() to convert time to other wait
+ * values.
*
- * @return Next highest priority buffer
+ * @return Next highest priority buffer, or ODP_BUFFER_INVALID
*/
-odp_buffer_t odp_schedule(odp_queue_t *from);
+odp_buffer_t odp_schedule(odp_queue_t *from, uint64_t wait);
/**
- * Schedule, non-blocking
+ * Schedule one buffer
+ *
+ * Like odp_schedule(), but is quaranteed to schedule only one buffer at a time.
+ * Each call will perform global scheduling and will reserve one buffer per
+ * thread in maximum. When called after other schedule functions, returns
+ * locally stored buffers (if any) first, and then continues in the global
+ * scheduling mode.
*
- * Like odp_schedule(), but returns after 'n' empty schedule rounds.
+ * This function optimises priority scheduling (over throughput).
*
- * @param from Queue pointer for outputing the queue where the buffer was
- * dequeued from. Ignored if NULL.
- * @param n Number of empty schedule rounds before returning
- * ODP_BUFFER_INVALID
+ * User can exit the schedule loop without first calling odp_schedule_pause().
+ *
+ * @param from Output parameter for the source queue (where the buffer was
+ * dequeued from). Ignored if NULL.
+ * @param wait Minimum time to wait for a buffer. Waits infinitely, if set to
+ * ODP_SCHED_WAIT. Does not wait, if set to ODP_SCHED_NO_WAIT.
+ * Use odp_schedule_wait_time() to convert time to other wait
+ * values.
*
* @return Next highest priority buffer, or ODP_BUFFER_INVALID
*/
-odp_buffer_t odp_schedule_n(odp_queue_t *from, unsigned int n);
+odp_buffer_t odp_schedule_one(odp_queue_t *from, uint64_t wait);
+
/**
- * Schedule, multiple buffers
+ * Schedule multiple buffers
*
* Like odp_schedule(), but returns multiple buffers from a queue.
*
- * @param from Queue pointer for outputing the queue where the buffers were
- * dequeued from. Ignored if NULL.
+ * @param from Output parameter for the source queue (where the buffer was
+ * dequeued from). Ignored if NULL.
+ * @param wait Minimum time to wait for a buffer. Waits infinitely, if set to
+ * ODP_SCHED_WAIT. Does not wait, if set to ODP_SCHED_NO_WAIT.
+ * Use odp_schedule_wait_time() to convert time to other wait
+ * values.
* @param out_buf Buffer array for output
* @param num Maximum number of buffers to output
*
* @return Number of buffers outputed (0 ... num)
*/
-int odp_schedule_multi(odp_queue_t *from, odp_buffer_t out_buf[],
+int odp_schedule_multi(odp_queue_t *from, uint64_t wait, odp_buffer_t out_buf[],
unsigned int num);
/**
- * Schedule, multiple buffers, non-blocking
+ * Pause scheduling
*
- * Like odp_schedule_multi(), but returns after 'n' empty schedule rounds.
- *
- * @param from Queue pointer for outputing the queue where the buffers were
- * dequeued from. Ignored if NULL.
- * @param out_buf Buffer array for output
- * @param num Maximum number of buffers to output
- * @param n Number of empty schedule rounds before returning
- * ODP_BUFFER_INVALID
+ * Pause global scheduling for this thread. After this call, all schedule calls
+ * will return only locally reserved buffers (if any). User can exit the
+ * schedule loop only after the schedule function indicates that there's no more
+ * buffers (no more locally reserved buffers).
*
- * @return Number of buffers outputed (0 ... num)
+ * Must be used with odp_schedule() and odp_schedule_multi() before exiting (or
+ * stalling) the schedule loop.
*/
-int odp_schedule_multi_n(odp_queue_t *from, odp_buffer_t out_buf[],
- unsigned int num, unsigned int n);
+void odp_schedule_pause(void);
/**
- * Number of scheduling priorities
+ * Resume scheduling
*
- * @return Number of scheduling priorities
+ * Resume global scheduling for this thread. After this call, all schedule calls
+ * will schedule normally (perform global scheduling).
*/
-int odp_schedule_num_prio(void);
+void odp_schedule_resume(void);
/**
* Release currently hold atomic context
*/
-void odp_schedule_release_atomic_context(void);
+void odp_schedule_release_atomic(void);
+
+/**
+ * Number of scheduling priorities
+ *
+ * @return Number of scheduling priorities
+ */
+int odp_schedule_num_prio(void);
#ifdef __cplusplus
@@ -114,5 +142,3 @@ void odp_schedule_release_atomic_context(void);
#endif
#endif
-
-
@@ -51,6 +51,15 @@ uint64_t odp_time_diff_cycles(uint64_t t1, uint64_t t2);
uint64_t odp_time_cycles_to_ns(uint64_t cycles);
+/**
+ * Convert nanoseconds to CPU cycles
+ *
+ * @param ns Time in nanoseconds
+ *
+ * @return Time in CPU cycles
+ */
+uint64_t odp_time_ns_to_cycles(uint64_t ns);
+
#ifdef __cplusplus
}
#endif
@@ -132,6 +132,15 @@ odp_queue_type_t odp_queue_type(odp_queue_t handle)
return queue->s.type;
}
+odp_schedule_sync_t odp_queue_sched_type(odp_queue_t handle)
+{
+ queue_entry_t *queue;
+
+ queue = queue_to_qentry(handle);
+
+ return queue->s.param.sched.sync;
+}
+
odp_queue_t odp_queue_create(const char *name, odp_queue_type_t type,
odp_queue_param_t *param)
{
@@ -15,6 +15,7 @@
#include <odp_config.h>
#include <odp_debug.h>
#include <odp_thread.h>
+#include <odp_time.h>
#include <odp_spinlock.h>
#include <odp_hints.h>
@@ -60,6 +61,7 @@ typedef struct {
int num;
int index;
odp_queue_t queue;
+ int pause;
} sched_local_t;
@@ -154,6 +156,7 @@ int odp_schedule_init_local(void)
sched_local.num = 0;
sched_local.index = 0;
sched_local.queue = ODP_QUEUE_INVALID;
+ sched_local.pause = 0;
return 0;
}
@@ -197,7 +200,7 @@ void odp_schedule_queue(odp_queue_t queue, int prio)
}
-void odp_schedule_release_atomic_context(void)
+void odp_schedule_release_atomic(void)
{
if (sched_local.pri_queue != ODP_QUEUE_INVALID &&
sched_local.num == 0) {
@@ -223,13 +226,14 @@ static inline int copy_bufs(odp_buffer_t out_buf[], unsigned int max)
return i;
}
+
/*
* Schedule queues
*
* TODO: SYNC_ORDERED not implemented yet
*/
static int schedule(odp_queue_t *out_queue, odp_buffer_t out_buf[],
- unsigned int max_num)
+ unsigned int max_num, unsigned int max_deq)
{
int i, j;
int thr;
@@ -244,7 +248,10 @@ static int schedule(odp_queue_t *out_queue, odp_buffer_t out_buf[],
return ret;
}
- odp_schedule_release_atomic_context();
+ odp_schedule_release_atomic();
+
+ if (odp_unlikely(sched_local.pause))
+ return 0;
thr = odp_thread_id();
@@ -279,7 +286,7 @@ static int schedule(odp_queue_t *out_queue, odp_buffer_t out_buf[],
num = odp_queue_deq_multi(queue,
sched_local.buf,
- MAX_DEQ);
+ max_deq);
if (num == 0) {
/* Remove empty queue from scheduling,
@@ -320,73 +327,92 @@ static int schedule(odp_queue_t *out_queue, odp_buffer_t out_buf[],
}
-odp_buffer_t odp_schedule_once(odp_queue_t *out_queue)
+static int schedule_loop(odp_queue_t *out_queue, uint64_t wait,
+ odp_buffer_t out_buf[],
+ unsigned int max_num, unsigned int max_deq)
{
- odp_buffer_t buf = ODP_BUFFER_INVALID;
+ uint64_t start_cycle, cycle, diff;
+ int ret;
- schedule(out_queue, &buf, 1);
+ start_cycle = 0;
- return buf;
+ while (1) {
+ ret = schedule(out_queue, out_buf, max_num, max_deq);
+
+ if (ret)
+ break;
+
+ if (wait == ODP_SCHED_WAIT)
+ continue;
+
+ if (wait == ODP_SCHED_NO_WAIT)
+ break;
+
+ if (start_cycle == 0) {
+ start_cycle = odp_time_get_cycles();
+ continue;
+ }
+
+ cycle = odp_time_get_cycles();
+ diff = odp_time_diff_cycles(start_cycle, cycle);
+
+ if (wait < diff)
+ break;
+ }
+
+ return ret;
}
-odp_buffer_t odp_schedule(odp_queue_t *out_queue)
+odp_buffer_t odp_schedule(odp_queue_t *out_queue, uint64_t wait)
{
odp_buffer_t buf;
- int ret;
- while (1) {
- ret = schedule(out_queue, &buf, 1);
+ buf = ODP_BUFFER_INVALID;
- if (ret)
- return buf;
- }
+ schedule_loop(out_queue, wait, &buf, 1, MAX_DEQ);
+
+ return buf;
}
-odp_buffer_t odp_schedule_n(odp_queue_t *out_queue, unsigned int n)
+odp_buffer_t odp_schedule_one(odp_queue_t *out_queue, uint64_t wait)
{
odp_buffer_t buf;
- int ret;
- while (n--) {
- ret = schedule(out_queue, &buf, 1);
+ buf = ODP_BUFFER_INVALID;
- if (ret)
- return buf;
- }
+ schedule_loop(out_queue, wait, &buf, 1, 1);
- return ODP_BUFFER_INVALID;
+ return buf;
}
-int odp_schedule_multi(odp_queue_t *out_queue, odp_buffer_t out_buf[],
- unsigned int num)
+int odp_schedule_multi(odp_queue_t *out_queue, uint64_t wait,
+ odp_buffer_t out_buf[], unsigned int num)
{
- int ret;
+ return schedule_loop(out_queue, wait, out_buf, num, MAX_DEQ);
+}
- while (1) {
- ret = schedule(out_queue, out_buf, num);
- if (ret)
- return ret;
- }
+void odp_schedule_pause(void)
+{
+ sched_local.pause = 1;
}
-int odp_schedule_multi_n(odp_queue_t *out_queue, odp_buffer_t out_buf[],
- unsigned int num, unsigned int n)
+void odp_schedule_resume(void)
{
- int ret;
+ sched_local.pause = 0;
+}
- while (n--) {
- ret = schedule(out_queue, out_buf, num);
- if (ret)
- return ret;
- }
+uint64_t odp_schedule_wait_time(uint64_t ns)
+{
+ if (ns <= ODP_SCHED_NO_WAIT)
+ ns = ODP_SCHED_NO_WAIT + 1;
- return 0;
+ return odp_time_ns_to_cycles(ns);
}
@@ -9,6 +9,8 @@
#include <odp_system_info.h>
#include <odp_debug.h>
+#define GIGA 1000000000
+
#if defined __x86_64__ || defined __i386__
uint64_t odp_time_get_cycles(void)
@@ -66,7 +68,7 @@ uint64_t odp_time_get_cycles(void)
ns = (uint64_t) time.tv_nsec;
cycles = sec * hz;
- cycles += (ns * hz) / 1000000000;
+ cycles += (ns * hz) / GIGA;
return cycles;
}
@@ -85,8 +87,19 @@ uint64_t odp_time_cycles_to_ns(uint64_t cycles)
{
uint64_t hz = odp_sys_cpu_hz();
- if (cycles > (UINT64_MAX / 1000000000))
- return 1000000000*(cycles/hz);
+ if (cycles > (UINT64_MAX / GIGA))
+ return (cycles/hz)*GIGA;
+
+ return (cycles*GIGA)/hz;
+}
+
+
+uint64_t odp_time_ns_to_cycles(uint64_t ns)
+{
+ uint64_t hz = odp_sys_cpu_hz();
+
+ if (ns > (UINT64_MAX / hz))
+ return (ns/GIGA)*hz;
- return (1000000000*cycles)/hz;
+ return (ns*hz)/GIGA;
}
@@ -9,6 +9,7 @@ all:
$(MAKE) -C example
$(MAKE) -C packet
$(MAKE) -C packet_netmap
+ $(MAKE) -C timer
.PHONY: clean
clean:
@@ -16,6 +17,7 @@ clean:
$(MAKE) -C example clean
$(MAKE) -C packet clean
$(MAKE) -C packet_netmap clean
+ $(MAKE) -C timer clean
.PHONY: install
install:
@@ -23,3 +25,4 @@ install:
$(MAKE) -C example install
$(MAKE) -C packet install
$(MAKE) -C packet_netmap install
+ $(MAKE) -C timer install
@@ -33,7 +33,6 @@
#define QUEUE_ROUNDS (512*1024) /**< Queue test rounds */
#define ALLOC_ROUNDS (1024*1024) /**< Alloc test rounds */
#define MULTI_BUFS_MAX 4 /**< Buffer burst size */
-#define SCHED_RETRY 100 /**< Schedule retries */
#define TEST_SEC 2 /**< Time test duration in sec */
/** Dummy message */
@@ -55,11 +54,6 @@ typedef struct {
static odp_barrier_t test_barrier;
-/* #define TEST_TIMEOUTS */
-#ifdef TEST_TIMEOUTS
-static odp_timer_t test_timer;
-#endif
-
/**
* @internal Clear all scheduled queues. Retry to be sure that all
* buffers have been scheduled.
@@ -69,7 +63,7 @@ static void clear_sched_queues(void)
odp_buffer_t buf;
while (1) {
- buf = odp_schedule_n(NULL, SCHED_RETRY);
+ buf = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
if (buf == ODP_BUFFER_INVALID)
break;
@@ -78,48 +72,76 @@ static void clear_sched_queues(void)
}
}
-#ifdef TEST_TIMEOUTS
-static void test_timeouts(int thr)
+
+static int create_queue(int thr, odp_buffer_pool_t msg_pool, int prio)
{
- uint64_t tick;
- odp_queue_t queue;
+ char name[] = "sched_XX_00";
odp_buffer_t buf;
- int num = 10;
+ odp_queue_t queue;
- ODP_DBG(" [%i] test_timeouts\n", thr);
+ buf = odp_buffer_alloc(msg_pool);
- queue = odp_queue_lookup("timer_queue");
+ if (!odp_buffer_is_valid(buf)) {
+ ODP_ERR(" [%i] msg_pool alloc failed\n", thr);
+ return -1;
+ }
- tick = odp_timer_current_tick(test_timer);
+ name[6] = '0' + prio/10;
+ name[7] = '0' + prio - 10*(prio/10);
- ODP_DBG(" [%i] current tick %"PRIu64"\n", thr, tick);
+ queue = odp_queue_lookup(name);
- tick += 100;
+ if (queue == ODP_QUEUE_INVALID) {
+ ODP_ERR(" [%i] Queue %s lookup failed.\n", thr, name);
+ return -1;
+ }
- odp_timer_absolute_tmo(test_timer, tick,
- queue, ODP_BUFFER_INVALID);
+ if (odp_queue_enq(queue, buf)) {
+ ODP_ERR(" [%i] Queue enqueue failed.\n", thr);
+ return -1;
+ }
+ return 0;
+}
- while (1) {
- while ((buf = odp_queue_deq(queue) == ODP_BUFFER_INVALID))
- ;
+static int create_queues(int thr, odp_buffer_pool_t msg_pool, int prio)
+{
+ char name[] = "sched_XX_YY";
+ odp_buffer_t buf;
+ odp_queue_t queue;
+ int i;
- /* ODP_DBG(" [%i] timeout\n", thr); */
+ name[6] = '0' + prio/10;
+ name[7] = '0' + prio - 10*(prio/10);
- odp_buffer_free(buf);
+ /* Alloc and enqueue a buffer per queue */
+ for (i = 0; i < QUEUES_PER_PRIO; i++) {
+ name[9] = '0' + i/10;
+ name[10] = '0' + i - 10*(i/10);
- num--;
+ queue = odp_queue_lookup(name);
- if (num == 0)
- break;
+ if (queue == ODP_QUEUE_INVALID) {
+ ODP_ERR(" [%i] Queue %s lookup failed.\n", thr, name);
+ return -1;
+ }
+
+ buf = odp_buffer_alloc(msg_pool);
- tick = odp_timer_current_tick(test_timer) + 100;
+ if (!odp_buffer_is_valid(buf)) {
+ ODP_ERR(" [%i] msg_pool alloc failed\n", thr);
+ return -1;
+ }
- odp_timer_absolute_tmo(test_timer, tick,
- queue, ODP_BUFFER_INVALID);
+ if (odp_queue_enq(queue, buf)) {
+ ODP_ERR(" [%i] Queue enqueue failed.\n", thr);
+ return -1;
+ }
}
+
+ return 0;
}
-#endif
+
/**
* @internal Test single buffer alloc and free
@@ -152,7 +174,7 @@ static int test_alloc_single(int thr, odp_buffer_pool_t pool)
cycles = odp_time_diff_cycles(t1, t2);
ns = odp_time_cycles_to_ns(cycles);
- printf(" [%i] alloc_sng alloc+free %"PRIu64" cycles, %"PRIu64" ns\n",
+ printf(" [%i] alloc_sng alloc+free %"PRIu64" cycles, %"PRIu64" ns\n",
thr, cycles/ALLOC_ROUNDS, ns/ALLOC_ROUNDS);
return 0;
@@ -258,7 +280,7 @@ static int test_poll_queue(int thr, odp_buffer_pool_t msg_pool)
cycles = odp_time_diff_cycles(t1, t2);
ns = odp_time_cycles_to_ns(cycles);
- printf(" [%i] poll_queue enq+deq %"PRIu64" cycles, %"PRIu64" ns\n",
+ printf(" [%i] poll_queue enq+deq %"PRIu64" cycles, %"PRIu64" ns\n",
thr, cycles/QUEUE_ROUNDS, ns/QUEUE_ROUNDS);
odp_buffer_free(buf);
@@ -266,7 +288,7 @@ static int test_poll_queue(int thr, odp_buffer_pool_t msg_pool)
}
/**
- * @internal Test scheduling of a single queue
+ * @internal Test scheduling of a single queue - with odp_schedule_one()
*
* Enqueue a buffer to the shared queue. Schedule and enqueue the received
* buffer back into the queue.
@@ -278,47 +300,22 @@ static int test_poll_queue(int thr, odp_buffer_pool_t msg_pool)
*
* @return 0 if successful
*/
-static int test_sched_single_queue(const char *str, int thr,
- odp_buffer_pool_t msg_pool, int prio)
+static int test_schedule_one_single(const char *str, int thr,
+ odp_buffer_pool_t msg_pool, int prio)
{
odp_buffer_t buf;
odp_queue_t queue;
uint64_t t1, t2, cycles, ns;
uint32_t i;
uint32_t tot = 0;
- char name[] = "sched_XX_00";
-
- buf = odp_buffer_alloc(msg_pool);
-
- if (!odp_buffer_is_valid(buf)) {
- ODP_ERR(" [%i] msg_pool alloc failed\n", thr);
- return -1;
- }
-
- name[6] = '0' + prio/10;
- name[7] = '0' + prio - 10*(prio/10);
- queue = odp_queue_lookup(name);
-
- if (queue == ODP_QUEUE_INVALID) {
- ODP_ERR(" [%i] Queue %s lookup failed.\n", thr, name);
+ if (create_queue(thr, msg_pool, prio))
return -1;
- }
-
- /* printf(" [%i] prio %i queue %s\n", thr, prio, name); */
-
- if (odp_queue_enq(queue, buf)) {
- ODP_ERR(" [%i] Queue enqueue failed.\n", thr);
- return -1;
- }
t1 = odp_time_get_cycles();
for (i = 0; i < QUEUE_ROUNDS; i++) {
- buf = odp_schedule_n(NULL, SCHED_RETRY);
-
- if (buf == ODP_BUFFER_INVALID)
- break;
+ buf = odp_schedule_one(&queue, ODP_SCHED_WAIT);
if (odp_queue_enq(queue, buf)) {
ODP_ERR(" [%i] Queue enqueue failed.\n", thr);
@@ -326,6 +323,9 @@ static int test_sched_single_queue(const char *str, int thr,
}
}
+ if (odp_queue_sched_type(queue) == ODP_SCHED_SYNC_ATOMIC)
+ odp_schedule_release_atomic();
+
t2 = odp_time_get_cycles();
cycles = odp_time_diff_cycles(t1, t2);
ns = odp_time_cycles_to_ns(cycles);
@@ -349,7 +349,7 @@ static int test_sched_single_queue(const char *str, int thr,
}
/**
- * @internal Test scheduling of multiple queues
+ * @internal Test scheduling of multiple queues - with odp_schedule_one()
*
* Enqueue a buffer to each queue. Schedule and enqueue the received
* buffer back into the queue it came from.
@@ -361,7 +361,7 @@ static int test_sched_single_queue(const char *str, int thr,
*
* @return 0 if successful
*/
-static int test_sched_multi_queue(const char *str, int thr,
+static int test_schedule_one_many(const char *str, int thr,
odp_buffer_pool_t msg_pool, int prio)
{
odp_buffer_t buf;
@@ -371,29 +371,95 @@ static int test_sched_multi_queue(const char *str, int thr,
uint64_t cycles, ns;
uint32_t i;
uint32_t tot = 0;
- char name[] = "sched_XX_YY";
- name[6] = '0' + prio/10;
- name[7] = '0' + prio - 10*(prio/10);
+ if (create_queues(thr, msg_pool, prio))
+ return -1;
- /* Alloc and enqueue a buffer per queue */
- for (i = 0; i < QUEUES_PER_PRIO; i++) {
- name[9] = '0' + i/10;
- name[10] = '0' + i - 10*(i/10);
+ /* Start sched-enq loop */
+ t1 = odp_time_get_cycles();
- queue = odp_queue_lookup(name);
+ for (i = 0; i < QUEUE_ROUNDS; i++) {
+ buf = odp_schedule_one(&queue, ODP_SCHED_WAIT);
- if (queue == ODP_QUEUE_INVALID) {
- ODP_ERR(" [%i] Queue %s lookup failed.\n", thr, name);
+ if (odp_queue_enq(queue, buf)) {
+ ODP_ERR(" [%i] Queue enqueue failed.\n", thr);
return -1;
}
+ }
- buf = odp_buffer_alloc(msg_pool);
+ if (odp_queue_sched_type(queue) == ODP_SCHED_SYNC_ATOMIC)
+ odp_schedule_release_atomic();
- if (!odp_buffer_is_valid(buf)) {
- ODP_ERR(" [%i] msg_pool alloc failed\n", thr);
+ t2 = odp_time_get_cycles();
+ cycles = odp_time_diff_cycles(t1, t2);
+ ns = odp_time_cycles_to_ns(cycles);
+ tot = i;
+
+ odp_barrier_sync(&test_barrier);
+ clear_sched_queues();
+
+ if (tot) {
+ cycles = cycles/tot;
+ ns = ns/tot;
+ } else {
+ cycles = 0;
+ ns = 0;
+ }
+
+ printf(" [%i] %s enq+deq %"PRIu64" cycles, %"PRIu64" ns\n",
+ thr, str, cycles, ns);
+
+ return 0;
+}
+
+/**
+ * @internal Test scheduling of a single queue - with odp_schedule()
+ *
+ * Enqueue a buffer to the shared queue. Schedule and enqueue the received
+ * buffer back into the queue.
+ *
+ * @param str Test case name string
+ * @param thr Thread
+ * @param msg_pool Buffer pool
+ * @param prio Priority
+ *
+ * @return 0 if successful
+ */
+static int test_schedule_single(const char *str, int thr,
+ odp_buffer_pool_t msg_pool, int prio)
+{
+ odp_buffer_t buf;
+ odp_queue_t queue;
+ uint64_t t1, t2, cycles, ns;
+ uint32_t i;
+ uint32_t tot = 0;
+
+ if (create_queue(thr, msg_pool, prio))
+ return -1;
+
+ t1 = odp_time_get_cycles();
+
+ for (i = 0; i < QUEUE_ROUNDS; i++) {
+ buf = odp_schedule(&queue, ODP_SCHED_WAIT);
+
+ if (odp_queue_enq(queue, buf)) {
+ ODP_ERR(" [%i] Queue enqueue failed.\n", thr);
return -1;
}
+ }
+
+ /* Clear possible locally stored buffers */
+ odp_schedule_pause();
+
+ tot = i;
+
+ while (1) {
+ buf = odp_schedule(&queue, ODP_SCHED_NO_WAIT);
+
+ if (buf == ODP_BUFFER_INVALID)
+ break;
+
+ tot++;
if (odp_queue_enq(queue, buf)) {
ODP_ERR(" [%i] Queue enqueue failed.\n", thr);
@@ -401,25 +467,93 @@ static int test_sched_multi_queue(const char *str, int thr,
}
}
+ odp_schedule_resume();
+
+ t2 = odp_time_get_cycles();
+ cycles = odp_time_diff_cycles(t1, t2);
+ ns = odp_time_cycles_to_ns(cycles);
+
+ odp_barrier_sync(&test_barrier);
+ clear_sched_queues();
+
+ if (tot) {
+ cycles = cycles/tot;
+ ns = ns/tot;
+ } else {
+ cycles = 0;
+ ns = 0;
+ }
+
+ printf(" [%i] %s enq+deq %"PRIu64" cycles, %"PRIu64" ns\n",
+ thr, str, cycles, ns);
+
+ return 0;
+}
+
+
+/**
+ * @internal Test scheduling of multiple queues - with odp_schedule()
+ *
+ * Enqueue a buffer to each queue. Schedule and enqueue the received
+ * buffer back into the queue it came from.
+ *
+ * @param str Test case name string
+ * @param thr Thread
+ * @param msg_pool Buffer pool
+ * @param prio Priority
+ *
+ * @return 0 if successful
+ */
+static int test_schedule_many(const char *str, int thr,
+ odp_buffer_pool_t msg_pool, int prio)
+{
+ odp_buffer_t buf;
+ odp_queue_t queue;
+ uint64_t t1 = 0;
+ uint64_t t2 = 0;
+ uint64_t cycles, ns;
+ uint32_t i;
+ uint32_t tot = 0;
+
+ if (create_queues(thr, msg_pool, prio))
+ return -1;
+
/* Start sched-enq loop */
t1 = odp_time_get_cycles();
for (i = 0; i < QUEUE_ROUNDS; i++) {
- buf = odp_schedule_n(&queue, SCHED_RETRY);
+ buf = odp_schedule(&queue, ODP_SCHED_WAIT);
+
+ if (odp_queue_enq(queue, buf)) {
+ ODP_ERR(" [%i] Queue enqueue failed.\n", thr);
+ return -1;
+ }
+ }
+
+ /* Clear possible locally stored buffers */
+ odp_schedule_pause();
+
+ tot = i;
+
+ while (1) {
+ buf = odp_schedule(&queue, ODP_SCHED_NO_WAIT);
if (buf == ODP_BUFFER_INVALID)
break;
+ tot++;
+
if (odp_queue_enq(queue, buf)) {
ODP_ERR(" [%i] Queue enqueue failed.\n", thr);
return -1;
}
}
+ odp_schedule_resume();
+
t2 = odp_time_get_cycles();
cycles = odp_time_diff_cycles(t1, t2);
ns = odp_time_cycles_to_ns(cycles);
- tot = i;
odp_barrier_sync(&test_barrier);
clear_sched_queues();
@@ -448,8 +582,8 @@ static int test_sched_multi_queue(const char *str, int thr,
*
* @return 0 if successful
*/
-static int test_sched_multi_queue_m(const char *str, int thr,
- odp_buffer_pool_t msg_pool, int prio)
+static int test_schedule_multi(const char *str, int thr,
+ odp_buffer_pool_t msg_pool, int prio)
{
odp_buffer_t buf[MULTI_BUFS_MAX];
odp_queue_t queue;
@@ -457,6 +591,7 @@ static int test_sched_multi_queue_m(const char *str, int thr,
uint64_t t2 = 0;
uint64_t cycles, ns;
int i, j;
+ int num;
uint32_t tot = 0;
char name[] = "sched_XX_YY";
@@ -494,10 +629,23 @@ static int test_sched_multi_queue_m(const char *str, int thr,
t1 = odp_time_get_cycles();
for (i = 0; i < QUEUE_ROUNDS; i++) {
- int num;
+ num = odp_schedule_multi(&queue, ODP_SCHED_WAIT, buf,
+ MULTI_BUFS_MAX);
+
+ tot += num;
- num = odp_schedule_multi_n(&queue, buf,
- MULTI_BUFS_MAX, SCHED_RETRY);
+ if (odp_queue_enq_multi(queue, buf, num)) {
+ ODP_ERR(" [%i] Queue enqueue failed.\n", thr);
+ return -1;
+ }
+ }
+
+ /* Clear possible locally stored buffers */
+ odp_schedule_pause();
+
+ while (1) {
+ num = odp_schedule_multi(&queue, ODP_SCHED_NO_WAIT, buf,
+ MULTI_BUFS_MAX);
if (num == 0)
break;
@@ -510,6 +658,9 @@ static int test_sched_multi_queue_m(const char *str, int thr,
}
}
+ odp_schedule_resume();
+
+
t2 = odp_time_get_cycles();
cycles = odp_time_diff_cycles(t1, t2);
ns = odp_time_cycles_to_ns(cycles);
@@ -580,47 +731,70 @@ static void *run_thread(void *arg)
if (test_poll_queue(thr, msg_pool))
return NULL;
+ /* Low prio */
+
odp_barrier_sync(&test_barrier);
- if (test_sched_single_queue("sched_single_hi", thr, msg_pool,
- ODP_SCHED_PRIO_HIGHEST))
+ if (test_schedule_one_single("sched_one_s_lo", thr, msg_pool,
+ ODP_SCHED_PRIO_LOWEST))
return NULL;
odp_barrier_sync(&test_barrier);
- if (test_sched_single_queue("sched_single_lo", thr, msg_pool,
- ODP_SCHED_PRIO_LOWEST))
+ if (test_schedule_single("sched_____s_lo", thr, msg_pool,
+ ODP_SCHED_PRIO_LOWEST))
return NULL;
odp_barrier_sync(&test_barrier);
- if (test_sched_multi_queue("sched_multi_hi", thr, msg_pool,
- ODP_SCHED_PRIO_HIGHEST))
+ if (test_schedule_one_many("sched_one_m_lo", thr, msg_pool,
+ ODP_SCHED_PRIO_LOWEST))
return NULL;
odp_barrier_sync(&test_barrier);
- if (test_sched_multi_queue("sched_multi_lo", thr, msg_pool,
- ODP_SCHED_PRIO_LOWEST))
+ if (test_schedule_many("sched_____m_lo", thr, msg_pool,
+ ODP_SCHED_PRIO_LOWEST))
return NULL;
odp_barrier_sync(&test_barrier);
- if (test_sched_multi_queue_m("sched_multi_hi_m", thr, msg_pool,
+ if (test_schedule_multi("sched_multi_lo", thr, msg_pool,
+ ODP_SCHED_PRIO_LOWEST))
+ return NULL;
+
+ /* High prio */
+
+ odp_barrier_sync(&test_barrier);
+
+ if (test_schedule_one_single("sched_one_s_hi", thr, msg_pool,
ODP_SCHED_PRIO_HIGHEST))
return NULL;
odp_barrier_sync(&test_barrier);
- if (test_sched_multi_queue_m("sched_multi_lo_m", thr, msg_pool,
- ODP_SCHED_PRIO_LOWEST))
+ if (test_schedule_single("sched_____s_hi", thr, msg_pool,
+ ODP_SCHED_PRIO_HIGHEST))
return NULL;
-#ifdef TEST_TIMEOUTS
odp_barrier_sync(&test_barrier);
- test_timeouts(thr);
-#endif
+ if (test_schedule_one_many("sched_one_m_hi", thr, msg_pool,
+ ODP_SCHED_PRIO_HIGHEST))
+ return NULL;
+
+ odp_barrier_sync(&test_barrier);
+
+ if (test_schedule_many("sched_____m_hi", thr, msg_pool,
+ ODP_SCHED_PRIO_HIGHEST))
+ return NULL;
+
+ odp_barrier_sync(&test_barrier);
+
+ if (test_schedule_multi("sched_multi_hi", thr, msg_pool,
+ ODP_SCHED_PRIO_HIGHEST))
+ return NULL;
+
printf("Thread %i exits\n", thr);
fflush(NULL);
@@ -836,22 +1010,6 @@ int main(int argc, char *argv[])
return -1;
}
-
-#ifdef TEST_TIMEOUTS
- /*
- * Create a queue for timer test
- */
- queue = odp_queue_create("timer_queue", ODP_QUEUE_TYPE_SCHED, NULL);
-
- if (queue == ODP_QUEUE_INVALID) {
- ODP_ERR("Timer queue create failed.\n");
- return -1;
- }
-
- test_timer = odp_timer_create("test_timer", pool,
- 1000000, 1000000, 1000000000000);
-#endif
-
/*
* Create queues for schedule test. QUEUES_PER_PRIO per priority.
*/
@@ -155,7 +155,7 @@ static void *pktio_queue_thread(void *arg)
#if 1
/* Use schedule to get buf from any input queue */
- buf = odp_schedule(NULL);
+ buf = odp_schedule(NULL, ODP_SCHED_WAIT);
#else
/* Always dequeue from the same input queue */
buf = odp_queue_deq(inq_def);
@@ -133,7 +133,7 @@ static void *pktio_queue_thread(void *arg)
pktio_info_t *pktio_info;
/* Use schedule to get buf from any input queue */
- buf = odp_schedule(NULL);
+ buf = odp_schedule(NULL, ODP_SCHED_WAIT);
pkt = odp_packet_from_buffer(buf);
new file mode 100644
@@ -0,0 +1,46 @@
+# Copyright (c) 2013, Linaro Limited
+# All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+
+ODP_ROOT = ../..
+ODP_APP = odp_timer_test
+
+include $(ODP_ROOT)/Makefile.inc
+include ../Makefile.inc
+
+.PHONY: default
+default: $(OBJ_DIR) $(ODP_APP)
+
+OBJS =
+OBJS += $(OBJ_DIR)/odp_timer_test.o
+
+DEPS = $(OBJS:.o=.d)
+
+-include $(DEPS)
+
+
+#
+# Compile rules
+#
+$(OBJ_DIR)/%.o: %.c
+ $(ECHO) Compiling $<
+ $(CC) -c -MD $(EXTRA_CFLAGS) $(CFLAGS) -o $@ $<
+
+#
+# Link rule
+#
+$(ODP_APP): $(ODP_LIB) $(OBJS)
+ $(ECHO) Linking $@
+ $(CC) $(LDFLAGS) $(OBJS) $(ODP_LIB) $(STD_LIBS) -o $@
+
+.PHONY: clean
+clean:
+ $(RMDIR) $(OBJ_DIR)
+ $(RM) $(ODP_APP)
+ $(MAKE) -C $(ODP_DIR) clean
+
+.PHONY: install
+install:
+ install -d $(DESTDIR)/share/odp
+ install -m 0755 $(ODP_APP) $(DESTDIR)/share/odp/
new file mode 100644
@@ -0,0 +1,335 @@
+/* Copyright (c) 2013, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/**
+ * @file
+ *
+ * @example odp_timer_test.c ODP timer example application
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+/* ODP main header */
+#include <odp.h>
+
+/* ODP helper for Linux apps */
+#include <helper/odp_linux.h>
+
+/* GNU lib C */
+#include <getopt.h>
+
+
+#define MAX_WORKERS 32 /**< Max worker threads */
+#define MSG_POOL_SIZE (4*1024*1024) /**< Message pool size */
+
+/** Dummy message */
+typedef struct {
+ int msg_id; /**< Message ID */
+ int seq; /**< Sequence number */
+} test_message_t;
+
+#define MSG_HELLO 1 /**< Hello */
+#define MSG_ACK 2 /**< Ack */
+
+/** Test arguments */
+typedef struct {
+ int core_count; /**< Core count*/
+} test_args_t;
+
+
+/** @private Barrier for test synchronisation */
+static odp_barrier_t test_barrier;
+
+/** @private Timer handle*/
+static odp_timer_t test_timer;
+
+
+/** @private test timeout */
+static void test_timeouts(int thr)
+{
+ uint64_t tick;
+ odp_queue_t queue;
+ odp_buffer_t buf;
+ int num = 10;
+
+ ODP_DBG(" [%i] test_timeouts\n", thr);
+
+ queue = odp_queue_lookup("timer_queue");
+
+ tick = odp_timer_current_tick(test_timer);
+
+ tick += 100;
+
+ odp_timer_absolute_tmo(test_timer, tick,
+ queue, ODP_BUFFER_INVALID);
+
+ ODP_DBG(" [%i] current tick %"PRIu64"\n", thr, tick);
+
+ while (1) {
+ buf = odp_schedule_one(&queue, ODP_SCHED_WAIT);
+
+ /* TODO: read tick from tmo metadata */
+ tick = odp_timer_current_tick(test_timer);
+
+ ODP_DBG(" [%i] timeout, tick %"PRIu64"\n", thr, tick);
+
+ odp_buffer_free(buf);
+
+ num--;
+
+ if (num == 0)
+ break;
+
+ tick += 100;
+
+ odp_timer_absolute_tmo(test_timer, tick,
+ queue, ODP_BUFFER_INVALID);
+ }
+
+ if (odp_queue_sched_type(queue) == ODP_SCHED_SYNC_ATOMIC)
+ odp_schedule_release_atomic();
+}
+
+
+/**
+ * @internal Worker thread
+ *
+ * @param arg Arguments
+ *
+ * @return NULL on failure
+ */
+static void *run_thread(void *arg)
+{
+ int thr;
+ odp_buffer_pool_t msg_pool;
+
+ thr = odp_thread_id();
+
+ printf("Thread %i starts on core %i\n", thr, odp_thread_core());
+
+ /*
+ * Test barriers back-to-back
+ */
+ odp_barrier_sync(&test_barrier);
+ odp_barrier_sync(&test_barrier);
+ odp_barrier_sync(&test_barrier);
+ odp_barrier_sync(&test_barrier);
+
+ /*
+ * Find the buffer pool
+ */
+ msg_pool = odp_buffer_pool_lookup("msg_pool");
+
+ if (msg_pool == ODP_BUFFER_POOL_INVALID) {
+ ODP_ERR(" [%i] msg_pool not found\n", thr);
+ return NULL;
+ }
+
+ odp_barrier_sync(&test_barrier);
+
+ test_timeouts(thr);
+
+
+ printf("Thread %i exits\n", thr);
+ fflush(NULL);
+ return arg;
+}
+
+
+/**
+ * @internal Print help
+ */
+static void print_usage(void)
+{
+ printf("\n\nUsage: ./odp_example [options]\n");
+ printf("Options:\n");
+ printf(" -c, --count <number> core count, core IDs start from 1\n");
+ printf(" -h, --help this help\n");
+ printf("\n\n");
+}
+
+
+/**
+ * @internal Parse arguments
+ *
+ * @param argc Argument count
+ * @param argv Argument vector
+ * @param args Test arguments
+ */
+static void parse_args(int argc, char *argv[], test_args_t *args)
+{
+ int opt;
+ int long_index;
+
+ static struct option longopts[] = {
+ {"count", required_argument, NULL, 'c'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ while (1) {
+ opt = getopt_long(argc, argv, "+c:h", longopts, &long_index);
+
+ if (opt == -1)
+ break; /* No more options */
+
+ switch (opt) {
+ case 'c':
+ args->core_count = atoi(optarg);
+ break;
+
+ case 'h':
+ print_usage();
+ exit(EXIT_SUCCESS);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+
+/**
+ * Test main function
+ */
+int main(int argc, char *argv[])
+{
+ odp_linux_pthread_t thread_tbl[MAX_WORKERS];
+ test_args_t args;
+ int thr_id;
+ int num_workers;
+ odp_buffer_pool_t pool;
+ void *pool_base;
+ odp_queue_t queue;
+ int first_core;
+ uint64_t cycles, ns;
+ odp_queue_param_t param;
+
+ printf("\nODP example starts\n");
+
+ memset(&args, 0, sizeof(args));
+ parse_args(argc, argv, &args);
+
+ memset(thread_tbl, 0, sizeof(thread_tbl));
+
+ if (odp_init_global()) {
+ printf("ODP global init failed.\n");
+ return -1;
+ }
+
+ printf("\n");
+ printf("ODP system info\n");
+ printf("---------------\n");
+ printf("ODP API version: %s\n", odp_version_api_str());
+ printf("CPU model: %s\n", odp_sys_cpu_model_str());
+ printf("CPU freq (hz): %"PRIu64"\n", odp_sys_cpu_hz());
+ printf("Cache line size: %i\n", odp_sys_cache_line_size());
+ printf("Max core count: %i\n", odp_sys_core_count());
+
+ printf("\n");
+
+ /* A worker thread per core */
+ num_workers = odp_sys_core_count();
+
+ if (args.core_count)
+ num_workers = args.core_count;
+
+ /* force to max core count */
+ if (num_workers > MAX_WORKERS)
+ num_workers = MAX_WORKERS;
+
+ printf("num worker threads: %i\n", num_workers);
+
+ /*
+ * By default core #0 runs Linux kernel background tasks.
+ * Start mapping thread from core #1
+ */
+ first_core = 1;
+
+ if (odp_sys_core_count() == 1)
+ first_core = 0;
+
+ printf("first core: %i\n", first_core);
+
+ /*
+ * Init this thread. It makes also ODP calls when
+ * setting up resources for worker threads.
+ */
+ thr_id = odp_thread_create(0);
+ odp_init_local(thr_id);
+
+ /*
+ * Create message pool
+ */
+ pool_base = odp_shm_reserve("msg_pool",
+ MSG_POOL_SIZE, ODP_CACHE_LINE_SIZE);
+
+ pool = odp_buffer_pool_create("msg_pool", pool_base, MSG_POOL_SIZE,
+ sizeof(test_message_t),
+ ODP_CACHE_LINE_SIZE, ODP_BUFFER_TYPE_RAW);
+
+ if (pool == ODP_BUFFER_POOL_INVALID) {
+ ODP_ERR("Pool create failed.\n");
+ return -1;
+ }
+
+ /*
+ * Create a queue for timer test
+ */
+ memset(¶m, 0, sizeof(param));
+ param.sched.prio = ODP_SCHED_PRIO_DEFAULT;
+ param.sched.sync = ODP_SCHED_SYNC_NONE;
+ param.sched.group = ODP_SCHED_GROUP_DEFAULT;
+
+ queue = odp_queue_create("timer_queue", ODP_QUEUE_TYPE_SCHED, ¶m);
+
+ if (queue == ODP_QUEUE_INVALID) {
+ ODP_ERR("Timer queue create failed.\n");
+ return -1;
+ }
+
+ test_timer = odp_timer_create("test_timer", pool,
+ 1000000, 1000000, 1000000000000);
+
+
+ odp_shm_print_all();
+
+ printf("CPU freq %"PRIu64" hz\n", odp_sys_cpu_hz());
+ printf("Cycles vs nanoseconds:\n");
+ ns = 0;
+ cycles = odp_time_ns_to_cycles(ns);
+
+ printf(" %12"PRIu64" ns -> %12"PRIu64" cycles\n", ns, cycles);
+ printf(" %12"PRIu64" cycles -> %12"PRIu64" ns\n", cycles,
+ odp_time_cycles_to_ns(cycles));
+
+ for (ns = 1; ns <= 100000000000; ns *= 10) {
+ cycles = odp_time_ns_to_cycles(ns);
+
+ printf(" %12"PRIu64" ns -> %12"PRIu64" cycles\n", ns,
+ cycles);
+ printf(" %12"PRIu64" cycles -> %12"PRIu64" ns\n", cycles,
+ odp_time_cycles_to_ns(cycles));
+ }
+
+ printf("\n");
+
+ /* Barrier to sync test case execution */
+ odp_barrier_init_count(&test_barrier, num_workers);
+
+ /* Create and launch worker threads */
+ odp_linux_pthread_create(thread_tbl, num_workers, first_core,
+ run_thread, NULL);
+
+ /* Wait for worker threads to exit */
+ odp_linux_pthread_join(thread_tbl, num_workers);
+
+ printf("ODP timer test complete\n\n");
+
+ return 0;
+}