• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &timestamp, 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, &timestamp, 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