/****************************************************************************** * 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 . */ #include #include #include #include #include #include /* 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. */ 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; #ifdef CONFIG_X86 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 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); }