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