It's come to our attention, that the time gets screwed up when set
between EPOCH and EPOCH + uptime. This may not seem important (because
we don't live in the 70s anymore) but it makes LTP fail. LTP has a date
test that checks what happens when set to EPOCH + 100 secs + 100 nsecs,
and makes sure that it gets a proper result.
The following patches helps xen handle the case where time is set back
to Jan 1st 1970 (or anytime from EPOCH to EPOCH + uptime).
Here's what you get without the patch:
# date -u 010100011970
Thu Jan 1 00:01:00 UTC 1970
# date
Mon Feb 22 16:42:30 EST 2010
Here's what you get with the patch:
# date -u 010100011970
Thu Jan 1 00:01:00 UTC 1970
# date
Wed Dec 31 19:01:01 EST 1969
-- Steve
Signed-off-by: Steven Rostedt <srostedt@xxxxxxxxxx>
diff -r fd2667419c53 linux-2.6-xen-sparse/arch/i386/kernel/time-xen.c
--- a/linux-2.6-xen-sparse/arch/i386/kernel/time-xen.c Tue Jan 16 14:04:12
2007 -0500
+++ b/linux-2.6-xen-sparse/arch/i386/kernel/time-xen.c Wed Jan 17 10:34:06
2007 -0500
@@ -260,7 +260,8 @@ static void __update_wallclock(time_t se
{
long wtm_nsec, xtime_nsec;
time_t wtm_sec, xtime_sec;
- u64 tmp, wc_nsec;
+ s64 tmp, wc_nsec;
+ int s;
/* Adjust wall-clock time base based on wall_jiffies ticks. */
wc_nsec = processed_system_time;
@@ -270,8 +271,17 @@ static void __update_wallclock(time_t se
/* Split wallclock base into seconds and nanoseconds. */
tmp = wc_nsec;
+ /*
+ * do_div does not like s64, and treats them as u64
+ * and we will not get the expected result.
+ * So for those with time machines, let 1970 work
+ * again!
+ */
+ s = tmp < 0 ? -1 : 1;
+ tmp *= s;
xtime_nsec = do_div(tmp, 1000000000);
xtime_sec = (time_t)tmp;
+ xtime_sec *= s;
wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - xtime_sec);
wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - xtime_nsec);
@@ -289,7 +299,12 @@ static void update_wallclock(void)
do {
shadow_tv_version = s->wc_version;
rmb();
- shadow_tv.tv_sec = s->wc_sec;
+ /*
+ * If someone decides to set the time to something
+ * before EPOCH + uptime, we can get a negative
+ * number here.
+ */
+ shadow_tv.tv_sec = (s32)s->wc_sec;
shadow_tv.tv_nsec = s->wc_nsec;
rmb();
} while ((s->wc_version & 1) | (shadow_tv_version ^ s->wc_version));
diff -r fd2667419c53 xen/arch/x86/time.c
--- a/xen/arch/x86/time.c Tue Jan 16 14:04:12 2007 -0500
+++ b/xen/arch/x86/time.c Wed Jan 17 10:34:06 2007 -0500
@@ -708,12 +708,23 @@ void update_domain_wallclock_time(struct
/* Set clock to <secs,usecs> after 00:00:00 UTC, 1 January, 1970. */
void do_settime(unsigned long secs, unsigned long nsecs, u64 system_time_base)
{
- u64 x;
+ s64 x;
u32 y, _wc_sec, _wc_nsec;
struct domain *d;
+ int s;
x = (secs * 1000000000ULL) + (u64)nsecs - system_time_base;
+ s = x < 0 ? -1 : 1;
+ /*
+ * do_div does not like negative s64 numbers and treats them
+ * as u64, thus the result is unexpected if the s64 is
+ * negative. For those that still live in the 70s, and
+ * want their Xen boxes to do the same, we must handle the
+ * negative case.
+ */
+ x *= s;
y = do_div(x, 1000000000);
+ x *= s;
spin_lock(&wc_lock);
wc_sec = _wc_sec = (u32)x;
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|