109 |
|
|
110 |
static void from_sys_clock(const char **pp_rtcname, int utc) |
static void from_sys_clock(const char **pp_rtcname, int utc) |
111 |
{ |
{ |
112 |
#define TWEAK_USEC 200 |
#if 1 |
|
struct tm tm_time; |
|
113 |
struct timeval tv; |
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 |
156 |
unsigned adj = TWEAK_USEC; |
unsigned adj = TWEAK_USEC; |
157 |
|
struct tm tm_time; |
158 |
|
struct timeval tv; |
159 |
int rtc = rtc_xopen(pp_rtcname, O_WRONLY); |
int rtc = rtc_xopen(pp_rtcname, O_WRONLY); |
160 |
|
|
161 |
/* Try to catch the moment when whole second is close */ |
/* Try to catch the moment when whole second is close */ |
167 |
|
|
168 |
t = tv.tv_sec; |
t = tv.tv_sec; |
169 |
rem_usec = 1000000 - tv.tv_usec; |
rem_usec = 1000000 - tv.tv_usec; |
170 |
if (rem_usec < 1024) { |
if (rem_usec < adj) { |
171 |
/* Less than 1ms to next second. Good enough */ |
/* Close enough */ |
172 |
small_rem: |
small_rem: |
173 |
t++; |
t++; |
174 |
} |
} |
175 |
|
|
176 |
/* Prepare tm */ |
/* Prepare tm_time from t */ |
177 |
if (utc) |
if (utc) |
178 |
gmtime_r(&t, &tm_time); /* may read /etc/xxx (it takes time) */ |
gmtime_r(&t, &tm_time); /* may read /etc/xxx (it takes time) */ |
179 |
else |
else |
180 |
localtime_r(&t, &tm_time); /* same */ |
localtime_r(&t, &tm_time); /* same */ |
181 |
tm_time.tm_isdst = 0; |
|
182 |
|
if (adj >= 32*1024) { |
183 |
|
break; /* 32 ms diff and still no luck?? give up trying to sync */ |
184 |
|
} |
185 |
|
|
186 |
/* gmtime/localtime took some time, re-get cur time */ |
/* gmtime/localtime took some time, re-get cur time */ |
187 |
gettimeofday(&tv, NULL); |
gettimeofday(&tv, NULL); |
188 |
|
|
189 |
if (tv.tv_sec < t /* may happen if rem_usec was < 1024 */ |
if (tv.tv_sec < t /* we are still in old second */ |
190 |
|| (tv.tv_sec == t && tv.tv_usec < 1024) |
|| (tv.tv_sec == t && tv.tv_usec < adj) /* not too far into next second */ |
191 |
) { |
) { |
192 |
/* We are not too far into next second. Good. */ |
break; /* good, we are in sync! */ |
|
break; |
|
|
} |
|
|
adj += 32; /* 2^(10-5) = 2^5 = 32 iterations max */ |
|
|
if (adj >= 1024) { |
|
|
/* Give up trying to sync */ |
|
|
break; |
|
193 |
} |
} |
194 |
|
|
|
/* Try to sync up by sleeping */ |
|
195 |
rem_usec = 1000000 - tv.tv_usec; |
rem_usec = 1000000 - tv.tv_usec; |
196 |
if (rem_usec < 1024) { |
if (rem_usec < adj) { |
197 |
goto small_rem; /* already close, don't sleep */ |
t = tv.tv_sec; |
198 |
|
goto small_rem; /* already close to next sec, don't sleep */ |
199 |
} |
} |
|
/* Need to sleep. |
|
|
* Note that small adj on slow processors can make us |
|
|
* to always overshoot tv.tv_usec < 1024 check on next |
|
|
* iteration. That's why adj is increased on each iteration. |
|
|
* This also allows it to be reused as a loop limiter. |
|
|
*/ |
|
|
usleep(rem_usec - adj); |
|
|
} |
|
200 |
|
|
201 |
xioctl(rtc, RTC_SET_TIME, &tm_time); |
/* Try to sync up by sleeping */ |
202 |
|
usleep(rem_usec - adj); |
203 |
|
|
204 |
/* Debug aid to find "good" TWEAK_USEC. |
/* 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. |
220 |
* Look for a value which makes tv_usec close to 999999 or 0. |
* Look for a value which makes tv_usec close to 999999 or 0. |
221 |
* for 2.20GHz Intel Core 2: TWEAK_USEC ~= 200 |
* For 2.20GHz Intel Core 2: optimal TWEAK_USEC ~= 200 |
222 |
*/ |
*/ |
223 |
//bb_error_msg("tv.tv_usec:%d adj:%d", (int)tv.tv_usec, adj); |
//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); |
228 |
|
|
229 |
if (ENABLE_FEATURE_CLEAN_UP) |
if (ENABLE_FEATURE_CLEAN_UP) |
230 |
close(rtc); |
close(rtc); |