CINXE.COM

LKML: john stultz: [PATCH 4/11] Time: Generic Timekeeping Infrastructure

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>LKML: john stultz: [PATCH 4/11] Time: Generic Timekeeping Infrastructure</title><link href="/css/message.css" rel="stylesheet" type="text/css" /><link href="/css/wrap.css" rel="alternate stylesheet" type="text/css" title="wrap" /><link href="/css/nowrap.css" rel="stylesheet" type="text/css" title="nowrap" /><link href="/favicon.ico" rel="shortcut icon" /><script src="/js/simple-calendar.js" type="text/javascript"></script><script src="/js/styleswitcher.js" type="text/javascript"></script><link rel="alternate" type="application/rss+xml" title="lkml.org : last 100 messages" href="/rss.php" /><link rel="alternate" type="application/rss+xml" title="lkml.org : last messages by john stultz" href="/groupie.php?aid=1263" /><!--Matomo--><script> var _paq = window._paq = window._paq || []; /* tracker methods like "setCustomDimension" should be called before "trackPageView" */ _paq.push(["setDoNotTrack", true]); _paq.push(["disableCookies"]); _paq.push(['trackPageView']); _paq.push(['enableLinkTracking']); (function() { var u="//m.lkml.org/"; _paq.push(['setTrackerUrl', u+'matomo.php']); _paq.push(['setSiteId', '1']); var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s); })(); </script><!--End Matomo Code--></head><body onload="es.jasper.simpleCalendar.init();" itemscope="itemscope" itemtype="http://schema.org/BlogPosting"><table border="0" cellpadding="0" cellspacing="0"><tr><td width="180" align="center"><a href="/"><img style="border:0;width:135px;height:32px" src="/images/toprowlk.gif" alt="lkml.org" /></a></td><td width="32">聽</td><td class="nb"><div><a class="nb" href="/lkml"> [lkml]</a> 聽 <a class="nb" href="/lkml/2005"> [2005]</a> 聽 <a class="nb" href="/lkml/2005/12"> [Dec]</a> 聽 <a class="nb" href="/lkml/2005/12/15"> [15]</a> 聽 <a class="nb" href="/lkml/last100"> [last100]</a> 聽 <a href="/rss.php"><img src="/images/rss-or.gif" border="0" alt="RSS Feed" /></a></div><div>Views: <a href="#" class="nowrap" onclick="setActiveStyleSheet('wrap');return false;">[wrap]</a><a href="#" class="wrap" onclick="setActiveStyleSheet('nowrap');return false;">[no wrap]</a> 聽 <a class="nb" href="/lkml/mheaders/2005/12/15/408" onclick="this.href='/lkml/headers'+'/2005/12/15/408';">[headers]</a>聽 <a href="/lkml/bounce/2005/12/15/408">[forward]</a>聽 </div></td><td width="32">聽</td></tr><tr><td valign="top"><div class="es-jasper-simpleCalendar" baseurl="/lkml/"></div><div class="threadlist">Messages in this thread</div><ul class="threadlist"><li class="root"><a href="/lkml/2005/12/15/405">First message in thread</a></li><li><a href="/lkml/2005/12/15/405">john stultz</a><ul><li><a href="/lkml/2005/12/15/404">john stultz</a></li><li><a href="/lkml/2005/12/15/406">john stultz</a></li><li><a href="/lkml/2005/12/15/407">john stultz</a><ul><li><a href="/lkml/2006/1/3/509">Andrew Morton</a><ul><li><a href="/lkml/2006/1/4/72">john stultz</a><ul><li><a href="/lkml/2006/1/4/76">Andrew Morton</a></li></ul></li></ul></li></ul></li><li class="origin"><a href="">john stultz</a></li><li><a href="/lkml/2005/12/15/409">john stultz</a></li><li><a href="/lkml/2005/12/15/410">john stultz</a></li><li><a href="/lkml/2005/12/15/411">john stultz</a></li><li><a href="/lkml/2005/12/15/412">john stultz</a></li><li><a href="/lkml/2005/12/15/413">john stultz</a></li><li><a href="/lkml/2005/12/15/414">john stultz</a></li><li><a href="/lkml/2005/12/15/415">john stultz</a></li></ul></li></ul><div class="threadlist">Patch in this message</div><ul class="threadlist"><li><a href="/lkml/diff/2005/12/15/408/1">Get diff 1</a></li></ul></td><td width="32" rowspan="2" class="c" valign="top"><img src="/images/icornerl.gif" width="32" height="32" alt="/" /></td><td class="c" rowspan="2" valign="top" style="padding-top: 1em"><table><tr><td><table><tr><td class="lp">Date</td><td class="rp" itemprop="datePublished">Thu, 15 Dec 2005 18:07:26 -0700</td></tr><tr><td class="lp">From</td><td class="rp" itemprop="author">john stultz &lt;&gt;</td></tr><tr><td class="lp">Subject</td><td class="rp" itemprop="name">[PATCH 4/11] Time: Generic Timekeeping Infrastructure</td></tr></table></td><td></td></tr></table><pre itemprop="articleBody">Andrew, Andrew,<br /><br />This patch implements my generic timeofday framework. This common <br />infrastructure is intended to be used by any arch to reduce code <br />duplication, improve robustness in the face of late or lost ticks, and <br />enable finer granularity timekeeping.<br /><br />The major change with this code is that it allows timekeeping to be <br />independent of the timer interrupt. This provides a linear mapping <br />(ignoring ntp adjustments) between a hardware clocksource counter and <br />the time of day. This allows for lost ticks or other software delays to <br />not affect timekeeping.<br /><br />Included below is timeofday.c (which includes all the time of day <br />management and accessor functions), and minimal hooks into arch <br />independent code. This patch does not remove the current timekeeping <br />code, allowing architectures to move over when they are ready. <br /><br />This patch applies on top of my clocksource management patch.<br /><br />The patch does nothing without at least minimal architecture specific <br />hooks, and it should be able to be applied to a tree without affecting <br />the existing code.<br /><br />Andrew, please consider for inclusion into your tree.<br /><br />thanks<br />-john<br /><br />Signed-off-by: John Stultz &lt;johnstul&#64;us.ibm.com&gt;<br /><br /> Documentation/timekeeping.txt | 350 ++++++++++++++++++++<br /> drivers/char/hangcheck-timer.c | 5 <br /> include/asm-generic/timeofday.h | 30 +<br /> include/linux/time.h | 30 +<br /> include/linux/timeofday.h | 46 ++<br /> include/linux/timex.h | 2 <br /> include/sound/timer.h | 1 <br /> init/main.c | 2 <br /> kernel/hrtimer.c | 1 <br /> kernel/posix-timers.c | 2 <br /> kernel/time.c | 17 <br /> kernel/time/Makefile | 2 <br /> kernel/time/timeofday.c | 685 ++++++++++++++++++++++++++++++++++++++++<br /> kernel/timer.c | 7 <br /> 14 files changed, 1169 insertions(+), 11 deletions(-)<br /><br />linux-2.6.15-rc5_timeofday-core_B14.patch<br />============================================<br />diff --git a/Documentation/timekeeping.txt b/Documentation/timekeeping.txt<br />new file mode 100644<br />index 0000000..255fd56<br />--- /dev/null<br />+++ b/Documentation/timekeeping.txt<br />&#64;&#64; -0,0 +1,350 &#64;&#64;<br />+How timekeeping works with CONFIG_GENERIC_TIME<br />+========================================================================<br />+<br />+The generic timekeeping code maintains and allows access to the systems<br />+understanding of how much time has passed from a certain point. However, in<br />+order to measure the passing of time, the generic timekeeping code relies on<br />+the clocksource abstraction. A clocksource abstracts a free running counter<br />+who's value increases at a known frequency.<br />+<br />+In the generic timekeeping code, we use a pointer to a selected clocksource to<br />+measure the passing of time.<br />+<br />+struct clocksource *clock<br />+<br />+The clocksource has some limitations however. Since its likely of fixed width,<br />+it will not increment forever and will overflow. In order to still properly<br />+keep time, we must occasionally accumulate an interval of time. In the generic<br />+timekeeping code, we accumulate the amount of time system the system booted<br />+into the value system_time, which keeps nanosecond resolution in a ktime_t<br />+storage.<br />+<br />+ktime_t system_time<br />+<br />+Since its likely your system has not been running continually since midnight on<br />+the 1st of January in 1970, we must provide an offset from that time in<br />+accordance with conventions. This only occasionally changed (via<br />+settimeofday()) offset is the wall_time_offset value, which is also stored as a<br />+ktime_t.<br />+<br />+ktime_t wall_time_offset<br />+<br />+<br />+Since we accumulate time in intervals, we need a base cycle value that we can<br />+use to generate an offset from the time value kept in system_time. We store<br />+this value in cycle_last.<br />+<br />+cycle_t cycle_last;<br />+<br />+<br />+Further since all clocks drift somewhat from each other, we use the adjustment<br />+values provided via adjtimex() to correct our clocksource frequency for each<br />+interval. This frequency adjustment value is stored in ntp_adj.<br />+<br />+long ntp_adj;<br />+<br />+Now that we've covered the core global variables for timekeeping, lets look at<br />+how we maintain these values.<br />+<br />+As stated above, we want to avoid the clocksource from overflowing on us, so we<br />+accumulate a time interval periodically. This periodic accumulation function is<br />+called timeofday_periodic_hook(). In simplified pseudo code, it logically is<br />+presented as:<br />+<br />+timeofday_periodic_hook():<br />+ cycle_now = read_clocksource(clock)<br />+ cycle_delta = (cycle_now - cycle_last) &amp; clock-&gt;mask<br />+ nsec = cyc2ns(clock, cycle_delta, ntp_adj)<br />+ system_time += nsec<br />+ cycle_last = cycle_now<br />+<br />+ /* do other stuff */<br />+<br />+You can see we read the cycle value from the clocksource, calculate a cycle<br />+delta for the interval since we last called timeofday_periodic_hook(), convert<br />+that cycle delta to a nanosecond interval (for now ignore ntp_adj), add it to<br />+the system time and finally set our cycle_last value to cycle_now for the next<br />+interval. Using this simple algorithm we can correctly measure and record the<br />+passing of time.<br />+<br />+But just storing this info isn't very useful, we also want to make it available<br />+to be used elsewhere. So how do we provide a notion of how much time has passed<br />+inbetween calls to timeofday_periodic_hook()?<br />+<br />+First, lets create a function that calculates the time since the last call to<br />+timeofday_peridoic_hook().<br />+<br />+get_nsec_offset():<br />+ cycle_now = read_clocksource(clock)<br />+ cycle_delta = (cycle_now - cycle_last) &amp; clock-&gt;mask<br />+ nsec = cyc2ns(clock, cycle_delta, ntp_adj)<br />+ return nsec<br />+<br />+Here you can see, we read the clocksource, calculate a cycle interval, and<br />+convert that to a nanosecond interval. Just like how it is done in<br />+timeofday_periodic_hook!<br />+<br />+Now lets use this function to provide the number of nanoseconds that the system<br />+has been running:<br />+<br />+do_monotonic_clock():<br />+ return system_time + get_nsec_offset()<br />+<br />+Here we trivially add the nanosecond offset since the last<br />+timeofday_periodic_hook() to the value of system_time which was stored at the<br />+last timeofday_periodic_hook().<br />+<br />+Note that since we use the same method to calculate time intervals, assuming<br />+each function is atomic and the clocksource functions as it should, time cannot<br />+go backward!<br />+<br />+Now to get the time of day using the standard convention:<br />+<br />+do_gettimeofday():<br />+ return do_monotonic_clock() + wall_time_offset<br />+<br />+We simply add the wall_time_offset, and we have the number of nanoseconds since<br />+1970 began!<br />+<br />+<br />+Of course, in real life, things are not so static. We have to handle a number<br />+of dynamic values that may change and affect timekeeping. In order to do these<br />+safely, we must only change values in-between intervals. This means the<br />+periodic_hook call must handle these changes.<br />+<br />+Since clocksources can be changed while the system is running, we need to check<br />+for and possibly switch to using new clocksources in the periodic_hook call.<br />+Further, clocksources may change their frequency. Since this must be done only<br />+at a safe point, we use the update_callback function pointer (for more details,<br />+see "How to write a clocksource driver" below), this too must be done<br />+in-between intervals in the periodic_hook call. Finally, since the ntp<br />+adjustment made in the cyc2ns conversion is not static, we need to update the<br />+ntp state machine and get a calculate a new adjustment value.<br />+<br />+This adds some extra pseudo code to the timeofday_periodic_hook function:<br />+<br />+timeofday_periodic_hook():<br />+ cycle_now = read_clocksource(clock)<br />+ cycle_delta = (cycle_now - cycle_last) &amp; clock-&gt;mask<br />+ nsec = cyc2ns(clock, cycle_delta, ntp_adj)<br />+ system_time += nsec<br />+ cycle_last = cycle_now<br />+<br />+ next = get_next_clocksource()<br />+ if(next != clock):<br />+ cycle_last = read_clocksource(next)<br />+ clock = next<br />+<br />+ if(clock-&gt;update_callback):<br />+ clock-&gt;update_callback()<br />+<br />+ ntp_advance(nsec)<br />+ ppm = ntp_get_ppm_adjustment()<br />+ ntp_adj = ppm_to_mult_adj(clock, ppm)<br />+<br />+<br />+Unfortunately, the actual timeofday_periodic_hook code is not as simple as this<br />+pseudo code. For performance concerns, much has been done to pre-calculate<br />+values and use them repeatedly. Thus be aware that the code in timeofday.c is<br />+more complex, however the functional logic is the same.<br />+<br />+<br />+How to port an architecture to GENERIC_TIME<br />+========================================================================<br />+Porting an architecture to the GENERIC_TIME timekeeping code consists of moving<br />+a little bit of code around then deleting a fair amount. It is my hope that<br />+this will reduce the arch specific maintenance work around timekeeping.<br />+<br />+Porting an arch usually requires the following steps.<br />+<br />+1. Define CONFIG_GENERIC_TIME in the arches Kconfig<br />+2. Implementing the following functions<br />+ nsec_t read_persistent_clock(void)<br />+ void sync_persistent_clock(struct timespec ts)<br />+3. Removing all of the arch specific timekeeping code<br />+ do_gettimeofday()<br />+ do_settimeofday()<br />+ etc<br />+4. Implementing clocksource drivers<br />+ See "How to write a clocksource driver" for more details<br />+<br />+The exceptions to the above are:<br />+<br />+5. If the arch is has no continuous clocksource<br />+ A) Implement 1-3 in the above list.<br />+ B) Define CONFIG_IS_TICK_BASED in arches Kconfig<br />+ C) Implement the "long arch_getoffset(void)" function<br />+<br />+6. If the arch supports vsyscall gettimeofday (see x86_64 for reference)<br />+ A) Implement 1-4 in the above list<br />+ B) Define GENERIC_TIME_VSYSCALL<br />+ C) Implement arch_update_vsyscall_gtod()<br />+ D) Implement vsyscall gettimeofday (similar to __get_realtime_clock_ts)<br />+ E) Implement vread functions for supported clocksources<br />+<br />+<br />+<br />+How to write a clocksource driver.<br />+========================================================================<br />+First, a quick summary of what a clocksource driver provides.<br />+<br />+Simply put, a clocksource is a abstraction of a free running increasing<br />+counter. The abstraction provides the minimal amount of info for that counter<br />+to be usable for timekeeping. Those required values are:<br />+ 1. It's name<br />+ 2. A rating value for selection priority<br />+ 3. A read function pointer<br />+ 4. A mask value for correct twos-complement subtraction<br />+ 5. A mult and shift pair that approximate the counter frequency<br />+ mult/(2^shift) ~= nanoseconds per cycle<br />+<br />+Additionally, there are other optionally set values that allow for advanced<br />+functionality. Those values are:<br />+ 6. The update_callback function.<br />+ 7. The is_continuous flag.<br />+ 8. The vread function pointer<br />+ 9. The vdata pointer value<br />+<br />+<br />+Now lets go over these values in detail.<br />+<br />+1. Name.<br />+ The clocksource's name should be unique since it is used for both<br />+identification as well as for manually overriding the default clocksource<br />+selection. The name length must be shorter then 32 characters in order for it<br />+to be properly overrided.<br />+<br />+2. Rating value<br />+ This rating value is used as a priority value for clocksource<br />+selection. It has no direct connection to quality or physical properties of the<br />+clocksource, but is to be set and manipulated to guarantee that the best (by no<br />+specific metric) clocksource that will provide correct timekeeping is<br />+automatically selected. Rating suggestions can be found in<br />+include/linux/clocksource.h<br />+<br />+3. Read function pointer<br />+ This pointer should point to a function that returns an unsigned<br />+increasing cycle value from the clocksource. The value should have a coverage<br />+from zero to the maximum cycle value the clocksource can provide. This does not<br />+have to be direct hardware value and can also be a software counter. An example<br />+of a software counter is the jiffies clocksource.<br />+<br />+4. The mask value<br />+ This value should be the largest power of two that is smaller then the<br />+maximum cycle value. This allows twos complement subtraction to work on<br />+overflow boundary conditions if the max value is less then (cycle_t)-1. So for<br />+example, if we have a 16 bit counter (ie: one that loops to zero after<br />+0x0000FFFF), the mask would be 0xFFFF. So then when finding the cycle<br />+difference around a overflow, where now = 0x0013 and then = 0xFFEE, we can<br />+compute the cycle delta properly using the equation:<br />+ delta = (now - then)&amp;mask<br />+ delta = (0x0013 - 0xFFEE) &amp; 0xFFFF<br />+ delta = 0xFFFF0025 &amp; 0xFFFF /* note the unmasked negative value */<br />+ delta = 0x25<br />+<br />+5. The mult and shift pair<br />+ These 32bit values approximate the nanosecond per cycle frequency of<br />+the clocksource using the equation: mult/(2^shift). If you have a khz or hz<br />+frequency value, the mult value for a given shift value can be easily<br />+calculated using the clocksource_hz2mult() and clocksource_khz2mult() helper<br />+functions. When selecting a shift value, it is important to be careful. Larger<br />+shift values give a finer precision in the cycle to nanosecond conversion and<br />+allows for more exact NTP adjustments. However if you select too large a shift<br />+value, the resulting mult value might overflow a cycle_t * mult computation.<br />+<br />+<br />+So if you have a simple hardware counter that does not change frequency,<br />+filling in the above should be sufficient for a functional clocksource. But<br />+read on for details on implementing a more complex clocksource.<br />+<br />+6. The update_callback function pointer.<br />+ If this function pointer is non-NULL, it will be called every periodic<br />+hook when it is safe for the clocksource to change its state. This would be<br />+necessary in the case where the counter frequency changes, for example. One<br />+user of this function pointer is the TSC clocksource. When the TSC frequency<br />+changes (which may occur if the cpu changes frequency) we need to notify the<br />+clocksource at a safe point where that state may change. Thus, if the TSC has<br />+changed frequency we set the new mult/shift values in the update_callback<br />+function.<br />+<br />+7. The is_continuous flag.<br />+ This flag variable (0 if false, 1 if true) denotes that the clocksource<br />+is continuous. This means that it is a purely hardware driven clocksource and<br />+is not dependent on any software code to run for it to increment properly. This<br />+denotation will be useful in the future when timer ticks may be disabled for<br />+long periods of time. Doing so using software clocksources, like the jiffies<br />+clocksource, would cause timekeeping problems.<br />+<br />+8. The vread function pointer.<br />+ This function pointer points to a user-space accessible function that<br />+reads the clocksource. This is used in userspace gettimeofday implementations<br />+to improve performance. See the x86-64 TSC clocksource implementation for an<br />+example.<br />+<br />+8. The vdata pointer.<br />+ This pointer is passed to the vread function pointer in a userspace<br />+gettimeofday implementation. Its usage is dependent on the vread<br />+implementation, but if the pointer points to data, that data must be readable<br />+from userspace.<br />+<br />+<br />+Now lets write a quick clocksource for an imaginary bit of hardware. Here are<br />+the specs:<br />+<br />+ A 32bit counter can be found at the MMIO address 0xFEEDF000. It runs at<br />+100Mhz. To enable it, the the low bit of the address 0xFEEDF0F0 must be set to<br />+one.<br />+<br />+So lets start out an empty cool-counter.c file, and define the clocksource.<br />+<br />+#include &lt;linux/clocksource.h&gt;<br />+#include &lt;linux/init.h&gt;<br />+#include &lt;asm/io.h&gt;<br />+<br />+#define COOL_READ_PTR 0xFEEDF000<br />+#define COOL_START_PTR 0xFEEDF0F0<br />+<br />+static __iomem *cool_ptr = COOL_READ_PTR;<br />+<br />+struct clocksource clocksource_cool<br />+{<br />+ .name = "cool",<br />+ .rating = 200, /* its a pretty decent clock */<br />+ .mask = 0xFFFFFFFF, /* 32 bits */<br />+ .mult = 0, /*to be computed */<br />+ .shift = 10,<br />+}<br />+<br />+<br />+Now let's write the read function:<br />+<br />+cycle_t cool_counter_read(void)<br />+{<br />+ cycle_t ret = readl(cool_ptr);<br />+ return ret;<br />+}<br />+<br />+Finally, lets write the init function:<br />+<br />+void cool_counter_init(void)<br />+{<br />+ __iomem *ptr = COOL_START_PTR;<br />+ u32 val;<br />+<br />+ /* start the counter */<br />+ val = readl(ptr);<br />+ val |= 0x1;<br />+ writel(val, ptr);<br />+<br />+ /* finish initializing the clocksource */<br />+ clocksource_cool.read = cool_counter_read;<br />+ clocksource_cool.mult = clocksource_khz2mult(100000,<br />+ clocksource_cool.shift);<br />+<br />+ /* register the clocksource */<br />+ register_clocksource(&amp;clocksource_cool);<br />+}<br />+module_init(cool_counter_init);<br />+<br />+<br />+Now wasn't that easy!<br />diff --git a/drivers/char/hangcheck-timer.c b/drivers/char/hangcheck-timer.c<br />index 66e53dd..59d8c48 100644<br />--- a/drivers/char/hangcheck-timer.c<br />+++ b/drivers/char/hangcheck-timer.c<br />&#64;&#64; -49,6 +49,7 &#64;&#64;<br /> #include &lt;linux/delay.h&gt;<br /> #include &lt;asm/uaccess.h&gt;<br /> #include &lt;linux/sysrq.h&gt;<br />+#include &lt;linux/timeofday.h&gt;<br /> <br /> <br /> #define VERSION_STR "0.9.0"<br />&#64;&#64; -130,8 +131,12 &#64;&#64; __setup("hcheck_dump_tasks", hangcheck_p<br /> #endif<br /> <br /> #ifdef HAVE_MONOTONIC<br />+#ifndef CONFIG_GENERIC_TIME<br /> extern unsigned long long monotonic_clock(void);<br /> #else<br />+#define monotonic_clock() ktime_to_ns(get_monotonic_clock())<br />+#endif<br />+#else<br /> static inline unsigned long long monotonic_clock(void)<br /> {<br /> # ifdef __s390__<br />diff --git a/include/asm-generic/timeofday.h b/include/asm-generic/timeofday.h<br />new file mode 100644<br />index 0000000..c3dbcd0<br />--- /dev/null<br />+++ b/include/asm-generic/timeofday.h<br />&#64;&#64; -0,0 +1,30 &#64;&#64;<br />+/* linux/include/asm-generic/timeofday.h<br />+ *<br />+ * This file contains the asm-generic interface<br />+ * to the arch specific calls used by the time of day subsystem<br />+ */<br />+#ifndef _ASM_GENERIC_TIMEOFDAY_H<br />+#define _ASM_GENERIC_TIMEOFDAY_H<br />+#include &lt;linux/types.h&gt;<br />+#include &lt;linux/time.h&gt;<br />+#include &lt;linux/timex.h&gt;<br />+#include &lt;linux/timeofday.h&gt;<br />+#include &lt;linux/clocksource.h&gt;<br />+<br />+#include &lt;asm/div64.h&gt;<br />+<br />+#ifdef CONFIG_GENERIC_TIME<br />+/* Required externs */<br />+extern nsec_t read_persistent_clock(void);<br />+extern void sync_persistent_clock(struct timespec ts);<br />+<br />+#ifdef CONFIG_GENERIC_TIME_VSYSCALL<br />+extern void arch_update_vsyscall_gtod(struct timespec wall_time,<br />+ cycle_t offset_base, struct clocksource* clock,<br />+ int ntp_adj);<br />+#else<br />+# define arch_update_vsyscall_gtod(x,y,z,w) do { } while(0)<br />+#endif /* CONFIG_GENERIC_TIME_VSYSCALL */<br />+<br />+#endif /* CONFIG_GENERIC_TIME */<br />+#endif<br />diff --git a/include/linux/time.h b/include/linux/time.h<br />index 1201155..4dbd133 100644<br />--- a/include/linux/time.h<br />+++ b/include/linux/time.h<br />&#64;&#64; -27,6 +27,10 &#64;&#64; struct timezone {<br /> <br /> #ifdef __KERNEL__<br /> <br />+/* timeofday base types */<br />+typedef s64 nsec_t;<br />+typedef u64 cycle_t;<br />+<br /> /* Parameters used to convert the timespec values: */<br /> #define MSEC_PER_SEC 1000L<br /> #define USEC_PER_SEC 1000000L<br />&#64;&#64; -50,12 +54,6 &#64;&#64; extern void set_normalized_timespec(stru<br /> #define timespec_valid(ts) \<br /> (((ts)-&gt;tv_sec &gt;= 0) &amp;&amp; (((unsigned) (ts)-&gt;tv_nsec) &lt; NSEC_PER_SEC))<br /> <br />-/*<br />- * 64-bit nanosec type. Large enough to span 292+ years in nanosecond<br />- * resolution. Ought to be enough for a while.<br />- */<br />-typedef s64 nsec_t;<br />-<br /> extern struct timespec xtime;<br /> extern struct timespec wall_to_monotonic;<br /> extern seqlock_t xtime_lock;<br />&#64;&#64; -79,8 +77,6 &#64;&#64; struct itimerval;<br /> extern int do_setitimer(int which, struct itimerval *value,<br /> struct itimerval *ovalue);<br /> extern int do_getitimer(int which, struct itimerval *value);<br />-extern void getnstimeofday(struct timespec *tv);<br />-extern void getnstimestamp(struct timespec *ts);<br /> <br /> extern struct timespec timespec_trunc(struct timespec t, unsigned gran);<br /> <br />&#64;&#64; -125,6 +121,24 &#64;&#64; extern struct timespec ns_to_timespec(co<br /> */<br /> extern struct timeval ns_to_timeval(const nsec_t nsec);<br /> <br />+static inline void normalize_timespec(struct timespec *ts)<br />+{<br />+ while (unlikely((unsigned long)ts-&gt;tv_nsec &gt;= NSEC_PER_SEC)) {<br />+ ts-&gt;tv_nsec -= NSEC_PER_SEC;<br />+ ts-&gt;tv_sec++;<br />+ }<br />+}<br />+<br />+static inline void timespec_add_ns(struct timespec *a, nsec_t ns)<br />+{<br />+ while(unlikely(ns &gt;= NSEC_PER_SEC)) {<br />+ ns -= NSEC_PER_SEC;<br />+ a-&gt;tv_sec++;<br />+ }<br />+ a-&gt;tv_nsec += ns;<br />+ normalize_timespec(a);<br />+}<br />+<br /> #endif /* __KERNEL__ */<br /> <br /> #define NFDBITS __NFDBITS<br />diff --git a/include/linux/timeofday.h b/include/linux/timeofday.h<br />new file mode 100644<br />index 0000000..5222c4c<br />--- /dev/null<br />+++ b/include/linux/timeofday.h<br />&#64;&#64; -0,0 +1,46 &#64;&#64;<br />+/* linux/include/linux/timeofday.h<br />+ *<br />+ * This file contains the interface to the time of day subsystem<br />+ */<br />+#ifndef _LINUX_TIMEOFDAY_H<br />+#define _LINUX_TIMEOFDAY_H<br />+#include &lt;linux/calc64.h&gt;<br />+#include &lt;linux/types.h&gt;<br />+#include &lt;linux/ktime.h&gt;<br />+#include &lt;linux/time.h&gt;<br />+#include &lt;linux/timex.h&gt;<br />+<br />+#ifdef CONFIG_GENERIC_TIME<br />+<br />+/* Kernel internal interfaces */<br />+extern ktime_t get_monotonic_clock(void);<br />+extern ktime_t get_realtime_clock(void);<br />+extern ktime_t get_realtime_offset(void);<br />+<br />+/* Timepsec based interfaces for user space functionality */<br />+extern void get_realtime_clock_ts(struct timespec *ts);<br />+extern void get_monotonic_clock_ts(struct timespec *ts);<br />+<br />+/* legacy timeofday interfaces*/<br />+#define getnstimeofday(ts) get_realtime_clock_ts(ts)<br />+#define getnstimestamp(ts) get_monotonic_clock_ts(ts)<br />+extern void getnstimeofday(struct timespec *ts);<br />+extern void do_gettimeofday(struct timeval *tv);<br />+extern int do_settimeofday(struct timespec *ts);<br />+<br />+/* Internal functions */<br />+extern int timeofday_is_continuous(void);<br />+extern void timeofday_init(void);<br />+<br />+#ifndef CONFIG_IS_TICK_BASED<br />+#define arch_getoffset() (0)<br />+#else<br />+extern unsigned long arch_getoffset(void);<br />+#endif<br />+<br />+#else /* CONFIG_GENERIC_TIME */<br />+#define timeofday_init()<br />+extern void getnstimeofday(struct timespec *ts);<br />+extern void getnstimestamp(struct timespec *ts);<br />+#endif /* CONFIG_GENERIC_TIME */<br />+#endif /* _LINUX_TIMEOFDAY_H */<br />diff --git a/include/linux/timex.h b/include/linux/timex.h<br />index e35c683..91a7fd6 100644<br />--- a/include/linux/timex.h<br />+++ b/include/linux/timex.h<br />&#64;&#64; -310,6 +310,7 &#64;&#64; extern int ntp_leapsecond(struct timespe<br /> __x &lt; 0 ? -(-__x &gt;&gt; __s) : __x &gt;&gt; __s; \<br /> })<br /> <br />+#ifndef CONFIG_GENERIC_TIME<br /> <br /> #ifdef CONFIG_TIME_INTERPOLATION<br /> <br />&#64;&#64; -365,6 +366,7 &#64;&#64; time_interpolator_reset(void)<br /> }<br /> <br /> #endif /* !CONFIG_TIME_INTERPOLATION */<br />+#endif /* !CONFIG_GENERIC_TIME */<br /> <br /> #endif /* KERNEL */<br /> <br />diff --git a/include/sound/timer.h b/include/sound/timer.h<br />index b55f38a..6a31988 100644<br />--- a/include/sound/timer.h<br />+++ b/include/sound/timer.h<br />&#64;&#64; -25,6 +25,7 &#64;&#64;<br /> <br /> #include &lt;sound/asound.h&gt;<br /> #include &lt;linux/interrupt.h&gt;<br />+#include &lt;linux/timeofday.h&gt;<br /> <br /> typedef enum sndrv_timer_class snd_timer_class_t;<br /> typedef enum sndrv_timer_slave_class snd_timer_slave_class_t;<br />diff --git a/init/main.c b/init/main.c<br />index 0d6475e..fd08b56 100644<br />--- a/init/main.c<br />+++ b/init/main.c<br />&#64;&#64; -47,6 +47,7 &#64;&#64;<br /> #include &lt;linux/rmap.h&gt;<br /> #include &lt;linux/mempolicy.h&gt;<br /> #include &lt;linux/key.h&gt;<br />+#include &lt;linux/timeofday.h&gt;<br /> #include &lt;net/sock.h&gt;<br /> <br /> #include &lt;asm/io.h&gt;<br />&#64;&#64; -489,6 +490,7 &#64;&#64; asmlinkage void __init start_kernel(void<br /> init_timers();<br /> hrtimers_init();<br /> softirq_init();<br />+ timeofday_init();<br /> time_init();<br /> <br /> /*<br />diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c<br />index 3a08078..2db11ed 100644<br />--- a/kernel/hrtimer.c<br />+++ b/kernel/hrtimer.c<br />&#64;&#64; -29,6 +29,7 &#64;&#64;<br /> #include &lt;linux/percpu.h&gt;<br /> #include &lt;linux/hrtimer.h&gt;<br /> #include &lt;linux/notifier.h&gt;<br />+#include &lt;linux/timeofday.h&gt;<br /> #include &lt;linux/syscalls.h&gt;<br /> #include &lt;linux/interrupt.h&gt;<br /> <br />diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c<br />index 9e66e61..771f3a6 100644<br />--- a/kernel/posix-timers.c<br />+++ b/kernel/posix-timers.c<br />&#64;&#64; -34,7 +34,7 &#64;&#64;<br /> #include &lt;linux/smp_lock.h&gt;<br /> #include &lt;linux/interrupt.h&gt;<br /> #include &lt;linux/slab.h&gt;<br />-#include &lt;linux/time.h&gt;<br />+#include &lt;linux/timeofday.h&gt;<br /> <br /> #include &lt;asm/uaccess.h&gt;<br /> #include &lt;asm/semaphore.h&gt;<br />diff --git a/kernel/time.c b/kernel/time.c<br />index 6529064..6db0113 100644<br />--- a/kernel/time.c<br />+++ b/kernel/time.c<br />&#64;&#64; -38,6 +38,7 &#64;&#64;<br /> <br /> #include &lt;asm/uaccess.h&gt;<br /> #include &lt;asm/unistd.h&gt;<br />+#include &lt;linux/timeofday.h&gt;<br /> <br /> /* <br /> * The timezone where the local system is located. Used as a default by some<br />&#64;&#64; -128,6 +129,7 &#64;&#64; asmlinkage long sys_gettimeofday(struct <br /> * as real UNIX machines always do it. This avoids all headaches about<br /> * daylight saving times and warping kernel clocks.<br /> */<br />+#ifndef CONFIG_GENERIC_TIME<br /> static inline void warp_clock(void)<br /> {<br /> write_seqlock_irq(&amp;xtime_lock);<br />&#64;&#64; -137,6 +139,18 &#64;&#64; static inline void warp_clock(void)<br /> write_sequnlock_irq(&amp;xtime_lock);<br /> clock_was_set();<br /> }<br />+#else /* !CONFIG_GENERIC_TIME */<br />+/* XXX - this is somewhat cracked out and should<br />+ be checked -johnstul&#64;us.ibm.com<br />+*/<br />+static inline void warp_clock(void)<br />+{<br />+ struct timespec ts;<br />+ getnstimeofday(&amp;ts);<br />+ ts.tv_sec += sys_tz.tz_minuteswest * 60;<br />+ do_settimeofday(&amp;ts);<br />+}<br />+#endif /* !CONFIG_GENERIC_TIME */<br /> <br /> /*<br /> * In case for some reason the CMOS clock has not already been running<br />&#64;&#64; -479,6 +493,7 &#64;&#64; struct timespec timespec_trunc(struct ti<br /> }<br /> EXPORT_SYMBOL(timespec_trunc);<br /> <br />+#ifndef CONFIG_GENERIC_TIME<br /> #ifdef CONFIG_TIME_INTERPOLATION<br /> void getnstimeofday (struct timespec *tv)<br /> {<br />&#64;&#64; -586,6 +601,8 &#64;&#64; void getnstimestamp(struct timespec *ts)<br /> }<br /> EXPORT_SYMBOL_GPL(getnstimestamp);<br /> <br />+#endif /* !CONFIG_GENERIC_TIME */<br />+<br /> /* Converts Gregorian date to seconds since 1970-01-01 00:00:00.<br /> * Assumes input in normal date format, i.e. 1980-12-31 23:59:59<br /> * =&gt; year=1980, mon=12, day=31, hour=23, min=59, sec=59.<br />diff --git a/kernel/time/Makefile b/kernel/time/Makefile<br />index e1dfd8e..4bdb5e6 100644<br />--- a/kernel/time/Makefile<br />+++ b/kernel/time/Makefile<br />&#64;&#64; -1 +1 &#64;&#64;<br />-obj-y += clocksource.o jiffies.o<br />+obj-y += clocksource.o jiffies.o timeofday.o<br />diff --git a/kernel/time/timeofday.c b/kernel/time/timeofday.c<br />new file mode 100644<br />index 0000000..2aa595d<br />--- /dev/null<br />+++ b/kernel/time/timeofday.c<br />&#64;&#64; -0,0 +1,685 &#64;&#64;<br />+/*<br />+ * linux/kernel/time/timeofday.c<br />+ *<br />+ * This file contains the functions which access and manage<br />+ * the system's time of day functionality.<br />+ *<br />+ * Copyright (C) 2003, 2004, 2005 IBM, John Stultz (johnstul&#64;us.ibm.com)<br />+ *<br />+ * This program is free software; you can redistribute it and/or modify<br />+ * it under the terms of the GNU General Public License as published by<br />+ * the Free Software Foundation; either version 2 of the License, or<br />+ * (at your option) any later version.<br />+ *<br />+ * This program is distributed in the hope that it will be useful,<br />+ * but WITHOUT ANY WARRANTY; without even the implied warranty of<br />+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br />+ * GNU General Public License for more details.<br />+ *<br />+ * You should have received a copy of the GNU General Public License<br />+ * along with this program; if not, write to the Free Software<br />+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.<br />+ *<br />+ * TODO WishList:<br />+ * o See XXX's below.<br />+ */<br />+<br />+#include &lt;linux/clocksource.h&gt;<br />+#include &lt;linux/timeofday.h&gt;<br />+#include &lt;linux/jiffies.h&gt;<br />+#include &lt;linux/sysdev.h&gt;<br />+#include &lt;linux/timer.h&gt;<br />+#include &lt;linux/module.h&gt;<br />+#include &lt;linux/ktime.h&gt;<br />+#include &lt;linux/timex.h&gt;<br />+#include &lt;linux/sched.h&gt;<br />+<br />+#include &lt;asm/timeofday.h&gt;<br />+<br />+/* Periodic hook interval */<br />+#define PERIODIC_INTERVAL_MS 50<br />+<br />+/* [ktime_t based variables]<br />+ * system_time:<br />+ * Monotonically increasing counter of the number of nanoseconds<br />+ * since boot.<br />+ * wall_time_offset:<br />+ * Offset added to system_time to provide accurate time-of-day<br />+ */<br />+static ktime_t system_time;<br />+static ktime_t wall_time_offset;<br />+<br />+/* [timespec based variables]<br />+ * These variables mirror teh ktime_t based variables to avoid<br />+ * performance issues in the userspace syscall paths.<br />+ *<br />+ * wall_time_ts:<br />+ * timespec holding the current wall time.<br />+ * NOTE: clock_was_set() must be called when this value changes.<br />+ * mono_time_ts:<br />+ * timespec holding the current monotonic time.<br />+ * monotonic_time_offset_ts:<br />+ * timespec holding the difference between wall and monotonic time.<br />+ */<br />+static struct timespec wall_time_ts;<br />+static struct timespec mono_time_ts;<br />+static struct timespec monotonic_time_offset_ts;<br />+<br />+/* [cycle based variables]<br />+ * cycle_last:<br />+ * Value of the clocksource at the last timeofday_periodic_hook()<br />+ * (adjusted only minorly to account for rounded off cycles)<br />+ */<br />+static cycle_t cycle_last;<br />+<br />+/* [clocksource_interval variables]<br />+ * ts_interval:<br />+ * This clocksource_interval is used in the fixed interval<br />+ * cycles to nanosecond calculation.<br />+ * INTERVAL_LEN:<br />+ * This constant is the requested fixed interval period<br />+ * in nanoseconds.<br />+ */<br />+struct clocksource_interval ts_interval;<br />+#define INTERVAL_LEN ((PERIODIC_INTERVAL_MS-1)*1000000)<br />+<br />+/* [clocksource data]<br />+ * clock:<br />+ * current clocksource pointer<br />+ */<br />+static struct clocksource *clock;<br />+<br />+/* [NTP adjustment]<br />+ * ntp_adj:<br />+ * value of the current ntp adjustment, stored in<br />+ * clocksource multiplier units.<br />+ */<br />+static int ntp_adj;<br />+<br />+/* [locks]<br />+ * system_time_lock:<br />+ * generic lock for all locally scoped time values<br />+ */<br />+static seqlock_t system_time_lock = SEQLOCK_UNLOCKED;<br />+<br />+<br />+/* [suspend/resume info]<br />+ * time_suspend_state:<br />+ * variable that keeps track of suspend state<br />+ * suspend_start:<br />+ * start of the suspend call<br />+ */<br />+static enum {<br />+ TIME_RUNNING,<br />+ TIME_SUSPENDED<br />+} time_suspend_state = TIME_RUNNING;<br />+<br />+static nsec_t suspend_start;<br />+<br />+/* [Soft-Timers]<br />+ * timeofday_timer:<br />+ * soft-timer used to call timeofday_periodic_hook()<br />+ */<br />+static struct timer_list timeofday_timer;<br />+<br />+/**<br />+ * update_legacy_time_values - sync legacy time values<br />+ *<br />+ * This function is necessary for a smooth transition to the<br />+ * new timekeeping code. When all the xtime/wall_to_monotonic<br />+ * users are converted this function can be removed.<br />+ *<br />+ * system_time_lock must be held by the caller<br />+ */<br />+static void update_legacy_time_values(void)<br />+{<br />+ unsigned long flags;<br />+<br />+ write_seqlock_irqsave(&amp;xtime_lock, flags);<br />+<br />+ xtime = wall_time_ts;<br />+ set_normalized_timespec(&amp;wall_to_monotonic,<br />+ -monotonic_time_offset_ts.tv_sec,<br />+ -monotonic_time_offset_ts.tv_nsec);<br />+<br />+ write_sequnlock_irqrestore(&amp;xtime_lock, flags);<br />+<br />+ /* since time state has changed, notify vsyscall code */<br />+ arch_update_vsyscall_gtod(wall_time_ts, cycle_last, clock, ntp_adj);<br />+}<br />+<br />+/**<br />+ * __get_nsec_offset - Returns nanoseconds since last call to periodic_hook<br />+ *<br />+ * private function, must hold system_time_lock lock when being<br />+ * called. Returns the number of nanoseconds since the<br />+ * last call to timeofday_periodic_hook() (adjusted by NTP scaling)<br />+ */<br />+static inline nsec_t __get_nsec_offset(void)<br />+{<br />+ cycle_t cycle_now, cycle_delta;<br />+ nsec_t ns_offset;<br />+<br />+ /* read clocksource: */<br />+ cycle_now = read_clocksource(clock);<br />+<br />+ /* calculate the delta since the last timeofday_periodic_hook: */<br />+ cycle_delta = (cycle_now - cycle_last) &amp; clock-&gt;mask;<br />+<br />+ /* convert to nanoseconds: */<br />+ ns_offset = cyc2ns(clock, ntp_adj, cycle_delta);<br />+<br />+ /*<br />+ * special case for jiffies tick/offset based systems,<br />+ * add arch-specific offset:<br />+ */<br />+ ns_offset += arch_getoffset();<br />+<br />+ return ns_offset;<br />+}<br />+<br />+/**<br />+ * __get_monotonic_clock - Returns monotonically increasing nanoseconds<br />+ *<br />+ * private function, must hold system_time_lock lock when being<br />+ * called. Returns the monotonically increasing number of<br />+ * nanoseconds since the system booted (adjusted by NTP scaling)<br />+ */<br />+static inline ktime_t __get_monotonic_clock(void)<br />+{<br />+ nsec_t offset = __get_nsec_offset();<br />+ return ktime_add_ns(system_time, offset);<br />+}<br />+<br />+/**<br />+ * get_monotonic_clock - Returns monotonic time in ktime_t format<br />+ *<br />+ * Returns the monotonically increasing number of nanoseconds<br />+ * since the system booted via __monotonic_clock()<br />+ */<br />+ktime_t get_monotonic_clock(void)<br />+{<br />+ unsigned long seq;<br />+ ktime_t ret;<br />+<br />+ /* atomically read __get_monotonic_clock() */<br />+ do {<br />+ seq = read_seqbegin(&amp;system_time_lock);<br />+<br />+ ret = __get_monotonic_clock();<br />+<br />+ } while (read_seqretry(&amp;system_time_lock, seq));<br />+<br />+ return ret;<br />+}<br />+<br />+EXPORT_SYMBOL_GPL(get_monotonic_clock);<br />+<br />+/**<br />+ * get_realtime_clock - Returns the timeofday in ktime_t format<br />+ *<br />+ * Returns the wall time in ktime_t format. The resolution<br />+ * is nanoseconds<br />+ */<br />+ktime_t get_realtime_clock(void)<br />+{<br />+ unsigned long seq;<br />+ ktime_t ret;<br />+<br />+ /* atomically read __get_monotonic_clock() */<br />+ do {<br />+ seq = read_seqbegin(&amp;system_time_lock);<br />+<br />+ ret = __get_monotonic_clock();<br />+ ret = ktime_add(ret, wall_time_offset);<br />+<br />+ } while (read_seqretry(&amp;system_time_lock, seq));<br />+<br />+ return ret;<br />+}<br />+<br />+/**<br />+ * get_realtime_offset - Returns the offset of realtime clock<br />+ *<br />+ * Returns the number of nanoseconds in ktime_t storage format which<br />+ * represents the offset of the realtime clock to the the monotonic clock<br />+ */<br />+ktime_t get_realtime_offset(void)<br />+{<br />+ unsigned long seq;<br />+ ktime_t ret;<br />+<br />+ /* atomically read wall_time_offset */<br />+ do {<br />+ seq = read_seqbegin(&amp;system_time_lock);<br />+<br />+ ret = wall_time_offset;<br />+<br />+ } while (read_seqretry(&amp;system_time_lock, seq));<br />+<br />+ return ret;<br />+}<br />+<br />+/**<br />+ * get_monotonic_clock_ts - Returns monotonic time in timespec format<br />+ * &#64;ts: pointer to the timespec to be set<br />+ *<br />+ * Returns a timespec of nanoseconds since the system booted and<br />+ * store the result in the timespec variable pointed to by &#64;ts<br />+ */<br />+void get_monotonic_clock_ts(struct timespec *ts)<br />+{<br />+ unsigned long seq;<br />+ nsec_t offset;<br />+<br />+ do {<br />+ seq = read_seqbegin(&amp;system_time_lock);<br />+<br />+ *ts = mono_time_ts;<br />+ offset = __get_nsec_offset();<br />+ } while (read_seqretry(&amp;system_time_lock, seq));<br />+<br />+ timespec_add_ns(ts, offset);<br />+}<br />+<br />+/**<br />+ * __get_realtime_clock_ts - Returns the time of day in a timespec<br />+ * &#64;ts: pointer to the timespec to be set<br />+ *<br />+ * Returns the time of day in a timespec. Used by<br />+ * do_gettimeofday() and get_realtime_clock_ts().<br />+ */<br />+static inline void __get_realtime_clock_ts(struct timespec *ts)<br />+{<br />+ unsigned long seq;<br />+ nsec_t nsecs;<br />+<br />+ do {<br />+ seq = read_seqbegin(&amp;system_time_lock);<br />+<br />+ *ts = wall_time_ts;<br />+ nsecs = __get_nsec_offset();<br />+<br />+ } while (read_seqretry(&amp;system_time_lock, seq));<br />+<br />+ timespec_add_ns(ts, nsecs);<br />+}<br />+<br />+/**<br />+ * get_realtime_clock_ts - Returns the time of day in a timespec<br />+ * &#64;ts: pointer to the timespec to be set<br />+ *<br />+ * Returns the time of day in a timespec.<br />+ */<br />+void get_realtime_clock_ts(struct timespec *ts)<br />+{<br />+ __get_realtime_clock_ts(ts);<br />+}<br />+<br />+EXPORT_SYMBOL(get_realtime_clock_ts);<br />+<br />+/**<br />+ * do_gettimeofday - Returns the time of day in a timeval<br />+ * &#64;tv: pointer to the timeval to be set<br />+ *<br />+ * NOTE: Users should be converted to using get_realtime_clock_ts()<br />+ */<br />+void do_gettimeofday(struct timeval *tv)<br />+{<br />+ struct timespec now;<br />+<br />+ __get_realtime_clock_ts(&amp;now);<br />+ tv-&gt;tv_sec = now.tv_sec;<br />+ tv-&gt;tv_usec = now.tv_nsec/1000;<br />+}<br />+<br />+EXPORT_SYMBOL(do_gettimeofday);<br />+<br />+/**<br />+ * do_settimeofday - Sets the time of day<br />+ * &#64;tv: pointer to the timespec variable containing the new time<br />+ *<br />+ * Sets the time of day to the new time and update NTP and notify hrtimers<br />+ */<br />+int do_settimeofday(struct timespec *tv)<br />+{<br />+ unsigned long flags;<br />+ ktime_t newtime;<br />+<br />+ newtime = timespec_to_ktime(*tv);<br />+<br />+ write_seqlock_irqsave(&amp;system_time_lock, flags);<br />+<br />+ /* calculate the new offset from the monotonic clock */<br />+ wall_time_offset = ktime_sub(newtime, __get_monotonic_clock());<br />+<br />+ /* update the internal timespec variables */<br />+ wall_time_ts = ktime_to_timespec(ktime_add(system_time,<br />+ wall_time_offset));<br />+ monotonic_time_offset_ts = ktime_to_timespec(wall_time_offset);<br />+<br />+ ntp_clear();<br />+ update_legacy_time_values();<br />+<br />+ write_sequnlock_irqrestore(&amp;system_time_lock, flags);<br />+<br />+ /* signal hrtimers about time change */<br />+ clock_was_set();<br />+<br />+ return 0;<br />+}<br />+<br />+EXPORT_SYMBOL(do_settimeofday);<br />+<br />+/**<br />+ * __increment_system_time - Increments system time<br />+ * &#64;delta: nanosecond delta to add to the time variables<br />+ *<br />+ * Private helper that increments system_time and related<br />+ * timekeeping variables.<br />+ */<br />+static inline void __increment_system_time(nsec_t delta)<br />+{<br />+ system_time = ktime_add_ns(system_time, delta);<br />+ timespec_add_ns(&amp;wall_time_ts, delta);<br />+ timespec_add_ns(&amp;mono_time_ts, delta);<br />+}<br />+<br />+/**<br />+ * timeofday_suspend_hook - allows the timeofday subsystem to be shutdown<br />+ * &#64;dev: unused<br />+ * &#64;state: unused<br />+ *<br />+ * This function allows the timeofday subsystem to be shutdown for a period<br />+ * of time. Called when going into suspend/hibernate mode.<br />+ */<br />+static int timeofday_suspend_hook(struct sys_device *dev, pm_message_t state)<br />+{<br />+ unsigned long flags;<br />+<br />+ write_seqlock_irqsave(&amp;system_time_lock, flags);<br />+<br />+ BUG_ON(time_suspend_state != TIME_RUNNING);<br />+<br />+ /*<br />+ * First off, save suspend start time<br />+ * then quickly accumulate the current nsec offset.<br />+ * These two calls hopefully occur quickly<br />+ * because the difference between reads will<br />+ * accumulate as time drift on resume.<br />+ */<br />+ suspend_start = read_persistent_clock();<br />+ __increment_system_time(__get_nsec_offset());<br />+<br />+ time_suspend_state = TIME_SUSPENDED;<br />+<br />+ write_sequnlock_irqrestore(&amp;system_time_lock, flags);<br />+<br />+ return 0;<br />+}<br />+<br />+/**<br />+ * timeofday_resume_hook - Resumes the timeofday subsystem.<br />+ * &#64;dev: unused<br />+ *<br />+ * This function resumes the timeofday subsystem from a previous call<br />+ * to timeofday_suspend_hook.<br />+ */<br />+static int timeofday_resume_hook(struct sys_device *dev)<br />+{<br />+ nsec_t suspend_end, suspend_time;<br />+ unsigned long flags;<br />+<br />+ write_seqlock_irqsave(&amp;system_time_lock, flags);<br />+<br />+ BUG_ON(time_suspend_state != TIME_SUSPENDED);<br />+<br />+ /*<br />+ * Read persistent clock to mark the end of<br />+ * the suspend interval then rebase the<br />+ * cycle_last to current clocksource value.<br />+ * Again, time between these two calls will<br />+ * not be accounted for and will show up as<br />+ * time drift.<br />+ */<br />+ suspend_end = read_persistent_clock();<br />+ cycle_last = read_clocksource(clock);<br />+<br />+ /* calculate suspend time and add it to system time: */<br />+ suspend_time = suspend_end - suspend_start;<br />+ __increment_system_time(suspend_time);<br />+<br />+ ntp_clear();<br />+<br />+ time_suspend_state = TIME_RUNNING;<br />+<br />+ update_legacy_time_values();<br />+<br />+ write_sequnlock_irqrestore(&amp;system_time_lock, flags);<br />+<br />+ /* notify the posix timers if wall_time_offset changed */<br />+ clock_was_set();<br />+<br />+ return 0;<br />+}<br />+<br />+/* sysfs resume/suspend bits */<br />+static struct sysdev_class timeofday_sysclass = {<br />+ .resume = timeofday_resume_hook,<br />+ .suspend = timeofday_suspend_hook,<br />+ set_kset_name("timeofday"),<br />+};<br />+<br />+static struct sys_device device_timer = {<br />+ .id = 0,<br />+ .cls = &amp;timeofday_sysclass,<br />+};<br />+<br />+static int timeofday_init_device(void)<br />+{<br />+ int error = sysdev_class_register(&amp;timeofday_sysclass);<br />+<br />+ if (!error)<br />+ error = sysdev_register(&amp;device_timer);<br />+<br />+ return error;<br />+}<br />+<br />+device_initcall(timeofday_init_device);<br />+<br />+/**<br />+ * timeofday_periodic_hook - Does periodic update of timekeeping values.<br />+ * &#64;unused: unused value<br />+ *<br />+ * Calculates the delta since the last call, updates system time and<br />+ * clears the offset.<br />+ *<br />+ * Called via timeofday_timer.<br />+ */<br />+static void timeofday_periodic_hook(unsigned long unused)<br />+{<br />+ unsigned long flags;<br />+<br />+ cycle_t cycle_now, cycle_delta;<br />+ nsec_t delta_nsec;<br />+ static u64 remainder;<br />+<br />+ long leapsecond = 0;<br />+ struct clocksource* next;<br />+<br />+ int ppm;<br />+ static int ppm_last;<br />+<br />+ int something_changed = 0;<br />+ struct clocksource old_clock;<br />+ static nsec_t second_check;<br />+<br />+ write_seqlock_irqsave(&amp;system_time_lock, flags);<br />+<br />+ /* read time source &amp; calc time since last call: */<br />+ cycle_now = read_clocksource(clock);<br />+ cycle_delta = (cycle_now - cycle_last) &amp; clock-&gt;mask;<br />+<br />+ delta_nsec = cyc2ns_fixed_rem(ts_interval, &amp;cycle_delta, &amp;remainder);<br />+ cycle_last = (cycle_now - cycle_delta)&amp;clock-&gt;mask;<br />+<br />+ /* update system_time: */<br />+ __increment_system_time(delta_nsec);<br />+<br />+ /* advance the ntp state machine by ns interval: */<br />+ ntp_advance(delta_nsec);<br />+<br />+ /* only call ntp_leapsecond and ntp_sync once a sec: */<br />+ second_check += delta_nsec;<br />+ if (second_check &gt;= NSEC_PER_SEC) {<br />+ /* do ntp leap second processing: */<br />+ leapsecond = ntp_leapsecond(wall_time_ts);<br />+ if (leapsecond) {<br />+ wall_time_offset = ktime_add_ns(wall_time_offset,<br />+ leapsecond * NSEC_PER_SEC);<br />+ wall_time_ts.tv_sec += leapsecond;<br />+ monotonic_time_offset_ts.tv_sec += leapsecond;<br />+ }<br />+ /* sync the persistent clock: */<br />+ if (ntp_synced())<br />+ sync_persistent_clock(wall_time_ts);<br />+ second_check -= NSEC_PER_SEC;<br />+ }<br />+<br />+ /* if necessary, switch clocksources: */<br />+ next = get_next_clocksource();<br />+ if (next != clock) {<br />+ /* immediately set new cycle_last: */<br />+ cycle_last = read_clocksource(next);<br />+ /* update cycle_now to avoid problems in accumulation later: */<br />+ cycle_now = cycle_last;<br />+ /* swap clocksources: */<br />+ old_clock = *clock;<br />+ clock = next;<br />+ printk(KERN_INFO "Time: %s clocksource has been installed.\n",<br />+ clock-&gt;name);<br />+ ntp_clear();<br />+ ntp_adj = 0;<br />+ remainder = 0;<br />+ something_changed = 1;<br />+ }<br />+<br />+ /*<br />+ * now is a safe time, so allow clocksource to adjust<br />+ * itself (for example: to make cpufreq changes):<br />+ */<br />+ if (clock-&gt;update_callback) {<br />+ /*<br />+ * since clocksource state might change,<br />+ * keep a copy, but only if we've not<br />+ * already changed timesources:<br />+ */<br />+ if (!something_changed)<br />+ old_clock = *clock;<br />+ if (clock-&gt;update_callback()) {<br />+ remainder = 0;<br />+ something_changed = 1;<br />+ }<br />+ }<br />+<br />+ /* check for new PPM adjustment: */<br />+ ppm = ntp_get_ppm_adjustment();<br />+ if (ppm_last != ppm) {<br />+ /* make sure old_clock is set: */<br />+ if (!something_changed)<br />+ old_clock = *clock;<br />+ something_changed = 1;<br />+ }<br />+<br />+ /* if something changed, recalculate the ntp adjustment value: */<br />+ if (something_changed) {<br />+ /* accumulate current leftover cycles using old_clock: */<br />+ if (cycle_delta) {<br />+ delta_nsec = cyc2ns_rem(&amp;old_clock, ntp_adj,<br />+ cycle_delta, &amp;remainder);<br />+ cycle_last = cycle_now;<br />+ __increment_system_time(delta_nsec);<br />+ ntp_advance(delta_nsec);<br />+ }<br />+<br />+ /* recalculate the ntp adjustment and fixed interval values: */<br />+ ppm_last = ppm;<br />+ ntp_adj = ppm_to_mult_adj(clock, ppm);<br />+ ts_interval = calculate_clocksource_interval(clock, ntp_adj,<br />+ INTERVAL_LEN);<br />+ }<br />+<br />+ update_legacy_time_values();<br />+<br />+ write_sequnlock_irqrestore(&amp;system_time_lock, flags);<br />+<br />+ /* notify the posix timers if wall_time_offset changed */<br />+ if (leapsecond)<br />+ clock_was_set();<br />+<br />+ /* set us up to go off on the next interval: */<br />+ mod_timer(&amp;timeofday_timer,<br />+ jiffies + 1 + msecs_to_jiffies(PERIODIC_INTERVAL_MS));<br />+}<br />+<br />+/**<br />+ * timeofday_is_continuous - check to see if timekeeping is free running<br />+ */<br />+int timeofday_is_continuous(void)<br />+{<br />+ unsigned long seq;<br />+ int ret;<br />+<br />+ do {<br />+ seq = read_seqbegin(&amp;system_time_lock);<br />+<br />+ ret = clock-&gt;is_continuous;<br />+<br />+ } while (read_seqretry(&amp;system_time_lock, seq));<br />+<br />+ return ret;<br />+}<br />+<br />+/**<br />+ * timeofday_init - Initializes time variables<br />+ */<br />+void __init timeofday_init(void)<br />+{<br />+ unsigned long flags;<br />+<br />+ write_seqlock_irqsave(&amp;system_time_lock, flags);<br />+<br />+ /* initialize the clock variable: */<br />+ clock = get_next_clocksource();<br />+<br />+ /* initialize cycle_last offset base: */<br />+ cycle_last = read_clocksource(clock);<br />+<br />+ /* initialize wall_time_offset to now: */<br />+ /* XXX - this should be something like ns_to_ktime() */<br />+ wall_time_offset = ktime_add_ns(wall_time_offset,<br />+ read_persistent_clock());<br />+<br />+ /* initialize timespec values: */<br />+ wall_time_ts = ktime_to_timespec(ktime_add(system_time,<br />+ wall_time_offset));<br />+ monotonic_time_offset_ts = ktime_to_timespec(wall_time_offset);<br />+<br />+ /* clear NTP scaling factor &amp; state machine: */<br />+ ntp_adj = 0;<br />+ ntp_clear();<br />+ ts_interval = calculate_clocksource_interval(clock, ntp_adj,<br />+ INTERVAL_LEN);<br />+<br />+ /* initialize legacy time values: */<br />+ update_legacy_time_values();<br />+<br />+ write_sequnlock_irqrestore(&amp;system_time_lock, flags);<br />+<br />+ /* install timeofday_periodic_hook timer: */<br />+ init_timer(&amp;timeofday_timer);<br />+ timeofday_timer.function = timeofday_periodic_hook;<br />+ timeofday_timer.expires = jiffies + 1<br />+ + msecs_to_jiffies(PERIODIC_INTERVAL_MS);<br />+ add_timer(&amp;timeofday_timer);<br />+}<br />diff --git a/kernel/timer.c b/kernel/timer.c<br />index a543d54..8d10cd5 100644<br />--- a/kernel/timer.c<br />+++ b/kernel/timer.c<br />&#64;&#64; -28,7 +28,7 &#64;&#64;<br /> #include &lt;linux/swap.h&gt;<br /> #include &lt;linux/notifier.h&gt;<br /> #include &lt;linux/thread_info.h&gt;<br />-#include &lt;linux/time.h&gt;<br />+#include &lt;linux/timeofday.h&gt;<br /> #include &lt;linux/jiffies.h&gt;<br /> #include &lt;linux/posix-timers.h&gt;<br /> #include &lt;linux/cpu.h&gt;<br />&#64;&#64; -882,6 +882,10 &#64;&#64; void ntp_advance(unsigned long interval_<br /> write_sequnlock_irqrestore(&amp;ntp_lock, flags);<br /> }<br /> <br />+#ifdef CONFIG_GENERIC_TIME<br />+# define update_wall_time(x) do { } while (0)<br />+#else<br />+<br /> /**<br /> * phase_advance - advance the phase<br /> *<br />&#64;&#64; -957,6 +961,7 &#64;&#64; static void update_wall_time(unsigned lo<br /> <br /> } while (--ticks);<br /> }<br />+#endif /* !CONFIG_GENERIC_TIME */<br /> <br /> /*<br /> * Called from the timer interrupt handler to charge one tick to the current <br />-<br />To unsubscribe from this list: send the line "unsubscribe linux-kernel" in<br />the body of a message to majordomo&#64;vger.kernel.org<br />More majordomo info at <a href="http://vger.kernel.org/majordomo-info.html">http://vger.kernel.org/majordomo-info.html</a><br />Please read the FAQ at <a href="http://www.tux.org/lkml/">http://www.tux.org/lkml/</a><br /></pre></td><td width="32" rowspan="2" class="c" valign="top"><img src="/images/icornerr.gif" width="32" height="32" alt="\" /></td></tr><tr><td align="right" valign="bottom"> 聽 </td></tr><tr><td align="right" valign="bottom">聽</td><td class="c" valign="bottom" style="padding-bottom: 0px"><img src="/images/bcornerl.gif" width="32" height="32" alt="\" /></td><td class="c">聽</td><td class="c" valign="bottom" style="padding-bottom: 0px"><img src="/images/bcornerr.gif" width="32" height="32" alt="/" /></td></tr><tr><td align="right" valign="top" colspan="2"> 聽 </td><td class="lm">Last update: 2005-12-16 02:12 聽聽 [from the cache]<br />漏2003-2020 <a href="http://blog.jasper.es/"><span itemprop="editor">Jasper Spaans</span></a>|hosted at <a href="https://www.digitalocean.com/?refcode=9a8e99d24cf9">Digital Ocean</a> and my Meterkast|<a href="http://blog.jasper.es/categories.html#lkml-ref">Read the blog</a></td><td>聽</td></tr></table><script language="javascript" src="/js/styleswitcher.js" type="text/javascript"></script></body></html>

Pages: 1 2 3 4 5 6 7 8 9 10