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