summaryrefslogtreecommitdiff
path: root/usr/src/uts/i86pc/os/timestamp.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/i86pc/os/timestamp.c')
-rw-r--r--usr/src/uts/i86pc/os/timestamp.c49
1 files changed, 49 insertions, 0 deletions
diff --git a/usr/src/uts/i86pc/os/timestamp.c b/usr/src/uts/i86pc/os/timestamp.c
index 5b97d20e52..c40159018c 100644
--- a/usr/src/uts/i86pc/os/timestamp.c
+++ b/usr/src/uts/i86pc/os/timestamp.c
@@ -24,6 +24,7 @@
* Use is subject to license terms.
*
* Copyright 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2014, 2016 by Delphix. All rights reserved.
*/
#include <sys/types.h>
@@ -45,6 +46,7 @@
#include <sys/time.h>
#include <sys/panic.h>
#include <sys/cpu.h>
+#include <sys/sdt.h>
/*
* Using the Pentium's TSC register for gethrtime()
@@ -151,6 +153,14 @@ static hrtime_t tsc_last = 0;
static hrtime_t tsc_last_jumped = 0;
static hrtime_t tsc_hrtime_base = 0;
static int tsc_jumped = 0;
+static uint32_t tsc_wayback = 0;
+/*
+ * The cap of 1 second was chosen since it is the frequency at which the
+ * tsc_tick() function runs which means that when gethrtime() is called it
+ * should never be more than 1 second since tsc_last was updated.
+ */
+static hrtime_t tsc_resume_cap;
+static hrtime_t tsc_resume_cap_ns = NANOSEC; /* 1s */
static hrtime_t shadow_tsc_hrtime_base;
static hrtime_t shadow_tsc_last;
@@ -158,6 +168,17 @@ static uint_t shadow_nsec_scale;
static uint32_t shadow_hres_lock;
int get_tsc_ready();
+static inline
+hrtime_t tsc_protect(hrtime_t a) {
+ if (a > tsc_resume_cap) {
+ atomic_inc_32(&tsc_wayback);
+ DTRACE_PROBE3(tsc__wayback, htrime_t, a, hrtime_t, tsc_last,
+ uint32_t, tsc_wayback);
+ return (tsc_resume_cap);
+ }
+ return (a);
+}
+
hrtime_t
tsc_gethrtime(void)
{
@@ -186,6 +207,20 @@ tsc_gethrtime(void)
* delta to be zero.
*/
tsc = 0;
+ } else {
+ /*
+ * If we reach this else clause we assume that we have
+ * gone through a suspend/resume cycle and use the
+ * current tsc value as the delta.
+ *
+ * In rare cases we can reach this else clause due to
+ * a lack of monotonicity in the TSC value. In such
+ * cases using the current TSC value as the delta would
+ * cause us to return a value ~2x of what it should
+ * be. To protect against these cases we cap the
+ * suspend/resume delta at tsc_resume_cap.
+ */
+ tsc = tsc_protect(tsc);
}
hrt = tsc_hrtime_base;
@@ -226,6 +261,8 @@ tsc_gethrtime_delta(void)
tsc -= tsc_last;
} else if (tsc >= tsc_last - 2 * tsc_max_delta) {
tsc = 0;
+ } else {
+ tsc = tsc_protect(tsc);
}
hrt = tsc_hrtime_base;
@@ -285,6 +322,8 @@ dtrace_gethrtime(void)
tsc -= tsc_last;
else if (tsc >= tsc_last - 2*tsc_max_delta)
tsc = 0;
+ else
+ tsc = tsc_protect(tsc);
hrt = tsc_hrtime_base;
@@ -332,6 +371,8 @@ dtrace_gethrtime(void)
tsc -= shadow_tsc_last;
else if (tsc >= shadow_tsc_last - 2 * tsc_max_delta)
tsc = 0;
+ else
+ tsc = tsc_protect(tsc);
hrt = shadow_tsc_hrtime_base;
@@ -598,6 +639,7 @@ tsc_tick(void)
* resume (i.e nsec_scale remains the same).
*/
delta = now;
+ delta = tsc_protect(delta);
tsc_last_jumped += tsc_last;
tsc_jumped = 1;
} else {
@@ -650,6 +692,13 @@ tsc_hrtimeinit(uint64_t cpu_freq_hz)
* This structure should be aligned on a multiple of cache line size.
*/
tscp = kmem_zalloc(PAGESIZE, KM_SLEEP);
+
+ /*
+ * Convert the TSC resume cap ns value into its unscaled TSC value.
+ * See tsc_gethrtime().
+ */
+ if (tsc_resume_cap == 0)
+ TSC_CONVERT(tsc_resume_cap_ns, tsc_resume_cap, nsec_unscale);
}
int