]> git.saurik.com Git - apple/xnu.git/blob - osfmk/kern/remote_time.c
xnu-6153.141.1.tar.gz
[apple/xnu.git] / osfmk / kern / remote_time.c
1 /*
2 * Copyright (c) 2017 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 #include <mach/mach_time.h>
29 #include <mach/clock_types.h>
30 #include <kern/misc_protos.h>
31 #include <kern/clock.h>
32 #include <kern/remote_time.h>
33 #include <kern/spl.h>
34 #include <kern/locks.h>
35 #include <sys/kdebug.h>
36 #include <machine/machine_routines.h>
37 #include <kern/assert.h>
38 #include <kern/kern_types.h>
39 #include <kern/thread.h>
40 #include <machine/commpage.h>
41 #include <machine/atomic.h>
42
43 #if CONFIG_MACH_BRIDGE_SEND_TIME
44
45 uint32_t bt_enable_flag = 0;
46 lck_spin_t *bt_maintenance_lock = NULL;
47 _Atomic uint32_t bt_init_flag = 0;
48
49 void mach_bridge_timer_maintenance(void);
50 void mach_bridge_timer_init(void);
51 uint32_t mach_bridge_timer_enable(uint32_t new_value, int change);
52
53 /*
54 * When CONFIG_MACH_BRIDGE_SEND_TIME is defined, it is expected
55 * that a machine-specific timestamp sending routine such as
56 * void mach_bridge_send_timestamp(uint64_t); has also been defined.
57 */
58 extern void mach_bridge_send_timestamp(uint64_t);
59
60 void
61 mach_bridge_timer_maintenance(void)
62 {
63 if (!os_atomic_load(&bt_init_flag, acquire)) {
64 return;
65 }
66
67 lck_spin_lock(bt_maintenance_lock);
68 if (!bt_enable_flag) {
69 goto done;
70 }
71 mach_bridge_send_timestamp(0);
72
73 done:
74 lck_spin_unlock(bt_maintenance_lock);
75 }
76
77 /*
78 * This function should be called only once from the callback
79 * registration function
80 */
81 void
82 mach_bridge_timer_init(void)
83 {
84 assert(!os_atomic_load(&bt_init_flag, relaxed));
85 /* Initialize the lock */
86 static lck_grp_t *bt_lck_grp = NULL;
87 bt_lck_grp = lck_grp_alloc_init("bridgetimestamp", LCK_GRP_ATTR_NULL);
88 bt_maintenance_lock = lck_spin_alloc_init(bt_lck_grp, NULL);
89 }
90
91 /*
92 * If change = 0, return the current value of bridge_timer_enable
93 * If change = 1, update bridge_timer_enable and return the updated
94 * value
95 */
96 uint32_t
97 mach_bridge_timer_enable(uint32_t new_value, int change)
98 {
99 uint32_t current_value = 0;
100 assert(os_atomic_load(&bt_init_flag, relaxed));
101 lck_spin_lock(bt_maintenance_lock);
102 if (change) {
103 bt_enable_flag = new_value;
104 }
105 current_value = bt_enable_flag;
106 lck_spin_unlock(bt_maintenance_lock);
107 return current_value;
108 }
109
110 #endif /* CONFIG_MACH_BRIDGE_SEND_TIME */
111
112 #if CONFIG_MACH_BRIDGE_RECV_TIME
113 #include <machine/machine_remote_time.h>
114
115 /*
116 * functions used by machine-specific code
117 * that implements CONFIG_MACH_BRIDGE_RECV_TIME
118 */
119 void mach_bridge_add_timestamp(uint64_t remote_timestamp, uint64_t local_timestamp);
120 void bt_calibration_thread_start(void);
121 lck_spin_t *ts_conversion_lock = NULL;
122 void bt_params_add(struct bt_params *params);
123
124 /* function called by sysctl */
125 struct bt_params bt_params_get_latest(void);
126
127 /*
128 * Platform specific bridge time receiving interface.
129 * These variables should be exported by the platform specific time receiving code.
130 */
131 extern lck_spin_t *bt_spin_lock;
132 extern _Atomic uint32_t bt_init_flag;
133
134 static uint64_t received_local_timestamp = 0;
135 static uint64_t received_remote_timestamp = 0;
136 /*
137 * Buffer the previous timestamp pairs and rate
138 * It is protected by the ts_conversion_lock
139 */
140 #define BT_PARAMS_COUNT 10
141 static struct bt_params bt_params_hist[BT_PARAMS_COUNT] = {};
142 static int bt_params_idx = -1;
143
144 void
145 bt_params_add(struct bt_params *params)
146 {
147 lck_spin_assert(ts_conversion_lock, LCK_ASSERT_OWNED);
148
149 bt_params_idx = (bt_params_idx + 1) % BT_PARAMS_COUNT;
150 bt_params_hist[bt_params_idx] = *params;
151 }
152
153 #if defined(XNU_TARGET_OS_BRIDGE)
154 static inline struct bt_params*
155 bt_params_find(uint64_t local_ts)
156 {
157 lck_spin_assert(ts_conversion_lock, LCK_ASSERT_OWNED);
158
159 int idx = bt_params_idx;
160 if (idx < 0) {
161 return NULL;
162 }
163 do {
164 if (local_ts >= bt_params_hist[idx].base_local_ts) {
165 return &bt_params_hist[idx];
166 }
167 if (--idx < 0) {
168 idx = BT_PARAMS_COUNT - 1;
169 }
170 } while (idx != bt_params_idx);
171
172 return NULL;
173 }
174 #endif /* defined(XNU_TARGET_OS_BRIDGE) */
175
176 static inline struct bt_params
177 bt_params_get_latest_locked(void)
178 {
179 lck_spin_assert(ts_conversion_lock, LCK_ASSERT_OWNED);
180
181 struct bt_params latest_params = {};
182 if (bt_params_idx >= 0) {
183 latest_params = bt_params_hist[bt_params_idx];
184 }
185
186 return latest_params;
187 }
188
189 struct bt_params
190 bt_params_get_latest(void)
191 {
192 struct bt_params latest_params = {};
193
194 /* Check if ts_converison_lock has been initialized */
195 if (os_atomic_load(&bt_init_flag, acquire)) {
196 lck_spin_lock(ts_conversion_lock);
197 latest_params = bt_params_get_latest_locked();
198 lck_spin_unlock(ts_conversion_lock);
199 }
200 return latest_params;
201 }
202
203 /*
204 * Conditions: bt_spin_lock held and called from primary interrupt context
205 */
206 void
207 mach_bridge_add_timestamp(uint64_t remote_timestamp, uint64_t local_timestamp)
208 {
209 lck_spin_assert(bt_spin_lock, LCK_ASSERT_OWNED);
210
211 /* sleep/wake might return the same mach_absolute_time as the previous timestamp pair */
212 if ((received_local_timestamp == local_timestamp) ||
213 (received_remote_timestamp == remote_timestamp)) {
214 return;
215 }
216
217 received_local_timestamp = local_timestamp;
218 received_remote_timestamp = remote_timestamp;
219 thread_wakeup((event_t)bt_params_hist);
220 }
221
222 static double
223 mach_bridge_compute_rate(uint64_t new_local_ts, uint64_t new_remote_ts,
224 uint64_t old_local_ts, uint64_t old_remote_ts)
225 {
226 int64_t rdiff = (int64_t)new_remote_ts - (int64_t)old_remote_ts;
227 int64_t ldiff = (int64_t)new_local_ts - (int64_t)old_local_ts;
228 double calc_rate = ((double)rdiff) / ldiff;
229 return calc_rate;
230 }
231
232 #define MAX_RECALCULATE_COUNT 8
233 #define CUMULATIVE_RATE_DECAY_CONSTANT 0.01
234 #define CUMULATIVE_RATE_WEIGHT 0.99
235 #define INITIAL_RATE 1.0
236 #define MIN_INITIAL_SAMPLE_COUNT 10
237 #define MAX_INITIAL_SAMPLE_COUNT 50
238 #define MAX_SKIP_RESET_COUNT 2
239 #define MIN_LOCAL_TS_DISTANCE_NS 100000000 /* 100 ms */
240 #define MAX_LOCAL_TS_DISTANCE_NS 350000000 /* 350 ms */
241 #define TS_PAIR_MISMATCH_THRESHOLD_NS 50000000 /* 50 ms */
242 #define MAX_TS_PAIR_MISMATCHES 5
243 #define MAX_TS_PAIR_MISMATCH_RESET_COUNT 3
244 #define MIN_OBSERVED_RATE 0.8
245 #define MAX_OBSERVED_RATE 1.2
246
247 static void
248 bt_calibration_thread(void)
249 {
250 static uint64_t prev_local_ts = 0, prev_remote_ts = 0, curr_local_ts = 0, curr_remote_ts = 0;
251 static uint64_t prev_received_local_ts = 0, prev_received_remote_ts = 0;
252 static double cumulative_rate = INITIAL_RATE;
253 static uint32_t initial_sample_count = 1;
254 static uint32_t max_initial_sample_count = MAX_INITIAL_SAMPLE_COUNT;
255 static uint32_t skip_reset_count = MAX_SKIP_RESET_COUNT;
256 int recalculate_count = 1;
257 static bool reset = false;
258 bool sleep = false;
259 static bool skip_rcv_ts = false;
260 static uint64_t ts_pair_mismatch = 0;
261 static uint32_t ts_pair_mismatch_reset_count = 0;
262 spl_t s = splsched();
263 lck_spin_lock(bt_spin_lock);
264 if (!received_remote_timestamp) {
265 if (PE_parse_boot_argn("rt_ini_count", &max_initial_sample_count,
266 sizeof(uint32_t)) == TRUE) {
267 if (max_initial_sample_count < MIN_INITIAL_SAMPLE_COUNT) {
268 max_initial_sample_count = MIN_INITIAL_SAMPLE_COUNT;
269 }
270 }
271 /* Nothing to do the first time */
272 goto block;
273 }
274 /*
275 * The values in bt_params are recalculated every time a new timestamp
276 * pair is received. Firstly, both timestamps are converted to nanoseconds.
277 * The current and previous timestamp pairs are used to compute the
278 * observed_rate of the two clocks w.r.t each other. For the first
279 * MIN_INITIAL_SAMPLE_COUNT number of pairs, the cumulative_rate is a simple
280 * average of the observed_rate. For the later pairs, the cumulative_rate
281 * is updated using exponential moving average of the observed_rate.
282 * The current and bt_params' base timestamp pairs are used to compute
283 * the rate_from_base. This value ensures that the bt_params base
284 * timestamp pair curve doesn't stay parallel to the observed timestamp
285 * pair curve, rather moves in the direction of the observed timestamp curve.
286 * The bt_params.rate is computed as a weighted average of the cumulative_rate
287 * and the rate_from_base. For each current local timestamp, the remote_time
288 * is predicted using the previous values of bt_params. After computing the new
289 * bt_params.rate, bt_params.base_remote_time is set to this predicted value
290 * and bt_params.base_local_time is set to the current local timestamp.
291 */
292 recalculate:
293 assertf(recalculate_count <= MAX_RECALCULATE_COUNT, "bt_caliberation_thread: recalculate \
294 invocation exceeds MAX_RECALCULATE_COUNT");
295
296 if ((received_remote_timestamp == BT_RESET_SENTINEL_TS) || (received_remote_timestamp == BT_WAKE_SENTINEL_TS)) {
297 KDBG(MACHDBG_CODE(DBG_MACH_CLOCK, MACH_BRIDGE_RESET_TS), received_local_timestamp, received_remote_timestamp, 1);
298 reset = true;
299 skip_reset_count = MAX_SKIP_RESET_COUNT;
300 ts_pair_mismatch_reset_count = 0;
301 goto block;
302 } else if (received_remote_timestamp == BT_SLEEP_SENTINEL_TS) {
303 sleep = true;
304 } else if (!received_local_timestamp) {
305 /* If the local timestamp isn't accurately captured, the received value will be ignored */
306 skip_rcv_ts = true;
307 goto block;
308 }
309
310 /* Keep a copy of the prev timestamps to compute distance */
311 prev_received_local_ts = curr_local_ts;
312 prev_received_remote_ts = curr_remote_ts;
313
314 uint64_t curr_local_abs = received_local_timestamp;
315 absolutetime_to_nanoseconds(curr_local_abs, &curr_local_ts);
316 curr_remote_ts = received_remote_timestamp;
317
318 /* Prevent unusual rate changes caused by delayed timestamps */
319 uint64_t local_diff = curr_local_ts - prev_received_local_ts;
320 if (!(reset || sleep) && ((local_diff < MIN_LOCAL_TS_DISTANCE_NS) ||
321 (!skip_rcv_ts && (local_diff > MAX_LOCAL_TS_DISTANCE_NS)))) {
322 /* Skip the current timestamp */
323 KDBG(MACHDBG_CODE(DBG_MACH_CLOCK, MACH_BRIDGE_SKIP_TS), curr_local_ts, curr_remote_ts,
324 prev_received_local_ts);
325 goto block;
326 } else {
327 skip_rcv_ts = false;
328 /* Use the prev copy of timestamps only if the distance is acceptable */
329 prev_local_ts = prev_received_local_ts;
330 prev_remote_ts = prev_received_remote_ts;
331 }
332 lck_spin_unlock(bt_spin_lock);
333 splx(s);
334
335 struct bt_params bt_params = {};
336
337 lck_spin_lock(ts_conversion_lock);
338 if (reset) {
339 if (skip_reset_count > 0) {
340 KDBG(MACHDBG_CODE(DBG_MACH_CLOCK, MACH_BRIDGE_SKIP_TS), curr_local_ts, curr_remote_ts,
341 prev_local_ts, skip_reset_count);
342 skip_reset_count--;
343 goto skip_reset;
344 }
345 bt_params.base_local_ts = curr_local_ts;
346 bt_params.base_remote_ts = curr_remote_ts;
347 bt_params.rate = cumulative_rate;
348 prev_local_ts = 0;
349 prev_remote_ts = 0;
350 ts_pair_mismatch = 0;
351 initial_sample_count = 1;
352 reset = false;
353 KDBG(MACHDBG_CODE(DBG_MACH_CLOCK, MACH_BRIDGE_RESET_TS), curr_local_ts, curr_remote_ts, 2);
354 } else if (sleep) {
355 absolutetime_to_nanoseconds(mach_absolute_time(), &bt_params.base_local_ts);
356 bt_params.base_remote_ts = 0;
357 bt_params.rate = 0;
358 sleep = false;
359 } else {
360 struct bt_params bt_params_snapshot = {};
361 if (bt_params_idx >= 0) {
362 bt_params_snapshot = bt_params_hist[bt_params_idx];
363 }
364 lck_spin_unlock(ts_conversion_lock);
365 if (bt_params_snapshot.rate == 0.0) {
366 /*
367 * The rate should never be 0 because we always expect a reset/wake
368 * sentinel after sleep, followed by valid timestamp pair data that
369 * will be handled by the reset clause (above). However, we should
370 * not rely on a paired version of the remote OS - we could actually
371 * be running a completely different OS! Treat a timestamp after
372 * a sleep as a reset condition.
373 */
374 reset = true;
375 skip_reset_count = MAX_SKIP_RESET_COUNT;
376 ts_pair_mismatch_reset_count = 0;
377 KDBG(MACHDBG_CODE(DBG_MACH_CLOCK, MACH_BRIDGE_RESET_TS), curr_local_ts, curr_remote_ts, 3);
378 s = splsched();
379 lck_spin_lock(bt_spin_lock);
380 goto block;
381 }
382
383 /* Check if the predicted remote timestamp is within the expected current remote timestamp range */
384 uint64_t pred_remote_ts = mach_bridge_compute_timestamp(curr_local_ts, &bt_params_snapshot);
385 uint64_t diff = 0;
386 if (initial_sample_count >= max_initial_sample_count) {
387 if (pred_remote_ts > curr_remote_ts) {
388 diff = pred_remote_ts - curr_remote_ts;
389 } else {
390 diff = curr_remote_ts - pred_remote_ts;
391 }
392 if (diff > TS_PAIR_MISMATCH_THRESHOLD_NS) {
393 ts_pair_mismatch++;
394 KDBG(MACHDBG_CODE(DBG_MACH_CLOCK, MACH_BRIDGE_TS_MISMATCH), curr_local_ts,
395 curr_remote_ts, pred_remote_ts, ts_pair_mismatch);
396 } else {
397 ts_pair_mismatch = 0;
398 }
399 if (ts_pair_mismatch > MAX_TS_PAIR_MISMATCHES) {
400 #if (DEVELOPMENT || DEBUG)
401 if (ts_pair_mismatch_reset_count == MAX_TS_PAIR_MISMATCH_RESET_COUNT) {
402 panic("remote_time: timestamp pair mismatch exceeded limit");
403 }
404 #endif /* (DEVELOPMENT || DEBUG) */
405 reset = true;
406 ts_pair_mismatch_reset_count++;
407 KDBG(MACHDBG_CODE(DBG_MACH_CLOCK, MACH_BRIDGE_RESET_TS), curr_local_ts, curr_remote_ts, 4);
408 s = splsched();
409 lck_spin_lock(bt_spin_lock);
410 goto block;
411 }
412 }
413 double observed_rate, rate_from_base, new_rate;
414 observed_rate = mach_bridge_compute_rate(curr_local_ts, curr_remote_ts, prev_local_ts, prev_remote_ts);
415 /* Log bad observed rates and skip the timestamp pair */
416 if ((observed_rate < MIN_OBSERVED_RATE) || (observed_rate > MAX_OBSERVED_RATE)) {
417 KDBG(MACHDBG_CODE(DBG_MACH_CLOCK, MACH_BRIDGE_OBSV_RATE), *(uint64_t *)((void *)&observed_rate));
418 ts_pair_mismatch = ts_pair_mismatch > 0 ? (ts_pair_mismatch - 1) : 0;
419 s = splsched();
420 lck_spin_lock(bt_spin_lock);
421 goto block;
422 }
423 if (initial_sample_count <= MIN_INITIAL_SAMPLE_COUNT) {
424 initial_sample_count++;
425 cumulative_rate = cumulative_rate + (observed_rate - cumulative_rate) / initial_sample_count;
426 } else {
427 if (initial_sample_count < max_initial_sample_count) {
428 initial_sample_count++;
429 }
430 cumulative_rate = cumulative_rate + CUMULATIVE_RATE_DECAY_CONSTANT * (observed_rate - cumulative_rate);
431 }
432 rate_from_base = mach_bridge_compute_rate(curr_local_ts, curr_remote_ts, bt_params_snapshot.base_local_ts,
433 bt_params_snapshot.base_remote_ts);
434 new_rate = CUMULATIVE_RATE_WEIGHT * cumulative_rate + (1 - CUMULATIVE_RATE_WEIGHT) * rate_from_base;
435 /*
436 * Acquire the lock first to ensure that bt_params.base_local_ts is always
437 * greater than the last value of now captured by mach_bridge_remote_time.
438 * This ensures that we always use the same parameters to compute remote
439 * timestamp for a given local timestamp.
440 */
441 lck_spin_lock(ts_conversion_lock);
442 absolutetime_to_nanoseconds(mach_absolute_time(), &bt_params.base_local_ts);
443 bt_params.base_remote_ts = mach_bridge_compute_timestamp(bt_params.base_local_ts, &bt_params_snapshot);
444 bt_params.rate = new_rate;
445 }
446 bt_params_add(&bt_params);
447 commpage_set_remotetime_params(bt_params.rate, bt_params.base_local_ts, bt_params.base_remote_ts);
448 KDBG(MACHDBG_CODE(DBG_MACH_CLOCK, MACH_BRIDGE_TS_PARAMS), bt_params.base_local_ts,
449 bt_params.base_remote_ts, *(uint64_t *)((void *)&bt_params.rate));
450
451 skip_reset:
452 lck_spin_unlock(ts_conversion_lock);
453
454 s = splsched();
455 lck_spin_lock(bt_spin_lock);
456 /* Check if a new timestamp pair was received */
457 if (received_local_timestamp != curr_local_abs) {
458 recalculate_count++;
459 goto recalculate;
460 }
461 block:
462 assert_wait((event_t)bt_params_hist, THREAD_UNINT);
463 lck_spin_unlock(bt_spin_lock);
464 splx(s);
465 thread_block((thread_continue_t)bt_calibration_thread);
466 }
467
468 void
469 bt_calibration_thread_start(void)
470 {
471 thread_t thread;
472 kern_return_t result = kernel_thread_start_priority((thread_continue_t)bt_calibration_thread,
473 NULL, BASEPRI_KERNEL, &thread);
474 if (result != KERN_SUCCESS) {
475 panic("mach_bridge_add_timestamp: thread_timestamp_calibration");
476 }
477 thread_deallocate(thread);
478 }
479
480 #endif /* CONFIG_MACH_BRIDGE_RECV_TIME */
481
482 /**
483 * mach_bridge_remote_time
484 *
485 * This function is used to predict the remote CPU's clock time, given
486 * the local time.
487 *
488 * If local_timestamp = 0, then the remote_timestamp is calculated
489 * corresponding to the current mach_absolute_time.
490 *
491 * If XNU_TARGET_OS_BRIDGE is defined, then monotonicity of
492 * predicted time is guaranteed only for recent local_timestamp values
493 * lesser than the current mach_absolute_time upto 1 second.
494 *
495 * If CONFIG_MACH_BRIDGE_SEND_TIME is true, then the function is compiled
496 * for the remote CPU. If CONFIG_MACH_BRIDGE_RECV_TIME is true, then the
497 * the function is compiled for the local CPU. Both config options cannot
498 * be true simultaneously.
499 */
500 uint64_t
501 mach_bridge_remote_time(uint64_t local_timestamp)
502 {
503 #if defined(CONFIG_MACH_BRIDGE_SEND_TIME)
504 #if !defined(CONFIG_MACH_BRIDGE_RECV_TIME)
505 /* only send side of the bridge is defined: no translation needed */
506 if (!local_timestamp) {
507 return mach_absolute_time();
508 }
509 return 0;
510 #else
511 #error "You cannot define both sides of the bridge!"
512 #endif /* !defined(CONFIG_MACH_BRIDGE_RECV_TIME) */
513 #else
514 #if !defined(CONFIG_MACH_BRIDGE_RECV_TIME)
515 /* neither the send or receive side of the bridge is defined: echo the input */
516 return local_timestamp;
517 #else
518 if (!os_atomic_load(&bt_init_flag, acquire)) {
519 return 0;
520 }
521
522 uint64_t remote_timestamp = 0;
523
524 lck_spin_lock(ts_conversion_lock);
525 uint64_t now = mach_absolute_time();
526 if (!local_timestamp) {
527 local_timestamp = now;
528 }
529 #if defined(XNU_TARGET_OS_BRIDGE)
530 uint64_t local_timestamp_ns = 0;
531 if (local_timestamp < now) {
532 absolutetime_to_nanoseconds(local_timestamp, &local_timestamp_ns);
533 struct bt_params *params = bt_params_find(local_timestamp_ns);
534 remote_timestamp = mach_bridge_compute_timestamp(local_timestamp_ns, params);
535 }
536 #else
537 struct bt_params params = bt_params_get_latest_locked();
538 remote_timestamp = mach_bridge_compute_timestamp(local_timestamp, &params);
539 #endif /* defined(XNU_TARGET_OS_BRIDGE) */
540 lck_spin_unlock(ts_conversion_lock);
541 KDBG(MACHDBG_CODE(DBG_MACH_CLOCK, MACH_BRIDGE_REMOTE_TIME), local_timestamp, remote_timestamp, now);
542
543 return remote_timestamp;
544 #endif /* !defined(CONFIG_MACH_BRIDGE_RECV_TIME) */
545 #endif /* defined(CONFIG_MACH_BRIDGE_SEND_TIME) */
546 }