1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
|
/******************************************************************************
* time.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; If not, see <http://www.gnu.org/licenses/>.
*/
#include <xen/sched.h>
#include <xen/shared.h>
#include <xen/spinlock.h>
#include <xen/time.h>
#include <asm/div64.h>
#include <asm/domain.h>
/* Nonzero if YEAR is a leap year (every 4 years,
except every 100th isn't, and every 400th is). */
#define __isleap(year) \
((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
/* How many days are in each month. */
static const unsigned short int __mon_lengths[2][12] = {
/* Normal years. */
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
/* Leap years. */
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
#define SECS_PER_HOUR (60 * 60)
#define SECS_PER_DAY (SECS_PER_HOUR * 24)
static uint64_t wc_sec; /* UTC time at last 'time update'. */
static unsigned int wc_nsec;
static DEFINE_SPINLOCK(wc_lock);
struct tm gmtime(unsigned long t)
{
struct tm tbuf;
long days, rem;
int y;
const unsigned short int *ip;
y = 1970;
#if BITS_PER_LONG >= 64
/* Allow the concept of time before 1970. 64-bit only; for 32-bit
* time after 2038 seems more important than time before 1970. */
while ( t & (1UL<<39) )
{
y -= 400;
t += ((unsigned long)(365 * 303 + 366 * 97)) * SECS_PER_DAY;
}
t &= (1UL << 40) - 1;
#endif
days = t / SECS_PER_DAY;
rem = t % SECS_PER_DAY;
tbuf.tm_hour = rem / SECS_PER_HOUR;
rem %= SECS_PER_HOUR;
tbuf.tm_min = rem / 60;
tbuf.tm_sec = rem % 60;
/* January 1, 1970 was a Thursday. */
tbuf.tm_wday = (4 + days) % 7;
if ( tbuf.tm_wday < 0 )
tbuf.tm_wday += 7;
while ( days >= (rem = __isleap(y) ? 366 : 365) )
{
++y;
days -= rem;
}
while ( days < 0 )
{
--y;
days += __isleap(y) ? 366 : 365;
}
tbuf.tm_year = y - 1900;
tbuf.tm_yday = days;
ip = (const unsigned short int *)__mon_lengths[__isleap(y)];
for ( y = 0; days >= ip[y]; ++y )
days -= ip[y];
tbuf.tm_mon = y;
tbuf.tm_mday = days + 1;
tbuf.tm_isdst = -1;
return tbuf;
}
void update_domain_wallclock_time(struct domain *d)
{
uint32_t *wc_version;
uint64_t sec;
spin_lock(&wc_lock);
wc_version = &shared_info(d, wc_version);
*wc_version = version_update_begin(*wc_version);
smp_wmb();
sec = wc_sec + d->time_offset.seconds;
shared_info(d, wc_sec) = sec;
shared_info(d, wc_nsec) = wc_nsec;
#if defined(CONFIG_X86) && defined(CONFIG_COMPAT)
if ( likely(!has_32bit_shinfo(d)) )
d->shared_info->native.wc_sec_hi = sec >> 32;
else
d->shared_info->compat.arch.wc_sec_hi = sec >> 32;
#else
shared_info(d, wc_sec_hi) = sec >> 32;
#endif
smp_wmb();
*wc_version = version_update_end(*wc_version);
spin_unlock(&wc_lock);
}
/* Set clock to <secs,usecs> after 00:00:00 UTC, 1 January, 1970. */
void do_settime(u64 secs, unsigned int nsecs, u64 system_time_base)
{
u64 x;
u32 y;
struct domain *d;
x = SECONDS(secs) + nsecs - system_time_base;
y = do_div(x, 1000000000);
spin_lock(&wc_lock);
wc_sec = x;
wc_nsec = y;
spin_unlock(&wc_lock);
rcu_read_lock(&domlist_read_lock);
for_each_domain ( d )
update_domain_wallclock_time(d);
rcu_read_unlock(&domlist_read_lock);
}
/* Return secs after 00:00:00 localtime, 1 January, 1970. */
unsigned long get_localtime(struct domain *d)
{
return wc_sec + (wc_nsec + NOW()) / 1000000000ULL
+ d->time_offset.seconds;
}
/* Return microsecs after 00:00:00 localtime, 1 January, 1970. */
uint64_t get_localtime_us(struct domain *d)
{
return (SECONDS(wc_sec + d->time_offset.seconds) + wc_nsec + NOW())
/ 1000UL;
}
unsigned long get_sec(void)
{
return wc_sec + (wc_nsec + NOW()) / 1000000000ULL;
}
struct tm wallclock_time(uint64_t *ns)
{
uint64_t seconds, nsec;
if ( !wc_sec )
return (struct tm) { 0 };
seconds = NOW() + SECONDS(wc_sec) + wc_nsec;
nsec = do_div(seconds, 1000000000);
if ( ns )
*ns = nsec;
return gmtime(seconds);
}
|