aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2010-04-14 09:19:20 -0700
committerDenys Vlasenko <vda.linux@googlemail.com>2010-04-14 09:19:20 -0700
commit60f659f9d998128d6db817888ab35a9de248be68 (patch)
tree3535a46dd228f1f8d7a079c84499cba9233c5161
parentd7b5289209131ae94b540b35f0f147d2302ec0c9 (diff)
downloadbusybox-w32-60f659f9d998128d6db817888ab35a9de248be68.tar.gz
busybox-w32-60f659f9d998128d6db817888ab35a9de248be68.tar.bz2
busybox-w32-60f659f9d998128d6db817888ab35a9de248be68.zip
hwclock: improve, and then disable clever sync code: it's bloat
...and hardware is too stupid to benefit from it anyway function old new delta hwclock_main 439 319 -120 Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--util-linux/hwclock.c112
1 files changed, 82 insertions, 30 deletions
diff --git a/util-linux/hwclock.c b/util-linux/hwclock.c
index b8300570e..416271b31 100644
--- a/util-linux/hwclock.c
+++ b/util-linux/hwclock.c
@@ -109,10 +109,53 @@ static void to_sys_clock(const char **pp_rtcname, int utc)
109 109
110static void from_sys_clock(const char **pp_rtcname, int utc) 110static void from_sys_clock(const char **pp_rtcname, int utc)
111{ 111{
112#define TWEAK_USEC 200 112#if 1
113 struct tm tm_time;
114 struct timeval tv; 113 struct timeval tv;
114 struct tm tm_time;
115 int rtc;
116
117 rtc = rtc_xopen(pp_rtcname, O_WRONLY);
118 gettimeofday(&tv, NULL);
119 /* Prepare tm_time */
120 if (sizeof(time_t) == sizeof(tv.tv_sec)) {
121 if (utc)
122 gmtime_r((time_t*)&tv.tv_sec, &tm_time);
123 else
124 localtime_r((time_t*)&tv.tv_sec, &tm_time);
125 } else {
126 time_t t = tv.tv_sec;
127 if (utc)
128 gmtime_r(&t, &tm_time);
129 else
130 localtime_r(&t, &tm_time);
131 }
132#else
133/* Bloated code which tries to set hw clock with better precision.
134 * On x86, even though code does set hw clock within <1ms of exact
135 * whole seconds, apparently hw clock (at least on some machines)
136 * doesn't reset internal fractional seconds to 0,
137 * making all this a pointless excercise.
138 */
139 /* If we see that we are N usec away from whole second,
140 * we'll sleep for N-ADJ usecs. ADJ corrects for the fact
141 * that CPU is not infinitely fast.
142 * On infinitely fast CPU, next wakeup would be
143 * on (exactly_next_whole_second - ADJ). On real CPUs,
144 * this difference between current time and whole second
145 * is less than ADJ (assuming system isn't heavily loaded).
146 */
147 /* Small value of 256us gives very precise sync for 2+ GHz CPUs.
148 * Slower CPUs will fail to sync and will go to bigger
149 * ADJ values. qemu-emulated armv4tl with ~100 MHz
150 * performance ends up using ADJ ~= 4*1024 and it takes
151 * 2+ secs (2 tries with successively larger ADJ)
152 * to sync. Even straced one on the same qemu (very slow)
153 * takes only 4 tries.
154 */
155#define TWEAK_USEC 256
115 unsigned adj = TWEAK_USEC; 156 unsigned adj = TWEAK_USEC;
157 struct tm tm_time;
158 struct timeval tv;
116 int rtc = rtc_xopen(pp_rtcname, O_WRONLY); 159 int rtc = rtc_xopen(pp_rtcname, O_WRONLY);
117 160
118 /* Try to catch the moment when whole second is close */ 161 /* Try to catch the moment when whole second is close */
@@ -124,55 +167,64 @@ static void from_sys_clock(const char **pp_rtcname, int utc)
124 167
125 t = tv.tv_sec; 168 t = tv.tv_sec;
126 rem_usec = 1000000 - tv.tv_usec; 169 rem_usec = 1000000 - tv.tv_usec;
127 if (rem_usec < 1024) { 170 if (rem_usec < adj) {
128 /* Less than 1ms to next second. Good enough */ 171 /* Close enough */
129 small_rem: 172 small_rem:
130 t++; 173 t++;
131 } 174 }
132 175
133 /* Prepare tm */ 176 /* Prepare tm_time from t */
134 if (utc) 177 if (utc)
135 gmtime_r(&t, &tm_time); /* may read /etc/xxx (it takes time) */ 178 gmtime_r(&t, &tm_time); /* may read /etc/xxx (it takes time) */
136 else 179 else
137 localtime_r(&t, &tm_time); /* same */ 180 localtime_r(&t, &tm_time); /* same */
138 tm_time.tm_isdst = 0; 181
182 if (adj >= 32*1024) {
183 break; /* 32 ms diff and still no luck?? give up trying to sync */
184 }
139 185
140 /* gmtime/localtime took some time, re-get cur time */ 186 /* gmtime/localtime took some time, re-get cur time */
141 gettimeofday(&tv, NULL); 187 gettimeofday(&tv, NULL);
142 188
143 if (tv.tv_sec < t /* may happen if rem_usec was < 1024 */ 189 if (tv.tv_sec < t /* we are still in old second */
144 || (tv.tv_sec == t && tv.tv_usec < 1024) 190 || (tv.tv_sec == t && tv.tv_usec < adj) /* not too far into next second */
145 ) { 191 ) {
146 /* We are not too far into next second. Good. */ 192 break; /* good, we are in sync! */
147 break;
148 }
149 adj += 32; /* 2^(10-5) = 2^5 = 32 iterations max */
150 if (adj >= 1024) {
151 /* Give up trying to sync */
152 break;
153 } 193 }
154 194
155 /* Try to sync up by sleeping */
156 rem_usec = 1000000 - tv.tv_usec; 195 rem_usec = 1000000 - tv.tv_usec;
157 if (rem_usec < 1024) { 196 if (rem_usec < adj) {
158 goto small_rem; /* already close, don't sleep */ 197 t = tv.tv_sec;
198 goto small_rem; /* already close to next sec, don't sleep */
159 } 199 }
160 /* Need to sleep.
161 * Note that small adj on slow processors can make us
162 * to always overshoot tv.tv_usec < 1024 check on next
163 * iteration. That's why adj is increased on each iteration.
164 * This also allows it to be reused as a loop limiter.
165 */
166 usleep(rem_usec - adj);
167 }
168 200
169 xioctl(rtc, RTC_SET_TIME, &tm_time); 201 /* Try to sync up by sleeping */
202 usleep(rem_usec - adj);
170 203
171 /* Debug aid to find "good" TWEAK_USEC. 204 /* Jump to 1ms diff, then increase fast (x2): EVERY loop
205 * takes ~1 sec, people won't like slowly converging code here!
206 */
207 //bb_error_msg("adj:%d tv.tv_usec:%d", adj, (int)tv.tv_usec);
208 if (adj < 512)
209 adj = 512;
210 /* ... and if last "overshoot" does not look insanely big,
211 * just use it as adj increment. This makes convergence faster.
212 */
213 if (tv.tv_usec < adj * 8) {
214 adj += tv.tv_usec;
215 continue;
216 }
217 adj *= 2;
218 }
219 /* Debug aid to find "optimal" TWEAK_USEC with nearly exact sync.
172 * Look for a value which makes tv_usec close to 999999 or 0. 220 * Look for a value which makes tv_usec close to 999999 or 0.
173 * for 2.20GHz Intel Core 2: TWEAK_USEC ~= 200 221 * For 2.20GHz Intel Core 2: optimal TWEAK_USEC ~= 200
174 */ 222 */
175 //bb_error_msg("tv.tv_usec:%d adj:%d", (int)tv.tv_usec, adj); 223 //bb_error_msg("tv.tv_usec:%d", (int)tv.tv_usec);
224#endif
225
226 tm_time.tm_isdst = 0;
227 xioctl(rtc, RTC_SET_TIME, &tm_time);
176 228
177 if (ENABLE_FEATURE_CLEAN_UP) 229 if (ENABLE_FEATURE_CLEAN_UP)
178 close(rtc); 230 close(rtc);