xref: /aosp_15_r20/external/trace-cmd/lib/trace-cmd/trace-timesync.c (revision 58e6ee5f017f6a8912852c892d18457e4bafb554)
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, &timestamp, 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, &timestamp, 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