| // SPDX-License-Identifier: LGPL-2.1 |
| /* |
| * Copyright (C) 2019, VMware, Tzvetomir Stoyanov tz.stoyanov@gmail.com> |
| * |
| */ |
| |
| #include <fcntl.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <arpa/inet.h> |
| #include <sys/types.h> |
| #include <linux/types.h> |
| #include <time.h> |
| #include <sched.h> |
| #include <limits.h> |
| |
| #include "trace-cmd.h" |
| #include "trace-cmd-private.h" |
| #include "tracefs.h" |
| #include "trace-tsync-local.h" |
| #include "trace-msg.h" |
| #include "trace-cmd-local.h" |
| |
| typedef __be32 be32; |
| typedef __u64 u64; |
| typedef __s64 s64; |
| |
| #define PTP_SYNC_LOOP 339 |
| |
| #define PTP_SYNC_PKT_START 1 |
| #define PTP_SYNC_PKT_PROBE 2 |
| #define PTP_SYNC_PKT_PROBES 3 |
| #define PTP_SYNC_PKT_OFFSET 4 |
| #define PTP_SYNC_PKT_END 5 |
| |
| /* print time sync debug messages */ |
| /* #define TSYNC_DEBUG */ |
| |
| struct ptp_clock_sync { |
| struct tep_handle *tep; |
| struct tep_format_field *id; |
| int raw_id; |
| int marker_fd; |
| int series_id; |
| int flags; |
| int debug_fd; |
| }; |
| |
| enum { |
| /* |
| * Consider only the probe with fastest response time, |
| * otherwise make a histogram from all probes. |
| */ |
| PTP_FLAG_FASTEST_RESPONSE = (1 << 0), |
| /* |
| * Use trace marker to get the clock, |
| * otherwise use the system clock directly. |
| */ |
| PTP_FLAG_USE_MARKER = (1 << 1), |
| }; |
| static int ptp_flags = PTP_FLAG_FASTEST_RESPONSE | PTP_FLAG_USE_MARKER; |
| |
| /* |
| * Calculated using formula [CPU rate]*[calculated offset deviation] |
| * tested on 3GHz CPU, with x86-tsc trace clock and compare the calculated |
| * offset with /sys/kernel/debug/kvm/<VM ID>/vcpu0/tsc-offset |
| * measured 2000ns deviation |
| * using PTP flags PTP_FLAG_FASTEST_RESPONSE | PTP_FLAG_USE_MARKER |
| */ |
| #define PTP_ACCURACY 6000 |
| #define PTP_NAME "ptp" |
| |
| struct ptp_clock_start_msg { |
| be32 series_id; |
| be32 flags; |
| } __packed; |
| |
| struct ptp_clock_sample { |
| s64 ts; |
| be32 id; |
| } __packed; |
| |
| struct ptp_clock_result_msg { |
| be32 series_id; |
| be32 count; |
| struct ptp_clock_sample samples[2*PTP_SYNC_LOOP]; |
| } __packed; |
| |
| struct ptp_clock_offset_msg { |
| s64 ts; |
| s64 offset; |
| }; |
| |
| struct ptp_markers_context { |
| struct clock_sync_context *clock; |
| struct ptp_clock_sync *ptp; |
| struct ptp_clock_result_msg msg; |
| int size; |
| }; |
| |
| struct ptp_marker_buf { |
| int local_id; |
| int remote_id; |
| int count; |
| int packet_id; |
| } __packed; |
| |
| struct ptp_marker { |
| int series_id; |
| struct ptp_marker_buf data; |
| } __packed; |
| |
| static int ptp_clock_sync_init(struct tracecmd_time_sync *tsync) |
| { |
| const char *systems[] = {"ftrace", NULL}; |
| struct clock_sync_context *clock_context; |
| struct ptp_clock_sync *ptp; |
| struct tep_event *raw; |
| char *path; |
| |
| if (!tsync || !tsync->context) |
| return -1; |
| clock_context = (struct clock_sync_context *)tsync->context; |
| if (clock_context->proto_data) |
| return 0; |
| |
| ptp = calloc(1, sizeof(struct ptp_clock_sync)); |
| if (!ptp) |
| return -1; |
| |
| ptp->marker_fd = -1; |
| ptp->debug_fd = -1; |
| |
| path = tracefs_instance_get_dir(clock_context->instance); |
| if (!path) |
| goto error; |
| ptp->tep = tracefs_local_events_system(path, systems); |
| tracefs_put_tracing_file(path); |
| if (!ptp->tep) |
| goto error; |
| raw = tep_find_event_by_name(ptp->tep, "ftrace", "raw_data"); |
| if (!raw) |
| goto error; |
| ptp->id = tep_find_field(raw, "id"); |
| if (!ptp->id) |
| goto error; |
| ptp->raw_id = raw->id; |
| |
| tep_set_file_bigendian(ptp->tep, tracecmd_host_bigendian()); |
| tep_set_local_bigendian(ptp->tep, tracecmd_host_bigendian()); |
| |
| path = tracefs_instance_get_file(clock_context->instance, "trace_marker_raw"); |
| if (!path) |
| goto error; |
| ptp->marker_fd = open(path, O_WRONLY); |
| tracefs_put_tracing_file(path); |
| |
| clock_context->proto_data = ptp; |
| |
| #ifdef TSYNC_DEBUG |
| if (clock_context->is_server) { |
| char buff[256]; |
| int res_fd; |
| |
| sprintf(buff, "res-id%d.txt", clock_context->remote_id); |
| |
| res_fd = open(buff, O_CREAT|O_WRONLY|O_TRUNC, 0644); |
| if (res_fd > 0) |
| close(res_fd); |
| } |
| #endif |
| |
| return 0; |
| |
| error: |
| if (ptp) { |
| tep_free(ptp->tep); |
| if (ptp->marker_fd >= 0) |
| close(ptp->marker_fd); |
| } |
| free(ptp); |
| return -1; |
| } |
| |
| static int ptp_clock_sync_free(struct tracecmd_time_sync *tsync) |
| { |
| struct clock_sync_context *clock_context; |
| struct ptp_clock_sync *ptp; |
| |
| if (!tsync || !tsync->context) |
| return -1; |
| clock_context = (struct clock_sync_context *)tsync->context; |
| |
| if (clock_context && clock_context->proto_data) { |
| ptp = (struct ptp_clock_sync *)clock_context->proto_data; |
| tep_free(ptp->tep); |
| if (ptp->marker_fd >= 0) |
| close(ptp->marker_fd); |
| if (ptp->debug_fd >= 0) |
| close(ptp->debug_fd); |
| free(clock_context->proto_data); |
| clock_context->proto_data = NULL; |
| } |
| return 0; |
| } |
| |
| /* Save the timestamps of sent ('s') and returned ('r') probes in the |
| * ctx->msg.samples[] array. Depending of the context (server or client), there |
| * may be only returned probes, or both sent and returned probes. The returned |
| * probes are saved first in the array, after them are the sent probes. |
| * Depending of the context, the array can be with size: |
| * [0 .. max data.count] - holds only returned probes |
| * [0 .. 2 * max data.count] - holds both returned and sent probes |
| */ |
| static void ptp_probe_store(struct ptp_markers_context *ctx, |
| struct ptp_marker *marker, |
| unsigned long long ts) |
| { |
| int index = -1; |
| |
| if (marker->data.packet_id == 'r' && |
| marker->data.count <= ctx->size) { |
| index = marker->data.count - 1; |
| } else if (marker->data.packet_id == 's' && |
| marker->data.count * 2 <= ctx->size){ |
| index = ctx->size / 2 + marker->data.count - 1; |
| } |
| |
| if (index >= 0) { |
| ctx->msg.samples[index].id = marker->data.count; |
| ctx->msg.samples[index].ts = ts; |
| ctx->msg.count++; |
| } |
| } |
| |
| static int ptp_marker_find(struct tep_event *event, struct tep_record *record, |
| int cpu, void *context) |
| { |
| struct ptp_markers_context *ctx; |
| struct ptp_marker *marker; |
| |
| ctx = (struct ptp_markers_context *)context; |
| |
| /* Make sure this is our event */ |
| if (event->id != ctx->ptp->raw_id || !ctx->ptp->id) |
| return 0; |
| if (record->size >= (ctx->ptp->id->offset + sizeof(struct ptp_marker))) { |
| marker = (struct ptp_marker *)(record->data + ctx->ptp->id->offset); |
| if (marker->data.local_id == ctx->clock->local_id && |
| marker->data.remote_id == ctx->clock->remote_id && |
| marker->series_id == ctx->ptp->series_id && |
| marker->data.count) |
| ptp_probe_store(ctx, marker, record->ts); |
| } |
| |
| return 0; |
| } |
| |
| static inline bool good_probe(struct ptp_clock_sample *server_sample, |
| struct ptp_clock_sample *send_sample, |
| struct ptp_clock_sample *client_sample, |
| int *bad_probes) |
| { |
| if (server_sample->ts && send_sample->ts && client_sample->ts && |
| server_sample->id == send_sample->id && |
| server_sample->id == client_sample->id) |
| return true; |
| (*bad_probes)++; |
| return false; |
| } |
| |
| static int ptp_calc_offset_fastest(struct clock_sync_context *clock, |
| struct ptp_clock_result_msg *server, |
| struct ptp_clock_result_msg *client, |
| long long *offset_ret, long long *ts_ret, |
| int *bad_probes) |
| { |
| struct ptp_clock_sample *sample_send; |
| long long delta_min = LLONG_MAX; |
| long long offset = 0; |
| long long delta = 0; |
| long long ts = 0; |
| int max_i; |
| int i; |
| |
| *bad_probes = 0; |
| sample_send = server->samples + (server->count / 2); |
| max_i = server->count / 2 < client->count ? |
| server->count / 2 : client->count; |
| for (i = 0; i < max_i; i++) { |
| if (!good_probe(&server->samples[i], &sample_send[i], |
| &client->samples[i], bad_probes)) |
| continue; |
| ts = (sample_send[i].ts + server->samples[i].ts) / 2; |
| offset = client->samples[i].ts - ts; |
| |
| delta = server->samples[i].ts - sample_send[i].ts; |
| if (delta_min > delta) { |
| delta_min = delta; |
| *offset_ret = offset; |
| *ts_ret = ts; |
| } |
| #ifdef TSYNC_DEBUG |
| { |
| struct ptp_clock_sync *ptp; |
| |
| ptp = (struct ptp_clock_sync *)clock->proto_data; |
| if (ptp && ptp->debug_fd > 0) { |
| char buff[256]; |
| |
| sprintf(buff, "%lld %lld %lld\n", |
| ts, client->samples[i].ts, offset); |
| write(ptp->debug_fd, buff, strlen(buff)); |
| } |
| } |
| #endif |
| } |
| |
| return 0; |
| } |
| |
| static int ptp_calc_offset_hist(struct clock_sync_context *clock, |
| struct ptp_clock_result_msg *server, |
| struct ptp_clock_result_msg *client, |
| long long *offset_ret, long long *ts_ret, |
| int *bad_probes) |
| { |
| struct ptp_clock_sample *sample_send; |
| long long timestamps[PTP_SYNC_LOOP]; |
| long long offsets[PTP_SYNC_LOOP]; |
| long long offset_min = LLONG_MAX; |
| long long offset_max = 0; |
| int hist[PTP_SYNC_LOOP]; |
| int ind, max = 0; |
| long long bin; |
| int i, k = 0; |
| |
| *bad_probes = 0; |
| memset(hist, 0, sizeof(int) * PTP_SYNC_LOOP); |
| sample_send = server->samples + (server->count / 2); |
| for (i = 0; i * 2 < server->count && i < client->count; i++) { |
| if (!good_probe(&server->samples[i], &sample_send[i], |
| &client->samples[i], bad_probes)) |
| continue; |
| timestamps[k] = (sample_send[i].ts + server->samples[i].ts) / 2; |
| offsets[k] = client->samples[i].ts - timestamps[k]; |
| if (offset_max < llabs(offsets[k])) |
| offset_max = llabs(offsets[k]); |
| if (offset_min > llabs(offsets[k])) |
| offset_min = llabs(offsets[k]); |
| #ifdef TSYNC_DEBUG |
| { |
| struct ptp_clock_sync *ptp; |
| |
| ptp = (struct ptp_clock_sync *)clock->proto_data; |
| |
| if (ptp && ptp->debug_fd > 0) { |
| char buff[256]; |
| |
| sprintf(buff, "%lld %lld %lld\n", |
| timestamps[k], |
| client->samples[i].ts, offsets[k]); |
| write(ptp->debug_fd, buff, strlen(buff)); |
| } |
| } |
| #endif |
| k++; |
| } |
| |
| bin = (offset_max - offset_min) / PTP_SYNC_LOOP; |
| for (i = 0; i < k; i++) { |
| ind = (llabs(offsets[i]) - offset_min) / bin; |
| if (ind < PTP_SYNC_LOOP) { |
| hist[ind]++; |
| if (max < hist[ind]) { |
| max = hist[ind]; |
| *offset_ret = offsets[i]; |
| *ts_ret = timestamps[i]; |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| static void ntoh_ptp_results(struct ptp_clock_result_msg *msg) |
| { |
| int i; |
| |
| msg->count = ntohl(msg->count); |
| for (i = 0; i < msg->count; i++) { |
| msg->samples[i].id = ntohl(msg->samples[i].id); |
| msg->samples[i].ts = ntohll(msg->samples[i].ts); |
| } |
| msg->series_id = ntohl(msg->series_id); |
| } |
| |
| |
| static void hton_ptp_results(struct ptp_clock_result_msg *msg) |
| { |
| int i; |
| |
| for (i = 0; i < msg->count; i++) { |
| msg->samples[i].id = htonl(msg->samples[i].id); |
| msg->samples[i].ts = htonll(msg->samples[i].ts); |
| } |
| msg->series_id = htonl(msg->series_id); |
| msg->count = htonl(msg->count); |
| } |
| |
| static inline void ptp_track_clock(struct ptp_markers_context *ctx, |
| struct ptp_marker *marker) |
| { |
| if (ctx->ptp->flags & PTP_FLAG_USE_MARKER) { |
| write(ctx->ptp->marker_fd, marker, sizeof(struct ptp_marker)); |
| } else { |
| struct timespec clock; |
| unsigned long long ts; |
| |
| clock_gettime(CLOCK_MONOTONIC_RAW, &clock); |
| ts = clock.tv_sec * 1000000000LL; |
| ts += clock.tv_nsec; |
| ptp_probe_store(ctx, marker, ts); |
| } |
| } |
| |
| static int ptp_clock_client(struct tracecmd_time_sync *tsync, |
| long long *offset, long long *timestamp) |
| { |
| char sync_proto[TRACECMD_TSYNC_PNAME_LENGTH]; |
| struct clock_sync_context *clock_context; |
| struct ptp_clock_offset_msg res_offset; |
| struct ptp_clock_start_msg start; |
| struct ptp_markers_context ctx; |
| struct ptp_clock_sync *ptp; |
| struct ptp_marker marker; |
| unsigned int sync_msg; |
| unsigned int size; |
| char *msg; |
| int count; |
| int ret; |
| |
| if (!tsync || !tsync->context || !tsync->msg_handle) |
| return -1; |
| |
| clock_context = (struct clock_sync_context *)tsync->context; |
| if (clock_context->proto_data == NULL) |
| return -1; |
| |
| ptp = (struct ptp_clock_sync *)clock_context->proto_data; |
| size = sizeof(start); |
| msg = (char *)&start; |
| ret = tracecmd_msg_recv_time_sync(tsync->msg_handle, |
| sync_proto, &sync_msg, |
| &size, &msg); |
| if (ret || strncmp(sync_proto, PTP_NAME, TRACECMD_TSYNC_PNAME_LENGTH) || |
| sync_msg != PTP_SYNC_PKT_START) |
| return -1; |
| ret = tracecmd_msg_send_time_sync(tsync->msg_handle, PTP_NAME, |
| PTP_SYNC_PKT_START, sizeof(start), |
| (char *)&start); |
| marker.data.local_id = clock_context->local_id; |
| marker.data.remote_id = clock_context->remote_id; |
| marker.series_id = ntohl(start.series_id); |
| marker.data.packet_id = 'r'; |
| ptp->series_id = marker.series_id; |
| ptp->flags = ntohl(start.flags); |
| msg = (char *)&count; |
| size = sizeof(count); |
| ctx.msg.count = 0; |
| ctx.size = PTP_SYNC_LOOP; |
| ctx.ptp = ptp; |
| ctx.clock = clock_context; |
| ctx.msg.series_id = ptp->series_id; |
| while (true) { |
| count = 0; |
| ret = tracecmd_msg_recv_time_sync(tsync->msg_handle, |
| sync_proto, &sync_msg, |
| &size, &msg); |
| if (ret || strncmp(sync_proto, PTP_NAME, TRACECMD_TSYNC_PNAME_LENGTH) || |
| sync_msg != PTP_SYNC_PKT_PROBE || !ntohl(count)) |
| break; |
| marker.data.count = ntohl(count); |
| ptp_track_clock(&ctx, &marker); |
| ret = tracecmd_msg_send_time_sync(tsync->msg_handle, PTP_NAME, |
| PTP_SYNC_PKT_PROBE, |
| sizeof(count), (char *)&count); |
| if (ret) |
| break; |
| } |
| |
| if (strncmp(sync_proto, PTP_NAME, TRACECMD_TSYNC_PNAME_LENGTH) || |
| sync_msg != PTP_SYNC_PKT_END) |
| return -1; |
| |
| if (ptp->flags & PTP_FLAG_USE_MARKER) |
| tracefs_iterate_raw_events(ptp->tep, clock_context->instance, |
| NULL, 0, ptp_marker_find, &ctx); |
| |
| hton_ptp_results(&ctx.msg); |
| ret = tracecmd_msg_send_time_sync(tsync->msg_handle, PTP_NAME, |
| PTP_SYNC_PKT_PROBES, |
| sizeof(ctx.msg), (char *)&ctx.msg); |
| |
| msg = (char *)&res_offset; |
| size = sizeof(res_offset); |
| ret = tracecmd_msg_recv_time_sync(tsync->msg_handle, |
| sync_proto, &sync_msg, |
| &size, (char **)&msg); |
| if (ret || strncmp(sync_proto, PTP_NAME, TRACECMD_TSYNC_PNAME_LENGTH) || |
| sync_msg != PTP_SYNC_PKT_OFFSET) |
| return -1; |
| |
| *offset = ntohll(res_offset.offset); |
| *timestamp = ntohll(res_offset.ts); |
| |
| return 0; |
| } |
| |
| |
| static int ptp_clock_server(struct tracecmd_time_sync *tsync, |
| long long *offset, long long *timestamp) |
| { |
| char sync_proto[TRACECMD_TSYNC_PNAME_LENGTH]; |
| struct ptp_clock_result_msg *results = NULL; |
| struct clock_sync_context *clock_context; |
| struct ptp_clock_offset_msg res_offset; |
| struct ptp_clock_start_msg start; |
| struct ptp_markers_context ctx; |
| int sync_loop = PTP_SYNC_LOOP; |
| struct ptp_clock_sync *ptp; |
| struct ptp_marker marker; |
| unsigned int sync_msg; |
| unsigned int size; |
| int bad_probes; |
| int count = 1; |
| int msg_count; |
| int msg_ret; |
| char *msg; |
| int ret; |
| |
| if (!tsync || !tsync->context || !tsync->msg_handle) |
| return -1; |
| |
| clock_context = (struct clock_sync_context *)tsync->context; |
| if (clock_context->proto_data == NULL) |
| return -1; |
| |
| ptp = (struct ptp_clock_sync *)clock_context->proto_data; |
| ptp->flags = ptp_flags; |
| memset(&start, 0, sizeof(start)); |
| start.series_id = htonl(ptp->series_id + 1); |
| start.flags = htonl(ptp->flags); |
| ret = tracecmd_msg_send_time_sync(tsync->msg_handle, PTP_NAME, |
| PTP_SYNC_PKT_START, sizeof(start), |
| (char *)&start); |
| if (!ret) |
| ret = tracecmd_msg_recv_time_sync(tsync->msg_handle, |
| sync_proto, &sync_msg, |
| NULL, NULL); |
| if (ret || strncmp(sync_proto, PTP_NAME, TRACECMD_TSYNC_PNAME_LENGTH) || |
| sync_msg != PTP_SYNC_PKT_START) |
| return -1; |
| |
| tracefs_instance_file_write(clock_context->instance, "trace", "\0"); |
| |
| ptp->series_id++; |
| marker.data.local_id = clock_context->local_id; |
| marker.data.remote_id = clock_context->remote_id; |
| marker.series_id = ptp->series_id; |
| msg = (char *)&msg_ret; |
| size = sizeof(msg_ret); |
| ctx.size = 2*PTP_SYNC_LOOP; |
| ctx.ptp = ptp; |
| ctx.clock = clock_context; |
| ctx.msg.count = 0; |
| ctx.msg.series_id = ptp->series_id; |
| do { |
| marker.data.count = count++; |
| marker.data.packet_id = 's'; |
| msg_count = htonl(marker.data.count); |
| ptp_track_clock(&ctx, &marker); |
| ret = tracecmd_msg_send_time_sync(tsync->msg_handle, PTP_NAME, |
| PTP_SYNC_PKT_PROBE, |
| sizeof(msg_count), |
| (char *)&msg_count); |
| if (!ret) |
| ret = tracecmd_msg_recv_time_sync(tsync->msg_handle, |
| sync_proto, &sync_msg, |
| &size, &msg); |
| |
| marker.data.packet_id = 'r'; |
| ptp_track_clock(&ctx, &marker); |
| if (ret || strncmp(sync_proto, PTP_NAME, TRACECMD_TSYNC_PNAME_LENGTH) || |
| sync_msg != PTP_SYNC_PKT_PROBE || |
| ntohl(msg_ret) != marker.data.count) |
| break; |
| } while (--sync_loop); |
| |
| if (sync_loop) |
| return -1; |
| |
| ret = tracecmd_msg_send_time_sync(tsync->msg_handle, PTP_NAME, |
| PTP_SYNC_PKT_END, 0, NULL); |
| |
| size = 0; |
| ret = tracecmd_msg_recv_time_sync(tsync->msg_handle, |
| sync_proto, &sync_msg, |
| &size, (char **)&results); |
| if (ret || strncmp(sync_proto, PTP_NAME, TRACECMD_TSYNC_PNAME_LENGTH) || |
| sync_msg != PTP_SYNC_PKT_PROBES || size == 0 || results == NULL) |
| return -1; |
| |
| ntoh_ptp_results(results); |
| if (ptp->flags & PTP_FLAG_USE_MARKER) |
| tracefs_iterate_raw_events(ptp->tep, clock_context->instance, |
| NULL, 0, ptp_marker_find, &ctx); |
| if (ptp->flags & PTP_FLAG_FASTEST_RESPONSE) |
| ptp_calc_offset_fastest(clock_context, &ctx.msg, results, offset, |
| timestamp, &bad_probes); |
| else |
| ptp_calc_offset_hist(clock_context, &ctx.msg, results, offset, |
| timestamp, &bad_probes); |
| #ifdef TSYNC_DEBUG |
| { |
| char buff[256]; |
| int res_fd; |
| |
| sprintf(buff, "res-id%d.txt", clock_context->remote_id); |
| |
| res_fd = open(buff, O_WRONLY|O_APPEND, 0644); |
| if (res_fd > 0) { |
| if (*offset && *timestamp) { |
| sprintf(buff, "%d %lld %lld\n", |
| ptp->series_id, *offset, *timestamp); |
| write(res_fd, buff, strlen(buff)); |
| } |
| close(res_fd); |
| } |
| |
| printf("\n calculated offset %d: %lld, %d probes, filtered out %d, PTP flags 0x%X\n\r", |
| ptp->series_id, *offset, results->count, bad_probes, ptp->flags); |
| if (ptp && ptp->debug_fd > 0) { |
| sprintf(buff, "%lld %lld 0\n", *offset, *timestamp); |
| write(ptp->debug_fd, buff, strlen(buff)); |
| close(ptp->debug_fd); |
| ptp->debug_fd = -1; |
| } |
| |
| } |
| #endif |
| |
| res_offset.offset = htonll(*offset); |
| res_offset.ts = htonll(*timestamp); |
| ret = tracecmd_msg_send_time_sync(tsync->msg_handle, PTP_NAME, |
| PTP_SYNC_PKT_OFFSET, |
| sizeof(res_offset), |
| (char *)&res_offset); |
| |
| free(results); |
| return 0; |
| } |
| |
| static int ptp_clock_sync_calc(struct tracecmd_time_sync *tsync, |
| long long *offset, long long *scaling, long long *frac, |
| long long *timestamp, unsigned int cpu) |
| { |
| struct clock_sync_context *clock_context; |
| int ret; |
| |
| if (!tsync || !tsync->context) |
| return -1; |
| clock_context = (struct clock_sync_context *)tsync->context; |
| |
| #ifdef TSYNC_DEBUG |
| if (clock_context->is_server) { |
| struct ptp_clock_sync *ptp; |
| char buff[256]; |
| |
| ptp = (struct ptp_clock_sync *)clock_context->proto_data; |
| if (ptp->debug_fd > 0) |
| close(ptp->debug_fd); |
| sprintf(buff, "s-id%d_%d.txt", |
| clock_context->remote_id, ptp->series_id+1); |
| ptp->debug_fd = open(buff, O_CREAT|O_WRONLY|O_TRUNC, 0644); |
| } |
| #endif |
| |
| if (scaling) |
| *scaling = 1; |
| if (frac) |
| *frac = 0; |
| if (clock_context->is_server) |
| ret = ptp_clock_server(tsync, offset, timestamp); |
| else |
| ret = ptp_clock_client(tsync, offset, timestamp); |
| |
| return ret; |
| } |
| |
| int ptp_clock_sync_register(void) |
| { |
| return tracecmd_tsync_proto_register(PTP_NAME, PTP_ACCURACY, |
| TRACECMD_TIME_SYNC_ROLE_GUEST | |
| TRACECMD_TIME_SYNC_ROLE_HOST | |
| TRACECMD_TIME_SYNC_ROLE_CLIENT | |
| TRACECMD_TIME_SYNC_ROLE_SERVER, |
| 0, TRACECMD_TSYNC_FLAG_INTERPOLATE, |
| ptp_clock_sync_init, |
| ptp_clock_sync_free, |
| ptp_clock_sync_calc); |
| |
| } |
| |
| int ptp_clock_sync_unregister(void) |
| { |
| return tracecmd_tsync_proto_unregister(PTP_NAME); |
| } |