[API-NEXT,2/6] time: implement odp_tick_t

Message ID 20170528192302.56363-2-brian.brooks@arm.com
State New
Headers show
Series
  • [API-NEXT,1/6] api: time: add odp_tick_t
Related show

Commit Message

Brian Brooks May 28, 2017, 7:22 p.m.
Signed-off-by: Brian Brooks <brian.brooks@arm.com>

Reviewed-by: Ola Liljedahl <ola.liljedahl@arm.com>

---
 platform/linux-generic/Makefile.am                 |  3 +-
 platform/linux-generic/arch/arm/odp_cpu.h          | 26 +++++++
 platform/linux-generic/arch/arm/odp_cpu_arch.c     | 15 ++++
 platform/linux-generic/arch/x86/odp_cpu.h          | 21 +++++
 platform/linux-generic/arch/x86/odp_cpu_arch.c     | 91 +++++++++++++++++++++-
 .../include/odp/api/plat/time_inlines.h            | 60 ++++++++++++++
 .../include/odp/api/plat/time_types.h              |  2 +
 platform/linux-generic/include/odp/api/time.h      |  3 +-
 platform/linux-generic/m4/odp_pthread.m4           |  2 +-
 platform/linux-generic/odp_time.c                  | 15 +++-
 10 files changed, 230 insertions(+), 8 deletions(-)
 create mode 100644 platform/linux-generic/arch/arm/odp_cpu.h
 create mode 100644 platform/linux-generic/arch/x86/odp_cpu.h
 create mode 100644 platform/linux-generic/include/odp/api/plat/time_inlines.h

-- 
2.13.0

Patch hide | download patch | download mbox

diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/Makefile.am
index 79f0e70c..f88cb4c9 100644
--- a/platform/linux-generic/Makefile.am
+++ b/platform/linux-generic/Makefile.am
@@ -68,7 +68,8 @@  odpapiinclude_HEADERS = \
 		  $(srcdir)/include/odp/api/timer.h \
 		  $(srcdir)/include/odp/api/traffic_mngr.h \
 		  $(srcdir)/include/odp/api/version.h \
-		  $(srcdir)/arch/@ARCH_DIR@/odp/api/cpu_arch.h
+		  $(srcdir)/arch/@ARCH_DIR@/odp/api/cpu_arch.h \
+		  $(srcdir)/arch/@ARCH_DIR@/odp_cpu.h
 
 odpapiplatincludedir= $(includedir)/odp/api/plat
 odpapiplatinclude_HEADERS = \
diff --git a/platform/linux-generic/arch/arm/odp_cpu.h b/platform/linux-generic/arch/arm/odp_cpu.h
new file mode 100644
index 00000000..a2bbd3a4
--- /dev/null
+++ b/platform/linux-generic/arch/arm/odp_cpu.h
@@ -0,0 +1,26 @@ 
+/* Copyright (c) 2017, ARM Ltd. All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+#ifndef PLATFORM_LINUXGENERIC_ARCH_ARM_CPU_H
+#define PLATFORM_LINUXGENERIC_ARCH_ARM_CPU_H
+
+static inline uint64_t cpu_tick_now(void)
+{
+#if __ARM_ARCH == 8
+	uint64_t cntvct;
+
+	/* There are mixed use cases wrt whether an isb should be used here. */
+	/* Memory clobber to minimize optimization around load from sys reg. */
+	__asm__ volatile("mrs %0, cntvct_el0" : "=r"(cntvct) : : "memory");
+
+	return cntvct;
+#else
+	return 0ull;
+#endif
+}
+
+uint64_t cpu_tick_hz(void);
+
+#endif  /* PLATFORM_LINUXGENERIC_ARCH_ARM_CPU_H */
diff --git a/platform/linux-generic/arch/arm/odp_cpu_arch.c b/platform/linux-generic/arch/arm/odp_cpu_arch.c
index c31f9084..7d966268 100644
--- a/platform/linux-generic/arch/arm/odp_cpu_arch.c
+++ b/platform/linux-generic/arch/arm/odp_cpu_arch.c
@@ -13,6 +13,7 @@ 
 #include <odp/api/hints.h>
 #include <odp/api/system_info.h>
 #include <odp_debug_internal.h>
+#include <odp_cpu.h>
 #include <odp_time_internal.h>
 
 #define GIGA 1000000000
@@ -62,3 +63,17 @@  uint64_t cpu_global_time_freq(void)
 {
 	return 0;
 }
+
+uint64_t cpu_tick_hz(void)
+{
+#if __ARM_ARCH == 8
+	static uint64_t hz;
+
+	if (hz == 0)
+		__asm__ volatile("mrs %0, cntfrq_el0" : "=r"(hz));
+
+	return hz;
+#else
+	return 0ull;
+#endif
+}
diff --git a/platform/linux-generic/arch/x86/odp_cpu.h b/platform/linux-generic/arch/x86/odp_cpu.h
new file mode 100644
index 00000000..5c05fee0
--- /dev/null
+++ b/platform/linux-generic/arch/x86/odp_cpu.h
@@ -0,0 +1,21 @@ 
+/* Copyright (c) 2017, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+#ifndef PLATFORM_LINUXGENERIC_ARCH_X86_CPU_H
+#define PLATFORM_LINUXGENERIC_ARCH_X86_CPU_H
+
+static inline uint64_t cpu_tick_now(void)
+{
+	uint64_t hi, lo;
+
+	__asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi) : : "memory");
+
+	return (hi << 32) | lo;
+}
+
+uint64_t cpu_tick_hz(void);
+
+#endif  /* PLATFORM_LINUXGENERIC_ARCH_X86_CPU_H */
diff --git a/platform/linux-generic/arch/x86/odp_cpu_arch.c b/platform/linux-generic/arch/x86/odp_cpu_arch.c
index b1da428b..21aeee87 100644
--- a/platform/linux-generic/arch/x86/odp_cpu_arch.c
+++ b/platform/linux-generic/arch/x86/odp_cpu_arch.c
@@ -4,13 +4,15 @@ 
  * SPDX-License-Identifier:     BSD-3-Clause
  */
 
-#include <odp_posix_extensions.h>
+#define _GNU_SOURCE
+#include <time.h>
 
 #include <odp/api/cpu.h>
+#include <odp/api/time.h>
+
 #include <odp_time_internal.h>
 #include <odp_debug_internal.h>
-
-#include <time.h>
+#include <odp_cpu.h>
 
 uint64_t odp_cpu_cycles(void)
 {
@@ -99,3 +101,86 @@  uint64_t cpu_global_time_freq(void)
 
 	return avg / (rounds - 1);
 }
+
+static void cpuid(uint32_t leaf, uint32_t *eax, uint32_t *ebx,
+		  uint32_t *ecx, uint32_t *edx)
+{
+	*eax = leaf;
+	*ecx = 0;
+	__asm__ volatile("cpuid"
+			 : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx)
+			 : "0"(*eax), "2"(*ecx) : "memory");
+}
+
+static uint32_t cpuid_eax(uint32_t leaf)
+{
+	uint32_t eax, ebx, ecx, edx;
+
+	cpuid(leaf, &eax, &ebx, &ecx, &edx);
+
+	return eax;
+}
+
+static uint64_t estimate_hz(void)
+{
+	int err;
+	struct timespec ts;
+	uint64_t start, stop;
+	uint64_t adj;
+
+	ts.tv_sec  = 0;
+	ts.tv_nsec = 0;
+
+	start = odp_tick_now();
+	err = nanosleep(&ts, NULL);
+	stop  = odp_tick_now();
+
+	if (err != 0)
+		ODP_ABORT("nanosleep\n");
+
+	adj = stop - start;
+
+	ts.tv_sec  = 1;
+	ts.tv_nsec = 0;
+
+	start = odp_tick_now();
+	err = nanosleep(&ts, NULL);
+	stop  = odp_tick_now();
+
+	if (err != 0)
+		ODP_ABORT("nanosleep\n");
+
+	return stop - start - adj;
+}
+
+uint64_t cpu_tick_hz(void)
+{
+	static uint64_t hz;
+
+	if (hz == 0) {
+		/* Check for Invariant TSC */
+		uint32_t eax, ebx, ecx, edx;
+
+		if (cpuid_eax(0x80000000) >= 0x80000007) {
+			cpuid(0x80000007, &eax, &ebx, &ecx, &edx);
+			if (!(edx & (1 << 8)))
+				return 0;
+		}
+
+		/* Check if running under hyp. */
+		/* Note: This leaf is standardized in Hyper-V, and also
+		 * appears to work under VMware too. */
+		cpuid(0x1, &eax, &ebx, &ecx, &edx);
+		if (ecx & (1 << 31)) {
+			uint32_t khz = cpuid_eax(0x40000010);
+
+			if (khz) {
+				hz = khz * 1000;
+				return hz;
+			}
+		}
+		/* Can't determine, estimate it */
+		hz = estimate_hz();
+	}
+	return hz;
+}
diff --git a/platform/linux-generic/include/odp/api/plat/time_inlines.h b/platform/linux-generic/include/odp/api/plat/time_inlines.h
new file mode 100644
index 00000000..ebb88479
--- /dev/null
+++ b/platform/linux-generic/include/odp/api/plat/time_inlines.h
@@ -0,0 +1,60 @@ 
+/* Copyright (c) 2013, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+#ifndef ODP_PLAT_TIME_INLINES_H_
+#define ODP_PLAT_TIME_INLINES_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_cpu.h>
+
+#include <math.h>
+
+/* Fwd decl since spec file has to come after inlines */
+odp_time_t odp_time_global(void);
+uint64_t odp_time_to_ns(odp_time_t time);
+
+#define MSEC_PER_SEC  1000ull
+
+#define USEC_PER_SEC  1000000ull
+#define USEC_PER_MSEC 1000ull
+
+#define NSEC_PER_SEC  1000000000ull
+#define NSEC_PER_MSEC 1000000ull
+#define NSEC_PER_USEC 1000ull
+
+bool have_cpu_tick;
+
+static inline odp_tick_t odp_tick_now(void)
+{
+	return have_cpu_tick ?
+		(odp_tick_t)cpu_tick_now() :
+		(odp_tick_t)odp_time_to_ns(odp_time_global());
+}
+
+static inline uint64_t odp_tick_hz(void)
+{
+	return have_cpu_tick ? cpu_tick_hz() : NSEC_PER_SEC;
+}
+
+static inline uint64_t odp_ns_from_ticks(odp_tick_t ticks)
+{
+	return round((double)ticks / (double)odp_tick_hz() *
+		     (double)NSEC_PER_SEC);
+}
+
+static inline odp_tick_t odp_ticks_from_ns(uint64_t ns)
+{
+	return round((double)odp_tick_hz() / (double)NSEC_PER_SEC * (double)ns);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp/api/plat/time_types.h b/platform/linux-generic/include/odp/api/plat/time_types.h
index 71e354e6..fd0117ed 100644
--- a/platform/linux-generic/include/odp/api/plat/time_types.h
+++ b/platform/linux-generic/include/odp/api/plat/time_types.h
@@ -41,6 +41,8 @@  typedef struct odp_time_t {
 
 #define ODP_TIME_NULL ((odp_time_t){.u64 = 0})
 
+typedef uint64_t odp_tick_t;
+
 /**
  * @}
  */
diff --git a/platform/linux-generic/include/odp/api/time.h b/platform/linux-generic/include/odp/api/time.h
index 8d1c33e6..6ef6ab8c 100644
--- a/platform/linux-generic/include/odp/api/time.h
+++ b/platform/linux-generic/include/odp/api/time.h
@@ -19,9 +19,8 @@  extern "C" {
 
 #include <odp/api/std_types.h>
 
-
-
 #include <odp/api/plat/time_types.h>
+#include <odp/api/plat/time_inlines.h>
 #include <odp/api/spec/time.h>
 
 #ifdef __cplusplus
diff --git a/platform/linux-generic/m4/odp_pthread.m4 b/platform/linux-generic/m4/odp_pthread.m4
index 7f391039..b5705b2f 100644
--- a/platform/linux-generic/m4/odp_pthread.m4
+++ b/platform/linux-generic/m4/odp_pthread.m4
@@ -10,4 +10,4 @@  LIBS="$PTHREAD_LIBS $LIBS"
 AM_CFLAGS="$AM_CFLAGS $PTHREAD_CFLAGS"
 AM_LDFLAGS="$AM_LDFLAGS $PTHREAD_LDFLAGS"
 
-AM_LDFLAGS="$AM_LDFLAGS -pthread -lrt"
+AM_LDFLAGS="$AM_LDFLAGS -pthread -lrt -lm"
diff --git a/platform/linux-generic/odp_time.c b/platform/linux-generic/odp_time.c
index 2bbe5666..fc352983 100644
--- a/platform/linux-generic/odp_time.c
+++ b/platform/linux-generic/odp_time.c
@@ -4,15 +4,18 @@ 
  * SPDX-License-Identifier:     BSD-3-Clause
  */
 
+#define _GNU_SOURCE
+#include <time.h>
+
 #include <odp_posix_extensions.h>
 
-#include <time.h>
 #include <odp/api/time.h>
 #include <odp/api/hints.h>
 #include <odp_debug_internal.h>
 #include <odp_time_internal.h>
 #include <string.h>
 #include <inttypes.h>
+#include <odp_cpu.h>
 
 typedef struct time_global_t {
 	struct timespec spec_start;
@@ -23,6 +26,8 @@  typedef struct time_global_t {
 
 static time_global_t global;
 
+bool have_cpu_tick = false;
+
 /*
  * Posix timespec based functions
  */
@@ -302,6 +307,14 @@  int odp_time_init_global(void)
 
 	ret = clock_gettime(CLOCK_MONOTONIC_RAW, &global.spec_start);
 
+	/* Check for a suitable CPU tick counter */
+	{
+		uint64_t hz = cpu_tick_hz();
+
+		if (hz >= 1000000 && hz <= 4000000000)
+			have_cpu_tick = true;
+	}
+
 	return ret;
 }