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