1 // SPDX-License-Identifier: LGPL-2.1
2 /*
3 * Copyright (C) 2019, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
4 *
5 */
6
7 #include <fcntl.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <arpa/inet.h>
11 #include <linux/limits.h>
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <time.h>
15 #include <dirent.h>
16 #include <errno.h>
17 #include <pthread.h>
18
19 #include "trace-cmd-private.h"
20 #include "trace-cmd-local.h"
21 #include "tracefs.h"
22 #include "event-utils.h"
23 #include "trace-tsync-local.h"
24
25 struct tsync_proto {
26 struct tsync_proto *next;
27 char proto_name[TRACECMD_TSYNC_PNAME_LENGTH];
28 enum tracecmd_time_sync_role roles;
29 int accuracy;
30 int supported_clocks;
31 unsigned int flags;
32
33 int (*clock_sync_init)(struct tracecmd_time_sync *clock_context);
34 int (*clock_sync_free)(struct tracecmd_time_sync *clock_context);
35 int (*clock_sync_calc)(struct tracecmd_time_sync *clock_context,
36 long long *offset, long long *scaling, long long *frac,
37 long long *timestamp, unsigned int cpu);
38 };
39
40 struct tsync_probe_request_msg {
41 unsigned short cpu;
42 } __packed;
43
44 static struct tsync_proto *tsync_proto_list;
45
tsync_proto_find(const char * proto_name)46 static struct tsync_proto *tsync_proto_find(const char *proto_name)
47 {
48 struct tsync_proto *proto;
49
50 if (!proto_name)
51 return NULL;
52 for (proto = tsync_proto_list; proto; proto = proto->next) {
53 if (strlen(proto->proto_name) == strlen(proto_name) &&
54 !strncmp(proto->proto_name, proto_name, TRACECMD_TSYNC_PNAME_LENGTH))
55 return proto;
56 }
57 return NULL;
58 }
59
60 /**
61 * tracecmd_tsync_init - Initialize the global, per task, time sync data.
62 */
tracecmd_tsync_init(void)63 void tracecmd_tsync_init(void)
64 {
65 ptp_clock_sync_register();
66 kvm_clock_sync_register();
67 }
68
tracecmd_tsync_proto_register(const char * proto_name,int accuracy,int roles,int supported_clocks,unsigned int flags,int (* init)(struct tracecmd_time_sync *),int (* free)(struct tracecmd_time_sync *),int (* calc)(struct tracecmd_time_sync *,long long *,long long *,long long *,long long *,unsigned int))69 int tracecmd_tsync_proto_register(const char *proto_name, int accuracy, int roles,
70 int supported_clocks, unsigned int flags,
71 int (*init)(struct tracecmd_time_sync *),
72 int (*free)(struct tracecmd_time_sync *),
73 int (*calc)(struct tracecmd_time_sync *,
74 long long *, long long *, long long *,
75 long long *, unsigned int))
76 {
77 struct tsync_proto *proto = NULL;
78
79 if (tsync_proto_find(proto_name))
80 return -1;
81 proto = calloc(1, sizeof(struct tsync_proto));
82 if (!proto)
83 return -1;
84 strncpy(proto->proto_name, proto_name, TRACECMD_TSYNC_PNAME_LENGTH);
85 proto->accuracy = accuracy;
86 proto->roles = roles;
87 proto->flags = flags;
88 proto->supported_clocks = supported_clocks;
89 proto->clock_sync_init = init;
90 proto->clock_sync_free = free;
91 proto->clock_sync_calc = calc;
92
93 proto->next = tsync_proto_list;
94 tsync_proto_list = proto;
95 return 0;
96 }
97
tracecmd_tsync_proto_unregister(char * proto_name)98 int tracecmd_tsync_proto_unregister(char *proto_name)
99 {
100 struct tsync_proto **last = &tsync_proto_list;
101
102 if (!proto_name)
103 return -1;
104
105 for (; *last; last = &(*last)->next) {
106 if (strlen((*last)->proto_name) == strlen(proto_name) &&
107 !strncmp((*last)->proto_name, proto_name, TRACECMD_TSYNC_PNAME_LENGTH)) {
108 struct tsync_proto *proto = *last;
109
110 *last = proto->next;
111 free(proto);
112 return 0;
113 }
114 }
115
116 return -1;
117 }
118
tsync_proto_is_supported(const char * proto_name)119 bool __hidden tsync_proto_is_supported(const char *proto_name)
120 {
121 if (tsync_proto_find(proto_name))
122 return true;
123 return false;
124 }
125
126 /**
127 * tracecmd_tsync_get_offsets - Return the calculated time offsets
128 *
129 * @tsync: Pointer to time sync context
130 * @cpu: CPU for which to get the calculated offsets
131 * @count: Returns the number of calculated time offsets
132 * @ts: Array of size @count containing timestamps of callculated offsets
133 * @offsets: array of size @count, containing offsets for each timestamp
134 * @scalings: array of size @count, containing scaling ratios for each timestamp
135 * @frac: array of size @count, containing fraction bits for each timestamp
136 *
137 * Retuns -1 in case of an error, or 0 otherwise
138 */
tracecmd_tsync_get_offsets(struct tracecmd_time_sync * tsync,int cpu,int * count,long long ** ts,long long ** offsets,long long ** scalings,long long ** frac)139 int tracecmd_tsync_get_offsets(struct tracecmd_time_sync *tsync, int cpu,
140 int *count, long long **ts,
141 long long **offsets, long long **scalings, long long **frac)
142 {
143 struct clock_sync_context *tsync_context;
144
145 if (!tsync || !tsync->context)
146 return -1;
147 tsync_context = (struct clock_sync_context *)tsync->context;
148 if (cpu >= tsync_context->cpu_count || !tsync_context->offsets)
149 return -1;
150 if (count)
151 *count = tsync_context->offsets[cpu].sync_count;
152 if (ts)
153 *ts = tsync_context->offsets[cpu].sync_ts;
154 if (offsets)
155 *offsets = tsync_context->offsets[cpu].sync_offsets;
156 if (scalings)
157 *scalings = tsync_context->offsets[cpu].sync_scalings;
158 if (frac)
159 *frac = tsync_context->offsets[cpu].sync_frac;
160
161 return 0;
162 }
163
164 /**
165 * tsync_get_proto_flags - Get protocol flags
166 *
167 * @tsync: Pointer to time sync context
168 * @flags: Returns the protocol flags, a combination of TRACECMD_TSYNC_FLAG_...
169 *
170 * Retuns -1 in case of an error, or 0 otherwise
171 */
tsync_get_proto_flags(struct tracecmd_time_sync * tsync,unsigned int * flags)172 static int tsync_get_proto_flags(struct tracecmd_time_sync *tsync,
173 unsigned int *flags)
174 {
175 struct tsync_proto *protocol;
176
177 if (!tsync)
178 return -1;
179 protocol = tsync_proto_find(tsync->proto_name);
180 if (!protocol)
181 return -1;
182
183 if (flags)
184 *flags = protocol->flags;
185
186 return 0;
187 }
188
189
190 #define PROTO_MASK_SIZE (sizeof(char))
191 #define PROTO_MASK_BITS (PROTO_MASK_SIZE * 8)
192 /**
193 * tsync_proto_select - Select time sync protocol, to be used for
194 * timestamp synchronization with a peer
195 *
196 * @protos: list of tsync protocol names
197 * @clock : trace clock
198 * @role : local time sync role
199 *
200 * Retuns pointer to a protocol name, that can be used with the peer, or NULL
201 * in case there is no match with supported protocols.
202 * The returned string MUST NOT be freed by the caller
203 */
204 static const char *
tsync_proto_select(const struct tracecmd_tsync_protos * protos,const char * clock,enum tracecmd_time_sync_role role)205 tsync_proto_select(const struct tracecmd_tsync_protos *protos,
206 const char *clock, enum tracecmd_time_sync_role role)
207 {
208 struct tsync_proto *selected = NULL;
209 struct tsync_proto *proto;
210 char **pname;
211 int clock_id = 0;
212
213 if (!protos)
214 return NULL;
215
216 clock_id = tracecmd_clock_str2id(clock);
217 pname = protos->names;
218 while (*pname) {
219 for (proto = tsync_proto_list; proto; proto = proto->next) {
220 if (!(proto->roles & role))
221 continue;
222 if (proto->supported_clocks && clock_id &&
223 !(proto->supported_clocks & clock_id))
224 continue;
225 if (strncmp(proto->proto_name, *pname, TRACECMD_TSYNC_PNAME_LENGTH))
226 continue;
227 if (selected) {
228 if (selected->accuracy > proto->accuracy)
229 selected = proto;
230 } else
231 selected = proto;
232 }
233 pname++;
234 }
235
236 if (selected)
237 return selected->proto_name;
238
239 return NULL;
240 }
241
242 /**
243 * tracecmd_tsync_get_proto - return the appropriate synchronization protocol
244 * @protos: The list of synchronization protocols to choose from
245 * @clock: The clock that is being used (or NULL for unknown).
246 *
247 * Retuns pointer to a protocol name, that can be used with the peer, or NULL
248 * in case there is no match with supported protocols.
249 * The returned string MUST NOT be freed by the caller
250 */
251 __hidden const char *
tracecmd_tsync_get_proto(const struct tracecmd_tsync_protos * protos,const char * clock,enum tracecmd_time_sync_role role)252 tracecmd_tsync_get_proto(const struct tracecmd_tsync_protos *protos,
253 const char *clock, enum tracecmd_time_sync_role role)
254 {
255 return tsync_proto_select(protos, clock, role);
256 }
257
258 /**
259 * tracecmd_tsync_proto_getall - Returns list of all supported
260 * time sync protocols
261 * @protos: return, allocated list of time sync protocol names,
262 * supported by the peer. Must be freed by free()
263 * @clock: selected trace clock
264 * @role: supported protocol role
265 *
266 * If completed successfully 0 is returned and allocated list of strings in @protos.
267 * The last list entry is NULL. In case of an error, -1 is returned.
268 * @protos must be freed with free()
269 */
tracecmd_tsync_proto_getall(struct tracecmd_tsync_protos ** protos,const char * clock,int role)270 int tracecmd_tsync_proto_getall(struct tracecmd_tsync_protos **protos, const char *clock, int role)
271 {
272 struct tracecmd_tsync_protos *plist = NULL;
273 struct tsync_proto *proto;
274 int clock_id = 0;
275 int count = 1;
276 int i;
277
278 if (clock)
279 clock_id = tracecmd_clock_str2id(clock);
280 for (proto = tsync_proto_list; proto; proto = proto->next) {
281 if (!(proto->roles & role))
282 continue;
283 if (proto->supported_clocks && clock_id &&
284 !(proto->supported_clocks & clock_id))
285 continue;
286 count++;
287 }
288 plist = calloc(1, sizeof(struct tracecmd_tsync_protos));
289 if (!plist)
290 goto error;
291 plist->names = calloc(count, sizeof(char *));
292 if (!plist->names)
293 goto error;
294
295 for (i = 0, proto = tsync_proto_list; proto && i < (count - 1); proto = proto->next) {
296 if (!(proto->roles & role))
297 continue;
298 if (proto->supported_clocks && clock_id &&
299 !(proto->supported_clocks & clock_id))
300 continue;
301 plist->names[i++] = proto->proto_name;
302 }
303
304 *protos = plist;
305 return 0;
306
307 error:
308 if (plist) {
309 free(plist->names);
310 free(plist);
311 }
312 return -1;
313 }
314
get_first_cpu(cpu_set_t ** pin_mask,size_t * m_size)315 static int get_first_cpu(cpu_set_t **pin_mask, size_t *m_size)
316 {
317 int cpus = tracecmd_count_cpus();
318 cpu_set_t *cpu_mask;
319 int mask_size;
320 int i;
321
322 cpu_mask = CPU_ALLOC(cpus);
323 *pin_mask = CPU_ALLOC(cpus);
324 if (!cpu_mask || !*pin_mask || 1)
325 goto error;
326
327 mask_size = CPU_ALLOC_SIZE(cpus);
328 CPU_ZERO_S(mask_size, cpu_mask);
329 CPU_ZERO_S(mask_size, *pin_mask);
330
331 if (sched_getaffinity(0, mask_size, cpu_mask) == -1)
332 goto error;
333
334 for (i = 0; i < cpus; i++) {
335 if (CPU_ISSET_S(i, mask_size, cpu_mask)) {
336 CPU_SET_S(i, mask_size, *pin_mask);
337 break;
338 }
339 }
340
341 if (CPU_COUNT_S(mask_size, *pin_mask) < 1)
342 goto error;
343
344 CPU_FREE(cpu_mask);
345 *m_size = mask_size;
346 return 0;
347
348 error:
349 if (cpu_mask)
350 CPU_FREE(cpu_mask);
351 if (*pin_mask)
352 CPU_FREE(*pin_mask);
353 *pin_mask = NULL;
354 *m_size = 0;
355 return -1;
356 }
357
358 static struct tracefs_instance *
clock_synch_create_instance(const char * clock,unsigned int cid)359 clock_synch_create_instance(const char *clock, unsigned int cid)
360 {
361 struct tracefs_instance *instance;
362 char inst_name[256];
363
364 snprintf(inst_name, 256, "clock_synch-%d", cid);
365
366 instance = tracefs_instance_create(inst_name);
367 if (!instance)
368 return NULL;
369
370 tracefs_instance_file_write(instance, "trace", "\0");
371 if (clock)
372 tracefs_instance_file_write(instance, "trace_clock", clock);
373 return instance;
374 }
375
376 static void
clock_synch_delete_instance(struct tracefs_instance * inst)377 clock_synch_delete_instance(struct tracefs_instance *inst)
378 {
379 if (!inst)
380 return;
381 tracefs_instance_destroy(inst);
382 tracefs_instance_free(inst);
383 }
384
clock_context_init(struct tracecmd_time_sync * tsync,bool guest)385 static int clock_context_init(struct tracecmd_time_sync *tsync, bool guest)
386 {
387 struct clock_sync_context *clock = NULL;
388 struct tsync_proto *protocol;
389
390 if (tsync->context)
391 return 0;
392
393 protocol = tsync_proto_find(tsync->proto_name);
394 if (!protocol || !protocol->clock_sync_calc)
395 return -1;
396
397 clock = calloc(1, sizeof(struct clock_sync_context));
398 if (!clock)
399 return -1;
400 clock->is_guest = guest;
401 clock->is_server = clock->is_guest;
402
403 clock->instance = clock_synch_create_instance(tsync->clock_str,
404 tsync->remote_id);
405 if (!clock->instance)
406 goto error;
407
408 clock->cpu_count = tsync->vcpu_count;
409 if (clock->cpu_count) {
410 clock->offsets = calloc(clock->cpu_count, sizeof(struct clock_sync_offsets));
411 if (!clock->offsets)
412 goto error;
413 }
414
415 tsync->context = clock;
416 if (protocol->clock_sync_init && protocol->clock_sync_init(tsync) < 0)
417 goto error;
418
419 tsync->proto = protocol;
420
421 return 0;
422 error:
423 tsync->context = NULL;
424 if (clock->instance)
425 clock_synch_delete_instance(clock->instance);
426 free(clock->offsets);
427 free(clock);
428 return -1;
429 }
430
431 /**
432 * tracecmd_tsync_free - Free time sync context, allocated by
433 * tracecmd_tsync_with_host() or tracecmd_tsync_with_guest() APIs
434 *
435 * @tsync: Pointer to time sync context
436 *
437 */
tracecmd_tsync_free(struct tracecmd_time_sync * tsync)438 void tracecmd_tsync_free(struct tracecmd_time_sync *tsync)
439 {
440 struct clock_sync_context *tsync_context;
441 struct tsync_proto *proto;
442 int i;
443
444 if (!tsync)
445 return;
446
447 tsync_context = (struct clock_sync_context *)tsync->context;
448
449 proto = tsync_proto_find(tsync->proto_name);
450 if (proto && proto->clock_sync_free)
451 proto->clock_sync_free(tsync);
452
453
454 if (tsync_context) {
455 clock_synch_delete_instance(tsync_context->instance);
456 tsync_context->instance = NULL;
457
458 if (tsync_context->cpu_count && tsync_context->offsets) {
459 for (i = 0; i < tsync_context->cpu_count; i++) {
460 free(tsync_context->offsets[i].sync_ts);
461 free(tsync_context->offsets[i].sync_offsets);
462 free(tsync_context->offsets[i].sync_scalings);
463 free(tsync_context->offsets[i].sync_frac);
464 tsync_context->offsets[i].sync_ts = NULL;
465 tsync_context->offsets[i].sync_offsets = NULL;
466 tsync_context->offsets[i].sync_scalings = NULL;
467 tsync_context->offsets[i].sync_frac = NULL;
468 tsync_context->offsets[i].sync_count = 0;
469 tsync_context->offsets[i].sync_size = 0;
470 }
471 free(tsync_context->offsets);
472 tsync_context->offsets = NULL;
473 }
474 }
475
476 if (tsync->msg_handle)
477 tracecmd_msg_handle_close(tsync->msg_handle);
478
479 /* These are only created from the host */
480 if (tsync->guest_pid) {
481 pthread_mutex_destroy(&tsync->lock);
482 pthread_cond_destroy(&tsync->cond);
483 pthread_barrier_destroy(&tsync->first_sync);
484 }
485
486 free(tsync->clock_str);
487 free(tsync->proto_name);
488 free(tsync);
489 }
490
pin_to_cpu(int cpu)491 static cpu_set_t *pin_to_cpu(int cpu)
492 {
493 static size_t size;
494 static int cpus;
495 cpu_set_t *mask = NULL;
496 cpu_set_t *old = NULL;
497
498 if (!cpus) {
499 cpus = tracecmd_count_cpus();
500 size = CPU_ALLOC_SIZE(cpus);
501 }
502 if (cpu >= cpus)
503 goto error;
504
505 mask = CPU_ALLOC(cpus);
506 if (!mask)
507 goto error;
508 old = CPU_ALLOC(cpus);
509 if (!old)
510 goto error;
511
512 CPU_ZERO_S(size, mask);
513 CPU_SET_S(cpu, size, mask);
514 if (pthread_getaffinity_np(pthread_self(), size, old))
515 goto error;
516 if (pthread_setaffinity_np(pthread_self(), size, mask))
517 goto error;
518
519 CPU_FREE(mask);
520 return old;
521
522 error:
523 if (mask)
524 CPU_FREE(mask);
525 if (old)
526 CPU_FREE(old);
527 return NULL;
528 }
529
restore_pin_to_cpu(cpu_set_t * mask)530 static void restore_pin_to_cpu(cpu_set_t *mask)
531 {
532 static size_t size;
533
534 if (!size)
535 size = CPU_ALLOC_SIZE(tracecmd_count_cpus());
536
537 pthread_setaffinity_np(pthread_self(), size, mask);
538 CPU_FREE(mask);
539 }
540
tsync_send(struct tracecmd_time_sync * tsync,unsigned int cpu)541 static int tsync_send(struct tracecmd_time_sync *tsync, unsigned int cpu)
542 {
543 struct tsync_proto *proto = tsync->proto;
544 cpu_set_t *old_set = NULL;
545 long long timestamp = 0;
546 long long scaling = 0;
547 long long offset = 0;
548 long long frac = 0;
549 int ret;
550
551 old_set = pin_to_cpu(cpu);
552 ret = proto->clock_sync_calc(tsync, &offset, &scaling, &frac, ×tamp, cpu);
553 if (old_set)
554 restore_pin_to_cpu(old_set);
555
556 return ret;
557 }
558
tsync_with_host(struct tracecmd_time_sync * tsync)559 static void tsync_with_host(struct tracecmd_time_sync *tsync)
560 {
561 char protocol[TRACECMD_TSYNC_PNAME_LENGTH];
562 struct tsync_probe_request_msg probe;
563 unsigned int command;
564 unsigned int size;
565 char *msg;
566 int ret;
567
568 msg = (char *)&probe;
569 size = sizeof(probe);
570 while (true) {
571 memset(&probe, 0, size);
572 ret = tracecmd_msg_recv_time_sync(tsync->msg_handle,
573 protocol, &command,
574 &size, &msg);
575
576 if (ret || strncmp(protocol, TRACECMD_TSYNC_PROTO_NONE, TRACECMD_TSYNC_PNAME_LENGTH) ||
577 command != TRACECMD_TIME_SYNC_CMD_PROBE)
578 break;
579 ret = tsync_send(tsync, probe.cpu);
580 if (ret)
581 break;
582 }
583 }
584
record_sync_sample(struct clock_sync_offsets * offsets,int array_step,long long offset,long long scaling,long long frac,long long ts)585 static int record_sync_sample(struct clock_sync_offsets *offsets, int array_step,
586 long long offset, long long scaling, long long frac, long long ts)
587 {
588 long long *sync_scalings = NULL;
589 long long *sync_offsets = NULL;
590 long long *sync_frac = NULL;
591 long long *sync_ts = NULL;
592
593 if (offsets->sync_count >= offsets->sync_size) {
594 sync_ts = realloc(offsets->sync_ts,
595 (offsets->sync_size + array_step) * sizeof(long long));
596 sync_offsets = realloc(offsets->sync_offsets,
597 (offsets->sync_size + array_step) * sizeof(long long));
598 sync_scalings = realloc(offsets->sync_scalings,
599 (offsets->sync_size + array_step) * sizeof(long long));
600 sync_frac = realloc(offsets->sync_frac,
601 (offsets->sync_size + array_step) * sizeof(long long));
602
603 if (!sync_ts || !sync_offsets || !sync_scalings || !sync_frac) {
604 free(sync_ts);
605 free(sync_offsets);
606 free(sync_scalings);
607 free(sync_frac);
608 return -1;
609 }
610 offsets->sync_size += array_step;
611 offsets->sync_ts = sync_ts;
612 offsets->sync_offsets = sync_offsets;
613 offsets->sync_scalings = sync_scalings;
614 offsets->sync_frac = sync_frac;
615 }
616
617 offsets->sync_ts[offsets->sync_count] = ts;
618 offsets->sync_offsets[offsets->sync_count] = offset;
619 offsets->sync_scalings[offsets->sync_count] = scaling;
620 offsets->sync_frac[offsets->sync_count] = frac;
621 offsets->sync_count++;
622
623 return 0;
624 }
625
tsync_get_sample(struct tracecmd_time_sync * tsync,unsigned int cpu,int array_step)626 static int tsync_get_sample(struct tracecmd_time_sync *tsync, unsigned int cpu,
627 int array_step)
628 {
629 struct tsync_proto *proto = tsync->proto;
630 struct clock_sync_context *clock;
631 long long timestamp = 0;
632 long long scaling = 0;
633 long long offset = 0;
634 long long frac = 0;
635 int ret;
636
637 ret = proto->clock_sync_calc(tsync, &offset, &scaling, &frac, ×tamp, cpu);
638 if (ret) {
639 tracecmd_warning("Failed to synchronize timestamps with guest");
640 return -1;
641 }
642 if (!offset || !timestamp || !scaling)
643 return 0;
644 clock = tsync->context;
645 if (!clock || cpu >= clock->cpu_count || !clock->offsets)
646 return -1;
647 return record_sync_sample(&clock->offsets[cpu], array_step,
648 offset, scaling, frac, timestamp);
649 }
650
651 #define TIMER_SEC_NANO 1000000000LL
get_ts_loop_delay(struct timespec * timeout,int delay_ms)652 static inline void get_ts_loop_delay(struct timespec *timeout, int delay_ms)
653 {
654 memset(timeout, 0, sizeof(struct timespec));
655 clock_gettime(CLOCK_REALTIME, timeout);
656
657 timeout->tv_nsec += ((unsigned long long)delay_ms * 1000000LL);
658
659 if (timeout->tv_nsec >= TIMER_SEC_NANO) {
660 timeout->tv_sec += timeout->tv_nsec / TIMER_SEC_NANO;
661 timeout->tv_nsec %= TIMER_SEC_NANO;
662 }
663 }
664
665 #define CLOCK_TS_ARRAY 5
tsync_with_guest(struct tracecmd_time_sync * tsync)666 static int tsync_with_guest(struct tracecmd_time_sync *tsync)
667 {
668 struct tsync_probe_request_msg probe;
669 int ts_array_size = CLOCK_TS_ARRAY;
670 struct timespec timeout;
671 bool first = true;
672 bool end = false;
673 int ret;
674 int i;
675
676 if (tsync->loop_interval > 0 &&
677 tsync->loop_interval < (CLOCK_TS_ARRAY * 1000))
678 ts_array_size = (CLOCK_TS_ARRAY * 1000) / tsync->loop_interval;
679
680 while (true) {
681 pthread_mutex_lock(&tsync->lock);
682 for (i = 0; i < tsync->vcpu_count; i++) {
683 probe.cpu = i;
684 ret = tracecmd_msg_send_time_sync(tsync->msg_handle,
685 TRACECMD_TSYNC_PROTO_NONE,
686 TRACECMD_TIME_SYNC_CMD_PROBE,
687 sizeof(probe), (char *)&probe);
688 ret = tsync_get_sample(tsync, i, ts_array_size);
689 if (ret)
690 break;
691 }
692 if (first) {
693 first = false;
694 pthread_barrier_wait(&tsync->first_sync);
695 }
696 if (end || i < tsync->vcpu_count) {
697 pthread_mutex_unlock(&tsync->lock);
698 break;
699 }
700 if (tsync->loop_interval > 0) {
701 get_ts_loop_delay(&timeout, tsync->loop_interval);
702 ret = pthread_cond_timedwait(&tsync->cond, &tsync->lock, &timeout);
703 pthread_mutex_unlock(&tsync->lock);
704 if (ret && ret != ETIMEDOUT)
705 break;
706 else if (!ret)
707 end = true;
708 } else {
709 pthread_cond_wait(&tsync->cond, &tsync->lock);
710 end = true;
711 pthread_mutex_unlock(&tsync->lock);
712 }
713 };
714
715 tracecmd_msg_send_time_sync(tsync->msg_handle,
716 TRACECMD_TSYNC_PROTO_NONE,
717 TRACECMD_TIME_SYNC_CMD_STOP,
718 0, NULL);
719 return 0;
720 }
721
tsync_host_thread(void * data)722 static void *tsync_host_thread(void *data)
723 {
724 struct tracecmd_time_sync *tsync = data;
725
726 tsync_with_guest(tsync);
727 pthread_exit(0);
728 }
729
730 /**
731 * tracecmd_tsync_with_guest - Synchronize timestamps with guest
732 *
733 * @trace_id: Local ID for the current trace session
734 * @fd: file descriptor of guest
735 * @guest_pid: PID of the host OS process, running the guest
736 * @guest_cpus: Number of the guest VCPUs
737 * @proto_name: Name of the negotiated time synchronization protocol
738 * @clock: Trace clock, used for that session
739 *
740 * On success, a pointer to time sync context is returned, or NULL in
741 * case of an error. The context must be freed with tracecmd_tsync_free()
742 *
743 * This API spawns a pthread, which performs time stamps synchronization
744 * until tracecmd_tsync_with_guest_stop() is called.
745 */
746 struct tracecmd_time_sync *
tracecmd_tsync_with_guest(unsigned long long trace_id,int loop_interval,unsigned int fd,int guest_pid,int guest_cpus,const char * proto_name,const char * clock)747 tracecmd_tsync_with_guest(unsigned long long trace_id, int loop_interval,
748 unsigned int fd, int guest_pid,
749 int guest_cpus, const char *proto_name, const char *clock)
750 {
751 struct tracecmd_time_sync *tsync;
752 cpu_set_t *pin_mask = NULL;
753 pthread_attr_t attrib;
754 size_t mask_size = 0;
755 int ret;
756
757 if (!proto_name)
758 return NULL;
759
760 tsync = calloc(1, sizeof(*tsync));
761 if (!tsync)
762 return NULL;
763
764 tsync->trace_id = trace_id;
765 tsync->loop_interval = loop_interval;
766 tsync->proto_name = strdup(proto_name);
767
768 tsync->msg_handle = tracecmd_msg_handle_alloc(fd, 0);
769 if (!tsync->msg_handle) {
770 ret = -1;
771 goto error;
772 }
773 tsync->guest_pid = guest_pid;
774 tsync->vcpu_count = guest_cpus;
775
776 if (clock)
777 tsync->clock_str = strdup(clock);
778 pthread_mutex_init(&tsync->lock, NULL);
779 pthread_cond_init(&tsync->cond, NULL);
780 pthread_barrier_init(&tsync->first_sync, NULL, 2);
781 pthread_attr_init(&attrib);
782 pthread_attr_setdetachstate(&attrib, PTHREAD_CREATE_JOINABLE);
783
784 clock_context_init(tsync, false);
785 if (!tsync->context)
786 goto error;
787
788 ret = pthread_create(&tsync->thread, &attrib, tsync_host_thread, tsync);
789 if (ret)
790 goto error;
791 tsync->thread_running = true;
792
793 if (!get_first_cpu(&pin_mask, &mask_size))
794 pthread_setaffinity_np(tsync->thread, mask_size, pin_mask);
795 pthread_barrier_wait(&tsync->first_sync);
796
797 if (pin_mask)
798 CPU_FREE(pin_mask);
799 pthread_attr_destroy(&attrib);
800
801 return tsync;
802
803 error:
804 if (tsync->msg_handle)
805 tracecmd_msg_handle_close(tsync->msg_handle);
806 else if (fd >= 0)
807 close(fd);
808 free(tsync);
809
810 return NULL;
811 }
812
813 /**
814 * tracecmd_write_guest_time_shift - Write collected timestamp corrections in a file
815 *
816 * @handle: Handle to a trace file, where timestamp corrections will be saved
817 * @tsync: Time sync context with collected timestamp corrections
818 *
819 * Returns 0 on success, or -1 in case of an error.
820 *
821 * This API writes collected timestamp corrections in the metadata of the
822 * trace file, as TRACECMD_OPTION_TIME_SHIFT option.
823 */
tracecmd_write_guest_time_shift(struct tracecmd_output * handle,struct tracecmd_time_sync * tsync)824 int tracecmd_write_guest_time_shift(struct tracecmd_output *handle,
825 struct tracecmd_time_sync *tsync)
826 {
827 struct iovec *vector = NULL;
828 unsigned int flags;
829 long long *scalings = NULL;
830 long long *offsets = NULL;
831 long long *frac = NULL;
832 long long *ts = NULL;
833 int vcount;
834 int count;
835 int i, j;
836 int ret = -1;
837
838 if (!tsync || !tsync->vcpu_count)
839 return -1;
840 vcount = 3 + (5 * tsync->vcpu_count);
841 vector = calloc(vcount, sizeof(struct iovec));
842 if (!vector)
843 return -1;
844 ret = tsync_get_proto_flags(tsync, &flags);
845 if (ret < 0)
846 goto out;
847
848 j = 0;
849 vector[j].iov_len = 8;
850 vector[j++].iov_base = &tsync->trace_id;
851 vector[j].iov_len = 4;
852 vector[j++].iov_base = &flags;
853 vector[j].iov_len = 4;
854 vector[j++].iov_base = &tsync->vcpu_count;
855 for (i = 0; i < tsync->vcpu_count; i++) {
856 if (j >= vcount)
857 break;
858 ret = tracecmd_tsync_get_offsets(tsync, i, &count,
859 &ts, &offsets, &scalings, NULL);
860 if (ret < 0 || !count || !ts || !offsets || !scalings)
861 break;
862 vector[j].iov_len = 4;
863 vector[j++].iov_base = &count;
864 vector[j].iov_len = 8 * count;
865 vector[j++].iov_base = ts;
866 vector[j].iov_len = 8 * count;
867 vector[j++].iov_base = offsets;
868 vector[j].iov_len = 8 * count;
869 vector[j++].iov_base = scalings;
870 }
871 if (i < tsync->vcpu_count) {
872 ret = -1;
873 goto out;
874 }
875 /*
876 * Writing fraction bits into the option is implemented in a separate loop for
877 * backward compatibility. In the trace-cmd 2.9 release, this option has only offset
878 * and scaling. That legacy code must work with the new extended option.
879 *
880 */
881 for (i = 0; i < tsync->vcpu_count; i++) {
882 if (j >= vcount)
883 break;
884 ret = tracecmd_tsync_get_offsets(tsync, i, NULL,
885 NULL, NULL, NULL, &frac);
886 if (ret < 0)
887 break;
888 vector[j].iov_len = 8 * count;
889 vector[j++].iov_base = frac;
890 }
891 if (i < tsync->vcpu_count) {
892 ret = -1;
893 goto out;
894 }
895
896 tracecmd_add_option_v(handle, TRACECMD_OPTION_TIME_SHIFT, vector, vcount);
897 #ifdef TSYNC_DEBUG
898 if (count > 1)
899 printf("Got %d timestamp synch samples in %lld ns trace\n\r",
900 count, ts[count - 1] - ts[0]);
901 #endif
902 ret = 0;
903 out:
904 free(vector);
905 return ret;
906 }
907
908 /**
909 * tracecmd_tsync_with_guest_stop - Stop the time sync session with a guest
910 *
911 * @tsync: Time sync context, representing a running time sync session
912 *
913 * Returns 0 on success, or -1 in case of an error.
914 *
915 */
tracecmd_tsync_with_guest_stop(struct tracecmd_time_sync * tsync)916 int tracecmd_tsync_with_guest_stop(struct tracecmd_time_sync *tsync)
917 {
918 if (!tsync || !tsync->thread_running)
919 return -1;
920
921 /* Signal the time synchronization thread to complete and wait for it */
922 pthread_mutex_lock(&tsync->lock);
923 pthread_cond_signal(&tsync->cond);
924 pthread_mutex_unlock(&tsync->lock);
925 pthread_join(tsync->thread, NULL);
926 return 0;
927 }
928
tsync_agent_thread(void * data)929 static void *tsync_agent_thread(void *data)
930 {
931 struct tracecmd_time_sync *tsync = data;
932
933 tsync_with_host(tsync);
934 pthread_exit(NULL);
935 }
936
937 /**
938 * tracecmd_tsync_with_host - Synchronize timestamps with host
939 * @fd: File descriptor connecting with the host
940 * @proto: The selected protocol
941 * @clock: Trace clock, used for that session
942 * @port: returned, VSOCKET port, on which the guest listens for tsync requests
943 * @remote_id: Identifier to uniquely identify the remote host
944 * @local_id: Identifier to uniquely identify the local machine
945 *
946 * On success, a pointer to time sync context is returned, or NULL in
947 * case of an error. The context must be freed with tracecmd_tsync_free()
948 *
949 * This API spawns a pthread, which performs time stamps synchronization
950 * until tracecmd_tsync_with_host_stop() is called.
951 */
952 struct tracecmd_time_sync *
tracecmd_tsync_with_host(int fd,const char * proto,const char * clock,int remote_id,int local_id)953 tracecmd_tsync_with_host(int fd, const char *proto, const char *clock,
954 int remote_id, int local_id)
955 {
956 struct tracecmd_time_sync *tsync;
957 cpu_set_t *pin_mask = NULL;
958 pthread_attr_t attrib;
959 size_t mask_size = 0;
960 int ret;
961
962 tsync = calloc(1, sizeof(struct tracecmd_time_sync));
963 if (!tsync)
964 return NULL;
965
966 tsync->proto_name = strdup(proto);
967 tsync->msg_handle = tracecmd_msg_handle_alloc(fd, 0);
968 if (clock)
969 tsync->clock_str = strdup(clock);
970
971 tsync->remote_id = remote_id;
972 tsync->local_id = local_id;
973
974 pthread_attr_init(&attrib);
975 tsync->vcpu_count = tracecmd_count_cpus();
976 pthread_attr_setdetachstate(&attrib, PTHREAD_CREATE_JOINABLE);
977
978 clock_context_init(tsync, true);
979 if (!tsync->context)
980 goto error;
981
982 ret = pthread_create(&tsync->thread, &attrib, tsync_agent_thread, tsync);
983 if (ret) {
984 pthread_attr_destroy(&attrib);
985 goto error;
986 }
987 tsync->thread_running = true;
988 if (!get_first_cpu(&pin_mask, &mask_size))
989 pthread_setaffinity_np(tsync->thread, mask_size, pin_mask);
990
991 if (pin_mask)
992 CPU_FREE(pin_mask);
993 pthread_attr_destroy(&attrib);
994 return tsync;
995
996 error:
997 if (tsync) {
998 if (tsync->msg_handle) {
999 /* Do not close the fd that was passed it */
1000 tsync->msg_handle->fd = -1;
1001 tracecmd_msg_handle_close(tsync->msg_handle);
1002 }
1003 free(tsync->clock_str);
1004 free(tsync);
1005 }
1006
1007 return NULL;
1008
1009 }
1010
1011 /**
1012 * tracecmd_tsync_with_host_stop - Stop the time sync session with a host
1013 *
1014 * @tsync: Time sync context, representing a running time sync session
1015 *
1016 * Returns 0 on success, or error number in case of an error.
1017 *
1018 */
tracecmd_tsync_with_host_stop(struct tracecmd_time_sync * tsync)1019 int tracecmd_tsync_with_host_stop(struct tracecmd_time_sync *tsync)
1020 {
1021 return pthread_join(tsync->thread, NULL);
1022 }
1023