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