diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2010-04-14 09:19:20 -0700 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2010-04-14 09:19:20 -0700 |
commit | 60f659f9d998128d6db817888ab35a9de248be68 (patch) | |
tree | 3535a46dd228f1f8d7a079c84499cba9233c5161 | |
parent | d7b5289209131ae94b540b35f0f147d2302ec0c9 (diff) | |
download | busybox-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.c | 112 |
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 | ||
110 | static void from_sys_clock(const char **pp_rtcname, int utc) | 110 | static 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); |