Coverage Report

Created: 2017-10-25 09:10

/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
}