From e987449c9fce2ec6210be3d8bad680d08d68c9dc Mon Sep 17 00:00:00 2001 From: Intel Date: Mon, 3 Jun 2013 00:00:00 +0000 Subject: [PATCH] timer: prefer TSC to HPET Signed-off-by: Intel --- app/test/autotest_test_funcs.py | 4 +- app/test/test.c | 12 ++ app/test/test_cycles.c | 10 +- app/test/test_mempool.c | 12 +- app/test/test_mempool_perf.c | 6 +- app/test/test_ring.c | 6 +- app/test/test_spinlock.c | 6 +- app/test/test_timer.c | 70 +++++-- config/defconfig_i686-default-linuxapp-gcc | 2 +- config/defconfig_i686-default-linuxapp-icc | 2 +- config/defconfig_x86_64-default-linuxapp-gcc | 2 +- config/defconfig_x86_64-default-linuxapp-icc | 2 +- examples/timer/main.c | 4 +- lib/librte_eal/common/include/eal_private.h | 9 +- lib/librte_eal/common/include/rte_cycles.h | 90 ++++++++- lib/librte_eal/linuxapp/eal/Makefile | 2 +- lib/librte_eal/linuxapp/eal/eal.c | 4 +- .../linuxapp/eal/{eal_hpet.c => eal_timer.c} | 190 ++++++++++++------ lib/librte_timer/rte_timer.c | 9 +- mk/exec-env/linuxapp/rte.vars.mk | 3 +- mk/rte.app.mk | 1 + 21 files changed, 335 insertions(+), 111 deletions(-) rename lib/librte_eal/linuxapp/eal/{eal_hpet.c => eal_timer.c} (63%) diff --git a/app/test/autotest_test_funcs.py b/app/test/autotest_test_funcs.py index ad124b05ee..61de366151 100644 --- a/app/test/autotest_test_funcs.py +++ b/app/test/autotest_test_funcs.py @@ -182,7 +182,7 @@ def timer_autotest(child, test_name): i = 0 child.sendline(test_name) - index = child.expect(["Start timer stress tests \(30 seconds\)", + index = child.expect(["Start timer stress tests \(20 seconds\)", "Test Failed", pexpect.TIMEOUT], timeout = 10) @@ -191,7 +191,7 @@ def timer_autotest(child, test_name): elif index == 2: return -1, "Fail [Timeout]" - index = child.expect(["Start timer basic tests \(30 seconds\)", + index = child.expect(["Start timer basic tests \(20 seconds\)", "Test Failed", pexpect.TIMEOUT], timeout = 40) diff --git a/app/test/test.c b/app/test/test.c index f0209b5fad..ee287b947a 100644 --- a/app/test/test.c +++ b/app/test/test.c @@ -52,10 +52,14 @@ #include #include #include +#include +#include #include #include "test.h" +#define RTE_LOGTYPE_APP RTE_LOGTYPE_USER1 + const char *prgname; /* to be set to argv[0] */ #ifndef RTE_EXEC_ENV_BAREMETAL @@ -115,6 +119,14 @@ main(int argc, char **argv) return do_recursive_call(); #endif +#ifdef RTE_LIBEAL_USE_HPET + if (rte_eal_hpet_init(1) < 0) +#endif + RTE_LOG(INFO, APP, + "HPET is not enabled, using TSC as default timer\n"); + + + cl = cmdline_stdin_new(main_ctx, "RTE>>"); if (cl == NULL) { return -1; diff --git a/app/test/test_cycles.c b/app/test/test_cycles.c index c5b5fa01e5..92d302bc51 100644 --- a/app/test/test_cycles.c +++ b/app/test/test_cycles.c @@ -60,14 +60,14 @@ test_cycles(void) { unsigned i; uint64_t start_cycles, cycles, prev_cycles; - uint64_t hz = rte_get_hpet_hz(); + uint64_t hz = rte_get_timer_hz(); uint64_t max_inc = (hz / 100); /* 10 ms max between 2 reads */ /* check that the timer is always incrementing */ - start_cycles = rte_get_hpet_cycles(); + start_cycles = rte_get_timer_cycles(); prev_cycles = start_cycles; for (i=0; i max_inc) { printf("increment too high or going backwards\n"); return -1; @@ -76,9 +76,9 @@ test_cycles(void) } /* check that waiting 1 second is precise */ - prev_cycles = rte_get_hpet_cycles(); + prev_cycles = rte_get_timer_cycles(); rte_delay_us(1000000); - cycles = rte_get_hpet_cycles(); + cycles = rte_get_timer_cycles(); if ((uint64_t)(cycles - prev_cycles) > (hz + max_inc)) { printf("delay_us is not accurate: too long\n"); diff --git a/app/test/test_mempool.c b/app/test/test_mempool.c index 1e9e4a07f6..8ec0428b2a 100644 --- a/app/test/test_mempool.c +++ b/app/test/test_mempool.c @@ -224,11 +224,11 @@ static int test_mempool_single_producer(void) unsigned int i; void *obj = NULL; uint64_t start_cycles, end_cycles; - uint64_t duration = rte_get_hpet_hz() * 8; + uint64_t duration = rte_get_timer_hz() * 8; - start_cycles = rte_get_hpet_cycles(); + start_cycles = rte_get_timer_cycles(); while (1) { - end_cycles = rte_get_hpet_cycles(); + end_cycles = rte_get_timer_cycles(); /* duration uses up, stop producing */ if (start_cycles + duration < end_cycles) break; @@ -264,11 +264,11 @@ static int test_mempool_single_consumer(void) unsigned int i; void * obj; uint64_t start_cycles, end_cycles; - uint64_t duration = rte_get_hpet_hz() * 5; + uint64_t duration = rte_get_timer_hz() * 5; - start_cycles = rte_get_hpet_cycles(); + start_cycles = rte_get_timer_cycles(); while (1) { - end_cycles = rte_get_hpet_cycles(); + end_cycles = rte_get_timer_cycles(); /* duration uses up, stop consuming */ if (start_cycles + duration < end_cycles) break; diff --git a/app/test/test_mempool_perf.c b/app/test/test_mempool_perf.c index bb01cb0faa..a9c17051c5 100644 --- a/app/test/test_mempool_perf.c +++ b/app/test/test_mempool_perf.c @@ -140,7 +140,7 @@ per_lcore_mempool_test(__attribute__((unused)) void *arg) unsigned lcore_id = rte_lcore_id(); int ret; uint64_t start_cycles, end_cycles; - uint64_t time_diff = 0, hz = rte_get_hpet_hz(); + uint64_t time_diff = 0, hz = rte_get_timer_hz(); /* n_get_bulk and n_put_bulk must be divisors of n_keep */ if (((n_keep / n_get_bulk) * n_get_bulk) != n_keep) @@ -154,7 +154,7 @@ per_lcore_mempool_test(__attribute__((unused)) void *arg) if (lcore_id != rte_get_master_lcore()) while (rte_atomic32_read(&synchro) == 0); - start_cycles = rte_get_hpet_cycles(); + start_cycles = rte_get_timer_cycles(); while (time_diff/hz < TIME_S) { for (i = 0; likely(i < (N/n_keep)); i++) { @@ -180,7 +180,7 @@ per_lcore_mempool_test(__attribute__((unused)) void *arg) idx += n_put_bulk; } } - end_cycles = rte_get_hpet_cycles(); + end_cycles = rte_get_timer_cycles(); time_diff = end_cycles - start_cycles; stats[lcore_id].enq_count += N; } diff --git a/app/test/test_ring.c b/app/test/test_ring.c index 2699e1ef13..de0489fcd5 100644 --- a/app/test/test_ring.c +++ b/app/test/test_ring.c @@ -477,7 +477,7 @@ do_one_ring_test(unsigned enq_core_count, unsigned deq_core_count, static int check_live_watermark_change(__attribute__((unused)) void *dummy) { - uint64_t hz = rte_get_hpet_hz(); + uint64_t hz = rte_get_timer_hz(); void *obj_table[MAX_BULK]; unsigned watermark, watermark_old = 16; uint64_t cur_time, end_time; @@ -487,7 +487,7 @@ check_live_watermark_change(__attribute__((unused)) void *dummy) /* init the object table */ memset(obj_table, 0, sizeof(obj_table)); - end_time = rte_get_hpet_cycles() + (hz * 2); + end_time = rte_get_timer_cycles() + (hz * 2); /* check that bulk and watermark are 4 and 32 (respectively) */ while (diff >= 0) { @@ -525,7 +525,7 @@ check_live_watermark_change(__attribute__((unused)) void *dummy) } } - cur_time = rte_get_hpet_cycles(); + cur_time = rte_get_timer_cycles(); diff = end_time - cur_time; } diff --git a/app/test/test_spinlock.c b/app/test/test_spinlock.c index 00ae587d8d..c345c72953 100644 --- a/app/test/test_spinlock.c +++ b/app/test/test_spinlock.c @@ -139,7 +139,7 @@ static int load_loop_fn(void *func_param) { uint64_t time_diff = 0, begin; - uint64_t hz = rte_get_hpet_hz(); + uint64_t hz = rte_get_timer_hz(); uint64_t lcount = 0; const int use_lock = *(int*)func_param; const unsigned lcore = rte_lcore_id(); @@ -148,7 +148,7 @@ load_loop_fn(void *func_param) if (lcore != rte_get_master_lcore()) while (rte_atomic32_read(&synchro) == 0); - begin = rte_get_hpet_cycles(); + begin = rte_get_timer_cycles(); while (time_diff / hz < TIME_S) { if (use_lock) rte_spinlock_lock(&lk); @@ -157,7 +157,7 @@ load_loop_fn(void *func_param) rte_spinlock_unlock(&lk); /* delay to make lock duty cycle slighlty realistic */ rte_delay_us(1); - time_diff = rte_get_hpet_cycles() - begin; + time_diff = rte_get_timer_cycles() - begin; } lock_count[lcore] = lcount; return 0; diff --git a/app/test/test_timer.c b/app/test/test_timer.c index 8060299cdc..344e391a0b 100644 --- a/app/test/test_timer.c +++ b/app/test/test_timer.c @@ -103,6 +103,7 @@ #include #include #include +#include #include @@ -122,7 +123,7 @@ #include "test.h" -#define TEST_DURATION_S 30 /* in seconds */ +#define TEST_DURATION_S 20 /* in seconds */ #define NB_TIMER 4 #define RTE_LOGTYPE_TESTTIMER RTE_LOGTYPE_USER3 @@ -140,7 +141,7 @@ static struct mytimerinfo mytiminfo[NB_TIMER]; static void timer_basic_cb(struct rte_timer *tim, void *arg); static void -mytimer_reset(struct mytimerinfo *timinfo, unsigned ticks, +mytimer_reset(struct mytimerinfo *timinfo, uint64_t ticks, enum rte_timer_type type, unsigned tim_lcore, rte_timer_cb_t fct) { @@ -155,7 +156,7 @@ timer_stress_cb(__attribute__((unused)) struct rte_timer *tim, { long r; unsigned lcore_id = rte_lcore_id(); - uint64_t hz = rte_get_hpet_hz(); + uint64_t hz = rte_get_timer_hz(); if (rte_timer_pending(tim)) return; @@ -178,7 +179,7 @@ timer_stress_cb(__attribute__((unused)) struct rte_timer *tim, static int timer_stress_main_loop(__attribute__((unused)) void *arg) { - uint64_t hz = rte_get_hpet_hz(); + uint64_t hz = rte_get_timer_hz(); unsigned lcore_id = rte_lcore_id(); uint64_t cur_time; int64_t diff = 0; @@ -204,7 +205,7 @@ timer_stress_main_loop(__attribute__((unused)) void *arg) else if ((r & 0xff) == 1) { rte_timer_stop_sync(&mytiminfo[0].tim); } - cur_time = rte_get_hpet_cycles(); + cur_time = rte_get_timer_cycles(); diff = end_time - cur_time; } @@ -219,9 +220,9 @@ static void timer_basic_cb(struct rte_timer *tim, void *arg) { struct mytimerinfo *timinfo = arg; - uint64_t hz = rte_get_hpet_hz(); + uint64_t hz = rte_get_timer_hz(); unsigned lcore_id = rte_lcore_id(); - uint64_t cur_time = rte_get_hpet_cycles(); + uint64_t cur_time = rte_get_timer_cycles(); if (rte_timer_pending(tim)) return; @@ -274,7 +275,7 @@ timer_basic_cb(struct rte_timer *tim, void *arg) static int timer_basic_main_loop(__attribute__((unused)) void *arg) { - uint64_t hz = rte_get_hpet_hz(); + uint64_t hz = rte_get_timer_hz(); unsigned lcore_id = rte_lcore_id(); uint64_t cur_time; int64_t diff = 0; @@ -301,7 +302,7 @@ timer_basic_main_loop(__attribute__((unused)) void *arg) * (3 us = 6000 cycles at 2 Ghz) */ rte_delay_us(3); - cur_time = rte_get_hpet_cycles(); + cur_time = rte_get_timer_cycles(); diff = end_time - cur_time; } RTE_LOG(INFO, TESTTIMER, "core %u finished\n", lcore_id); @@ -309,6 +310,43 @@ timer_basic_main_loop(__attribute__((unused)) void *arg) return 0; } +static int +timer_sanity_check(void) +{ +#ifdef RTE_LIBEAL_USE_HPET + if (eal_timer_source != EAL_TIMER_HPET) { + printf("Not using HPET, can't sanity check timer sources\n"); + return 0; + } + + const uint64_t t_hz = rte_get_tsc_hz(); + const uint64_t h_hz = rte_get_hpet_hz(); + printf("Hertz values: TSC = %"PRIu64", HPET = %"PRIu64"\n", t_hz, h_hz); + + const uint64_t tsc_start = rte_get_tsc_cycles(); + const uint64_t hpet_start = rte_get_hpet_cycles(); + rte_delay_ms(100); /* delay 1/10 second */ + const uint64_t tsc_end = rte_get_tsc_cycles(); + const uint64_t hpet_end = rte_get_hpet_cycles(); + printf("Measured cycles: TSC = %"PRIu64", HPET = %"PRIu64"\n", + tsc_end-tsc_start, hpet_end-hpet_start); + + const double tsc_time = (double)(tsc_end - tsc_start)/t_hz; + const double hpet_time = (double)(hpet_end - hpet_start)/h_hz; + /* get the percentage that the times differ by */ + const double time_diff = fabs(tsc_time - hpet_time)*100/tsc_time; + printf("Measured time: TSC = %.4f, HPET = %.4f\n", tsc_time, hpet_time); + + printf("Elapsed time measured by TSC and HPET differ by %f%%\n", + time_diff); + if (time_diff > 0.1) { + printf("Error times differ by >0.1%%"); + return -1; + } +#endif + return 0; +} + int test_timer(void) { @@ -316,6 +354,12 @@ test_timer(void) uint64_t cur_time; uint64_t hz; + /* sanity check our timer sources and timer config values */ + if (timer_sanity_check() < 0) { + printf("Timer sanity checks failed\n"); + return -1; + } + if (rte_lcore_count() < 2) { printf("not enough lcores for this test\n"); return -1; @@ -329,8 +373,8 @@ test_timer(void) } /* calculate the "end of test" time */ - cur_time = rte_get_hpet_cycles(); - hz = rte_get_hpet_hz(); + cur_time = rte_get_timer_cycles(); + hz = rte_get_timer_hz(); end_time = cur_time + (hz * TEST_DURATION_S); /* start other cores */ @@ -342,8 +386,8 @@ test_timer(void) rte_timer_stop_sync(&mytiminfo[0].tim); /* calculate the "end of test" time */ - cur_time = rte_get_hpet_cycles(); - hz = rte_get_hpet_hz(); + cur_time = rte_get_timer_cycles(); + hz = rte_get_timer_hz(); end_time = cur_time + (hz * TEST_DURATION_S); /* start other cores */ diff --git a/config/defconfig_i686-default-linuxapp-gcc b/config/defconfig_i686-default-linuxapp-gcc index dba52897ed..a28b7f834a 100644 --- a/config/defconfig_i686-default-linuxapp-gcc +++ b/config/defconfig_i686-default-linuxapp-gcc @@ -110,7 +110,7 @@ CONFIG_RTE_MAX_MEMZONE=2560 CONFIG_RTE_MAX_TAILQ=32 CONFIG_RTE_LOG_LEVEL=8 CONFIG_RTE_LOG_HISTORY=256 -CONFIG_RTE_LIBEAL_USE_HPET=y +CONFIG_RTE_LIBEAL_USE_HPET=n CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n CONFIG_RTE_EAL_UNBIND_PORTS=n diff --git a/config/defconfig_i686-default-linuxapp-icc b/config/defconfig_i686-default-linuxapp-icc index 81901fd7e9..b4e0fc2d50 100644 --- a/config/defconfig_i686-default-linuxapp-icc +++ b/config/defconfig_i686-default-linuxapp-icc @@ -111,7 +111,7 @@ CONFIG_RTE_MAX_MEMZONE=2560 CONFIG_RTE_MAX_TAILQ=32 CONFIG_RTE_LOG_LEVEL=8 CONFIG_RTE_LOG_HISTORY=256 -CONFIG_RTE_LIBEAL_USE_HPET=y +CONFIG_RTE_LIBEAL_USE_HPET=n CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n CONFIG_RTE_EAL_UNBIND_PORTS=n diff --git a/config/defconfig_x86_64-default-linuxapp-gcc b/config/defconfig_x86_64-default-linuxapp-gcc index eeecfd39e1..daf9753d44 100644 --- a/config/defconfig_x86_64-default-linuxapp-gcc +++ b/config/defconfig_x86_64-default-linuxapp-gcc @@ -111,7 +111,7 @@ CONFIG_RTE_MAX_MEMZONE=2560 CONFIG_RTE_MAX_TAILQ=32 CONFIG_RTE_LOG_LEVEL=8 CONFIG_RTE_LOG_HISTORY=256 -CONFIG_RTE_LIBEAL_USE_HPET=y +CONFIG_RTE_LIBEAL_USE_HPET=n CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n CONFIG_RTE_EAL_UNBIND_PORTS=n diff --git a/config/defconfig_x86_64-default-linuxapp-icc b/config/defconfig_x86_64-default-linuxapp-icc index e96c80f318..ff668cac40 100644 --- a/config/defconfig_x86_64-default-linuxapp-icc +++ b/config/defconfig_x86_64-default-linuxapp-icc @@ -111,7 +111,7 @@ CONFIG_RTE_MAX_MEMZONE=2560 CONFIG_RTE_MAX_TAILQ=32 CONFIG_RTE_LOG_LEVEL=8 CONFIG_RTE_LOG_HISTORY=256 -CONFIG_RTE_LIBEAL_USE_HPET=y +CONFIG_RTE_LIBEAL_USE_HPET=n CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n CONFIG_RTE_EAL_UNBIND_PORTS=n diff --git a/examples/timer/main.c b/examples/timer/main.c index 22652a2550..3bee864d12 100644 --- a/examples/timer/main.c +++ b/examples/timer/main.c @@ -84,7 +84,7 @@ timer1_cb(__attribute__((unused)) struct rte_timer *tim, printf("%s() on lcore %u\n", __func__, lcore_id); /* reload it on another lcore */ - hz = rte_get_hpet_hz(); + hz = rte_get_timer_hz(); lcore_id = rte_get_next_lcore(lcore_id, 0, 1); rte_timer_reset(tim, hz/3, SINGLE, lcore_id, timer1_cb, NULL); } @@ -135,7 +135,7 @@ MAIN(int argc, char **argv) rte_timer_init(&timer1); /* load timer0, every second, on master lcore, reloaded automatically */ - hz = rte_get_hpet_hz(); + hz = rte_get_timer_hz(); lcore_id = rte_lcore_id(); rte_timer_reset(&timer0, hz, PERIODICAL, lcore_id, timer0_cb, NULL); diff --git a/lib/librte_eal/common/include/eal_private.h b/lib/librte_eal/common/include/eal_private.h index 314ee3091e..b9ae36b7ee 100644 --- a/lib/librte_eal/common/include/eal_private.h +++ b/lib/librte_eal/common/include/eal_private.h @@ -35,6 +35,8 @@ #ifndef _EAL_PRIVATE_H_ #define _EAL_PRIVATE_H_ +#include + /** * Initialize the memzone subsystem (private to eal). * @@ -84,17 +86,18 @@ int rte_eal_cpu_init(void); int rte_eal_memory_init(void); /** - * Configure HPET + * Configure timers * * This function is private to EAL. * * Mmap memory areas used by HPET (high precision event timer) that will - * provide our time reference. + * provide our time reference, and configure the TSC frequency also for it + * to be used as a reference. * * @return * 0 on success, negative on error */ -int rte_eal_hpet_init(void); +int rte_eal_timer_init(void); /** * Init early logs diff --git a/lib/librte_eal/common/include/rte_cycles.h b/lib/librte_eal/common/include/rte_cycles.h index 14a7c1baca..a490905728 100644 --- a/lib/librte_eal/common/include/rte_cycles.h +++ b/lib/librte_eal/common/include/rte_cycles.h @@ -76,6 +76,7 @@ extern "C" { #endif #include +#include #ifdef RTE_LIBRTE_EAL_VMWARE_TSC_MAP_SUPPORT /** Global switch to use VMWARE mapping of TSC instead of RDTSC */ @@ -83,6 +84,15 @@ extern int rte_cycles_vmware_tsc_map; #include #endif +#define MS_PER_S 1000 +#define US_PER_S 1000000 +#define NS_PER_S 1000000000 + +enum timer_source { + EAL_TIMER_TSC = 0, + EAL_TIMER_HPET +}; +extern enum timer_source eal_timer_source; /** * Read the TSC register. @@ -118,11 +128,30 @@ rte_rdtsc(void) return tsc.tsc_64; } +/** + * Get the measured frequency of the RDTSC counter + * + * @return + * The TSC frequency for this lcore + */ +uint64_t +rte_get_tsc_hz(void); + +/** + * Return the number of TSC cycles since boot + * + * @return + * the number of cycles + */ +static inline uint64_t +rte_get_tsc_cycles(void) { return rte_rdtsc(); } + +#ifdef RTE_LIBEAL_USE_HPET /** * Return the number of HPET cycles since boot * * This counter is global for all execution units. The number of - * cycles in one second can be retrived using rte_get_hpet_hz(). + * cycles in one second can be retrieved using rte_get_hpet_hz(). * * @return * the number of cycles @@ -131,7 +160,7 @@ uint64_t rte_get_hpet_cycles(void); /** - * Get the number of cycles in one second. + * Get the number of HPET cycles in one second. * * @return * The number of cycles in one second. @@ -139,6 +168,63 @@ rte_get_hpet_cycles(void); uint64_t rte_get_hpet_hz(void); +/** + * Initialise the HPET for use. This must be called before the rte_get_hpet_hz + * and rte_get_hpet_cycles APIs are called. If this function does not succeed, + * then the HPET functions are unavailable and should not be called. + * + * @param make_default + * If set, the hpet timer becomes the default timer whose values are + * returned by the rte_get_timer_hz/cycles API calls + * + * @return + * 0 on success, + * -1 on error, and the make_default parameter is ignored. + */ +int rte_eal_hpet_init(int make_default); + +#endif + +/** + * Get the number of cycles since boot from the default timer. + * + * @return + * The number of cycles + */ +static inline uint64_t +rte_get_timer_cycles(void) +{ + switch(eal_timer_source) { + case EAL_TIMER_TSC: + return rte_rdtsc(); + case EAL_TIMER_HPET: +#ifdef RTE_LIBEAL_USE_HPET + return rte_get_hpet_cycles(); +#endif + default: rte_panic("Invalid timer source specified\n"); + } +} + +/** + * Get the number of cycles in one second for the default timer. + * + * @return + * The number of cycles in one second. + */ +static inline uint64_t +rte_get_timer_hz(void) +{ + switch(eal_timer_source) { + case EAL_TIMER_TSC: + return rte_get_tsc_hz(); + case EAL_TIMER_HPET: +#ifdef RTE_LIBEAL_USE_HPET + return rte_get_hpet_hz(); +#endif + default: rte_panic("Invalid timer source specified\n"); + } +} + /** * Wait at least us microseconds. * diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile index f66c4c7bf7..882db8a50c 100644 --- a/lib/librte_eal/linuxapp/eal/Makefile +++ b/lib/librte_eal/linuxapp/eal/Makefile @@ -53,7 +53,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_log.c SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_pci.c SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_debug.c SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_lcore.c -SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_hpet.c +SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_timer.c SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_interrupts.c SRCS-$(CONFIG_RTE_LIBRTE_EAL_LINUXAPP) += eal_alarm.c diff --git a/lib/librte_eal/linuxapp/eal/eal.c b/lib/librte_eal/linuxapp/eal/eal.c index 43150b14c9..9ba3d1dbd8 100644 --- a/lib/librte_eal/linuxapp/eal/eal.c +++ b/lib/librte_eal/linuxapp/eal/eal.c @@ -858,8 +858,8 @@ rte_eal_init(int argc, char **argv) if (rte_eal_intr_init() < 0) rte_panic("Cannot init interrupt-handling thread\n"); - if (rte_eal_hpet_init() < 0) - rte_panic("Cannot init HPET\n"); + if (rte_eal_timer_init() < 0) + rte_panic("Cannot init HPET or TSC timers\n"); if (rte_eal_pci_init() < 0) rte_panic("Cannot init PCI\n"); diff --git a/lib/librte_eal/linuxapp/eal/eal_hpet.c b/lib/librte_eal/linuxapp/eal/eal_timer.c similarity index 63% rename from lib/librte_eal/linuxapp/eal/eal_hpet.c rename to lib/librte_eal/linuxapp/eal/eal_timer.c index a0d65b64e0..d046f5954d 100644 --- a/lib/librte_eal/linuxapp/eal/eal_hpet.c +++ b/lib/librte_eal/linuxapp/eal/eal_timer.c @@ -35,13 +35,12 @@ #include #include #include -#include #include +#include #include #include #include #include -#include #include #include @@ -52,10 +51,18 @@ #include #include #include +#include #include "eal_private.h" #include "eal_internal_cfg.h" +enum timer_source eal_timer_source = EAL_TIMER_HPET; + +/* The frequency of the RDTSC timer resolution */ +static uint64_t eal_tsc_resolution_hz = 0; + +#ifdef RTE_LIBEAL_USE_HPET + #define DEV_HPET "/dev/hpet" /* Maximum number of counters. */ @@ -98,10 +105,11 @@ struct eal_hpet_regs { /* Mmap'd hpet registers */ static volatile struct eal_hpet_regs *eal_hpet = NULL; -/* Period at which the counter increments in femtoseconds (10^-15 seconds). */ +/* Period at which the HPET counter increments in + * femtoseconds (10^-15 seconds). */ static uint32_t eal_hpet_resolution_fs = 0; -/* Frequency of the counter in Hz */ +/* Frequency of the HPET counter in Hz */ static uint64_t eal_hpet_resolution_hz = 0; /* Incremented 4 times during one 32bits hpet full count */ @@ -114,7 +122,7 @@ static pthread_t msb_inc_thread_id; * containing used to process MSB of the HPET (unfortunatelly, we need * this because hpet is 32 bits by default under linux). */ -static __attribute__((noreturn)) void * +static void hpet_msb_inc(__attribute__((unused)) void *arg) { uint32_t t; @@ -127,44 +135,83 @@ hpet_msb_inc(__attribute__((unused)) void *arg) } } -static inline void -set_rdtsc_freq(void) +uint64_t +rte_get_hpet_hz(void) { - uint64_t start; + if(internal_config.no_hpet) + rte_panic("Error, HPET called, but no HPET present\n"); - start = rte_rdtsc(); - sleep(1); - eal_hpet_resolution_hz = rte_rdtsc() - start; - eal_hpet_resolution_fs = (uint32_t) - ((1.0 / eal_hpet_resolution_hz) / 1e-15); + return eal_hpet_resolution_hz; +} + +uint64_t +rte_get_hpet_cycles(void) +{ + uint32_t t, msb; + uint64_t ret; + + if(internal_config.no_hpet) + rte_panic("Error, HPET called, but no HPET present\n"); + + t = eal_hpet->counter_l; + msb = eal_hpet_msb; + ret = (msb + 2 - (t >> 30)) / 4; + ret <<= 32; + ret += t; + return ret; +} + +#endif + + +void +rte_delay_us(unsigned us) +{ + const uint64_t start = rte_get_timer_cycles(); + const uint64_t ticks = (uint64_t)us * rte_get_timer_hz() / 1E6; + while ((rte_get_timer_cycles() - start) < ticks) + rte_pause(); +} + +uint64_t +rte_get_tsc_hz(void) +{ + return eal_tsc_resolution_hz; } + +#ifdef RTE_LIBEAL_USE_HPET /* * Open and mmap /dev/hpet (high precision event timer) that will * provide our time reference. */ int -rte_eal_hpet_init(void) +rte_eal_hpet_init(int make_default) { int fd, ret; if (internal_config.no_hpet) { - goto use_rdtsc; + RTE_LOG(INFO, EAL, "HPET is disabled\n"); + return -1; } fd = open(DEV_HPET, O_RDONLY); if (fd < 0) { - RTE_LOG(WARNING, EAL, "WARNING: Cannot open "DEV_HPET": %s! " - "The TSC will be used instead.\n", + RTE_LOG(ERR, EAL, "ERROR: Cannot open "DEV_HPET": %s!\n", strerror(errno)); - goto use_rdtsc; + internal_config.no_hpet = 1; + return -1; } eal_hpet = mmap(NULL, 1024, PROT_READ, MAP_SHARED, fd, 0); if (eal_hpet == MAP_FAILED) { - RTE_LOG(WARNING, EAL, "WARNING: Cannot mmap "DEV_HPET"! " - "The TSC will be used instead.\n"); + RTE_LOG(ERR, EAL, "ERROR: Cannot mmap "DEV_HPET"!\n" + "Please enable CONFIG_HPET_MMAP in your kernel configuration " + "to allow HPET support.\n" + "To run without using HPET, set CONFIG_RTE_LIBEAL_USE_HPET=n " + "in your build configuration or use '--no-hpet' EAL flag.\n"); close(fd); - goto use_rdtsc; + internal_config.no_hpet = 1; + return -1; } close(fd); @@ -175,57 +222,88 @@ rte_eal_hpet_init(void) eal_hpet_resolution_hz = (1000ULL*1000ULL*1000ULL*1000ULL*1000ULL) / (uint64_t)eal_hpet_resolution_fs; + RTE_LOG(INFO, EAL, "HPET frequency is ~%"PRIu64" kHz\n", + eal_hpet_resolution_hz/1000); + eal_hpet_msb = (eal_hpet->counter_l >> 30); /* create a thread that will increment a global variable for * msb (hpet is 32 bits by default under linux) */ - ret = pthread_create(&msb_inc_thread_id, NULL, hpet_msb_inc, NULL); + ret = pthread_create(&msb_inc_thread_id, NULL, + (void *(*)(void *))hpet_msb_inc, NULL); if (ret < 0) { - RTE_LOG(WARNING, EAL, "WARNING: Cannot create HPET timer thread! " - "The TSC will be used instead.\n"); - goto use_rdtsc; + RTE_LOG(ERR, EAL, "ERROR: Cannot create HPET timer thread!\n"); + internal_config.no_hpet = 1; + return -1; } - return 0; - -use_rdtsc: - internal_config.no_hpet = 1; - set_rdtsc_freq(); + if (make_default) + eal_timer_source = EAL_TIMER_HPET; return 0; } +#endif -uint64_t -rte_get_hpet_hz(void) +static int +set_tsc_freq_from_clock(void) { - return eal_hpet_resolution_hz; +#ifdef CLOCK_MONOTONIC_RAW +#define NS_PER_SEC 1E9 + + struct timespec sleeptime = {.tv_nsec = 5E8 }; /* 1/2 second */ + + struct timespec t_start, t_end; + + if (clock_gettime(CLOCK_MONOTONIC_RAW, &t_start) == 0) { + uint64_t ns, end, start = rte_rdtsc(); + nanosleep(&sleeptime,NULL); + clock_gettime(CLOCK_MONOTONIC_RAW, &t_end); + end = rte_rdtsc(); + ns = ((t_end.tv_sec - t_start.tv_sec) * NS_PER_SEC); + ns += (t_end.tv_nsec - t_start.tv_nsec); + + double secs = (double)ns/NS_PER_SEC; + eal_tsc_resolution_hz = (uint64_t)((end - start)/secs); + return 0; + } +#endif + return -1; } -uint64_t -rte_get_hpet_cycles(void) +static void +set_tsc_freq_fallback(void) { - uint32_t t, msb; - uint64_t ret; - - if(internal_config.no_hpet) - /* fallback to rdtsc */ - return rte_rdtsc(); + RTE_LOG(WARNING, EAL, "WARNING: clock_gettime cannot use " + "CLOCK_MONOTONIC_RAW and HPET is not available" + " - clock timings may be less accurate.\n"); + /* assume that the sleep(1) will sleep for 1 second */ + uint64_t start = rte_rdtsc(); + sleep(1); + eal_tsc_resolution_hz = rte_rdtsc() - start; +} +/* + * This function measures the TSC frequency. It uses a variety of approaches. + * + * 1. If kernel provides CLOCK_MONOTONIC_RAW we use that to tune the TSC value + * 2. If kernel does not provide that, and we have HPET support, tune using HPET + * 3. Lastly, if neither of the above can be used, just sleep for 1 second and + * tune off that, printing a warning about inaccuracy of timing + */ +static void +set_tsc_freq(void) +{ + if (set_tsc_freq_from_clock() < 0) + set_tsc_freq_fallback(); - t = eal_hpet->counter_l; - msb = eal_hpet_msb; - ret = (msb + 2 - (t >> 30)) / 4; - ret <<= 32; - ret += t; - return ret; + RTE_LOG(INFO, EAL, "TSC frequency is ~%"PRIu64" KHz\n", + eal_tsc_resolution_hz/1000); } -void -rte_delay_us(unsigned us) +int +rte_eal_timer_init(void) { - uint64_t start; - uint64_t ticks; - ticks = (uint64_t)us * 1000ULL * 1000ULL * 1000ULL; - ticks /= eal_hpet_resolution_fs; - start = rte_get_hpet_cycles(); - while ((rte_get_hpet_cycles() - start) < ticks) - rte_pause(); + + eal_timer_source = EAL_TIMER_TSC; + + set_tsc_freq(); + return 0; } diff --git a/lib/librte_timer/rte_timer.c b/lib/librte_timer/rte_timer.c index c9b253a820..136b641e37 100644 --- a/lib/librte_timer/rte_timer.c +++ b/lib/librte_timer/rte_timer.c @@ -203,7 +203,7 @@ timer_set_running_state(struct rte_timer *tim) static void timer_add(struct rte_timer *tim, unsigned tim_lcore, int local_is_locked) { - uint64_t cur_time = rte_get_hpet_cycles(); + uint64_t cur_time = rte_get_timer_cycles(); unsigned lcore_id = rte_lcore_id(); struct rte_timer *t, *t_prev; @@ -322,7 +322,7 @@ rte_timer_reset(struct rte_timer *tim, uint64_t ticks, enum rte_timer_type type, unsigned tim_lcore, rte_timer_cb_t fct, void *arg) { - uint64_t cur_time = rte_get_hpet_cycles(); + uint64_t cur_time = rte_get_timer_cycles(); uint64_t period; if (unlikely((tim_lcore != (unsigned)LCORE_ID_ANY) && @@ -406,12 +406,11 @@ void rte_timer_manage(void) uint64_t cur_time; int ret; + __TIMER_STAT_ADD(manage, 1); /* optimize for the case where per-cpu list is empty */ if (LIST_EMPTY(&priv_timer[lcore_id].pending)) return; - - cur_time = rte_get_hpet_cycles(); - __TIMER_STAT_ADD(manage, 1); + cur_time = rte_get_timer_cycles(); /* browse ordered list, add expired timers in 'expired' list */ rte_spinlock_lock(&priv_timer[lcore_id].list_lock); diff --git a/mk/exec-env/linuxapp/rte.vars.mk b/mk/exec-env/linuxapp/rte.vars.mk index d1b657beb3..92f30c067d 100644 --- a/mk/exec-env/linuxapp/rte.vars.mk +++ b/mk/exec-env/linuxapp/rte.vars.mk @@ -42,7 +42,8 @@ # EXECENV_CFLAGS = -pthread -EXECENV_LDFLAGS = +EXECENV_LDFLAGS = +EXECENV_LDLIBS = -lrt EXECENV_ASFLAGS = # force applications to link with gcc/icc instead of using ld diff --git a/mk/rte.app.mk b/mk/rte.app.mk index 4b802556f7..2375edcc7a 100644 --- a/mk/rte.app.mk +++ b/mk/rte.app.mk @@ -131,6 +131,7 @@ endif ifeq ($(CONFIG_RTE_LIBC),y) LDLIBS += -lc +LDLIBS += -lm endif ifeq ($(CONFIG_RTE_LIBGLOSS),y) -- 2.20.1