Add "use_vclocks" option to enable synchronization with virtual clocks. This enables multiple ptp4l instances sharing an interface to use hardware timestamping. By default, vclocks are enabled if running on Linux 5.18 or later, which should have all features to make them work as well as physical clocks.
When preparing the script, count how many vclocks are needed for each physical clock. Add a placeholder option in the form of "--phc_index %PHC0-0%" to the generated ptp4l commands that need hardware clock. The index of the virtual clock is unknown at this point. When running the script (not just printing), create the required number of virtual clocks by writing to /sys/.../n_vclocks and fix the --phc_index options to refer to the indices of the created vclocks. On exit, remove the virtual clocks to restore the original state. Signed-off-by: Miroslav Lichvar <mlich...@redhat.com> --- timemaster.8 | 14 +++- timemaster.c | 196 ++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 199 insertions(+), 11 deletions(-) diff --git a/timemaster.8 b/timemaster.8 index a11f4dd..2891a97 100644 --- a/timemaster.8 +++ b/timemaster.8 @@ -97,6 +97,15 @@ configuration error). If a process was terminated and is not started again, \fBtimemaster\fR will kill the other processes and exit with a non-zero status. The default value is 1 (enabled). +.TP +.B use_vclocks +Enable or disable synchronization with virtual clocks. If enabled, +\fBtimemaster\fR will create virtual clocks running on top of physical clocks +needed by configured PTP domains. This enables hardware time stamping for +multiple \fBptp4l\fR instances using the same network interface. The default +value is -1, which enables the virtual clocks if running on Linux 5.18 or +later. + .SS [ntp_server address] The \fBntp_server\fR section specifies an NTP server that should be used as a @@ -140,8 +149,8 @@ Specify which network interfaces should be used for this PTP domain. A separate \fBptp4l\fR instance will be started for each group of interfaces sharing the same PHC and for each interface that supports only SW time stamping. HW time stamping is enabled automatically. If an interface with HW time stamping is -specified also in other PTP domains, only the \fBptp4l\fR instance from the -first PTP domain will be using HW time stamping. +specified also in other PTP domains and virtual clocks are disabled, only the +\fBptp4l\fR instance from the first PTP domain will be using HW time stamping. .TP .B ntp_poll @@ -333,6 +342,7 @@ ntp_program chronyd rundir /var/run/timemaster first_shm_segment 1 restart_processes 0 +use_vclocks 0 [chronyd] path /usr/sbin/chronyd diff --git a/timemaster.c b/timemaster.c index 616a99a..cafd3a3 100644 --- a/timemaster.c +++ b/timemaster.c @@ -20,6 +20,7 @@ #include <ctype.h> #include <errno.h> +#include <glob.h> #include <libgen.h> #include <limits.h> #include <time.h> @@ -33,6 +34,7 @@ #include <string.h> #include <sys/stat.h> #include <sys/types.h> +#include <sys/utsname.h> #include <sys/wait.h> #include <unistd.h> @@ -46,6 +48,7 @@ #define DEFAULT_FIRST_SHM_SEGMENT 0 #define DEFAULT_RESTART_PROCESSES 1 +#define DEFAULT_USE_VCLOCKS -1 #define DEFAULT_NTP_PROGRAM CHRONYD #define DEFAULT_NTP_MINPOLL 6 @@ -111,6 +114,7 @@ struct timemaster_config { char *rundir; int first_shm_segment; int restart_processes; + int use_vclocks; struct program_config chronyd; struct program_config ntpd; struct program_config phc2sys; @@ -122,7 +126,13 @@ struct config_file { char *content; }; +struct phc_vclocks { + int pclock_index; + int vclocks; +}; + struct script { + struct phc_vclocks **vclocks; struct config_file **configs; char ***commands; int **command_groups; @@ -393,6 +403,8 @@ static int parse_timemaster_settings(char **settings, r = parse_int(value, &config->first_shm_segment); } else if (!strcasecmp(name, "restart_processes")) { r = parse_int(value, &config->restart_processes); + } else if (!strcasecmp(name, "use_vclocks")) { + r = parse_int(value, &config->use_vclocks); } else { pr_err("unknown timemaster setting %s", name); return 1; @@ -520,6 +532,20 @@ static void config_destroy(struct timemaster_config *config) free(config); } +static int check_kernel_version(int version, int patch) +{ + struct utsname uts; + int v, p; + + if (uname(&uts) < 0) + return 1; + if (sscanf(uts.release, "%d.%d", &v, &p) < 2) + return 1; + if (version > v || (version == v && patch > p)) + return 1; + return 0; +} + static struct timemaster_config *config_parse(char *path) { struct timemaster_config *config = xcalloc(1, sizeof(*config)); @@ -533,6 +559,7 @@ static struct timemaster_config *config_parse(char *path) config->rundir = xstrdup(DEFAULT_RUNDIR); config->first_shm_segment = DEFAULT_FIRST_SHM_SEGMENT; config->restart_processes = DEFAULT_RESTART_PROCESSES; + config->use_vclocks = DEFAULT_USE_VCLOCKS; init_program_config(&config->chronyd, "chronyd", NULL, DEFAULT_CHRONYD_SETTINGS, NULL); @@ -593,6 +620,9 @@ static struct timemaster_config *config_parse(char *path) fclose(f); + if (config->use_vclocks < 0) + config->use_vclocks = !check_kernel_version(5, 18); + if (section_name) free(section_name); if (section_lines) @@ -608,7 +638,7 @@ static struct timemaster_config *config_parse(char *path) static char **get_ptp4l_command(struct program_config *config, struct config_file *file, char **interfaces, - int hw_ts) + char *phc_index, int hw_ts) { char **command = (char **)parray_new(); @@ -617,6 +647,9 @@ static char **get_ptp4l_command(struct program_config *config, parray_extend((void ***)&command, xstrdup("-f"), xstrdup(file->path), xstrdup(hw_ts ? "-H" : "-S"), NULL); + if (phc_index && phc_index[0]) + parray_extend((void ***)&command, + xstrdup("--phc_index"), xstrdup(phc_index), NULL); for (; *interfaces; interfaces++) parray_extend((void ***)&command, @@ -706,6 +739,24 @@ static int add_ntp_source(struct ntp_server *source, char **ntp_config) return 0; } +static int add_vclock(struct script *script, int pclock_index) +{ + struct phc_vclocks **vclocks, *v; + + for (vclocks = script->vclocks; *vclocks; vclocks++) { + if ((*vclocks)->pclock_index != pclock_index) + continue; + return (*vclocks)->vclocks++; + } + + v = xmalloc(sizeof(*v)); + v->pclock_index = pclock_index; + v->vclocks = 1; + parray_append((void ***)&script->vclocks, v); + + return 0; +} + static int add_ptp_source(struct ptp_domain *source, struct timemaster_config *config, int *shm_segment, int *command_group, int ***allocated_phcs, @@ -713,7 +764,7 @@ static int add_ptp_source(struct ptp_domain *source, { struct config_file *config_file; char **command, *uds_path, *uds_path2, **interfaces, *message_tag; - char ts_interface[IF_NAMESIZE]; + char ts_interface[IF_NAMESIZE], vclock_index[20]; int i, j, num_interfaces, *phc, *phcs, hw_ts, sw_ts; struct sk_ts_info ts_info; @@ -801,10 +852,18 @@ static int add_ptp_source(struct ptp_domain *source, } } - /* don't use this PHC in other sources */ - phc = xmalloc(sizeof(int)); - *phc = phcs[i]; - parray_append((void ***)allocated_phcs, phc); + if (config->use_vclocks) { + /* request new vclock for the PHC */ + int vclock = add_vclock(script, phcs[i]); + snprintf(vclock_index, sizeof(vclock_index), + "%%PHC%d-%d%%", phcs[i], vclock); + } else { + /* don't use this PHC in other sources */ + phc = xmalloc(sizeof(int)); + *phc = phcs[i]; + parray_append((void ***)allocated_phcs, phc); + vclock_index[0] = '\0'; + } } uds_path = string_newf("%s/ptp4l.%d.socket", @@ -842,7 +901,8 @@ static int add_ptp_source(struct ptp_domain *source, if (phcs[i] >= 0) { /* HW time stamping */ command = get_ptp4l_command(&config->ptp4l, config_file, - interfaces, 1); + interfaces, + vclock_index, 1); add_command(command, *command_group, script); command = get_phc2sys_command(&config->phc2sys, @@ -854,7 +914,7 @@ static int add_ptp_source(struct ptp_domain *source, } else { /* SW time stamping */ command = get_ptp4l_command(&config->ptp4l, config_file, - interfaces, 0); + interfaces, NULL, 0); add_command(command, (*command_group)++, script); string_appendf(&config_file->content, @@ -943,6 +1003,11 @@ static void script_destroy(struct script *script) char ***commands, **command; int **groups; struct config_file *config, **configs; + struct phc_vclocks **vclocks; + + for (vclocks = script->vclocks; *vclocks; vclocks++) + free(*vclocks); + free(script->vclocks); for (configs = script->configs; *configs; configs++) { config = *configs; @@ -974,6 +1039,7 @@ static struct script *script_create(struct timemaster_config *config) int **allocated_phcs = (int **)parray_new(); int ret = 0, shm_segment, command_group = 0; + script->vclocks = (struct phc_vclocks **)parray_new(); script->configs = (struct config_file **)parray_new(); script->commands = (char ***)parray_new(); script->command_groups = (int **)parray_new(); @@ -1116,6 +1182,102 @@ static int remove_config_files(struct config_file **configs) return 0; } +static int set_phc_n_vclocks(int phc_index, int n_vclocks) +{ + char path[PATH_MAX]; + FILE *f; + + snprintf(path, sizeof(path), "/sys/class/ptp/ptp%d/n_vclocks", + phc_index); + f = fopen(path, "w"); + if (!f) { + pr_err("failed to open %s: %m", path); + return 1; + } + fprintf(f, "%d\n", n_vclocks); + fclose(f); + + return 0; +} + +static int create_vclocks(struct phc_vclocks **phc_vclocks) +{ + struct phc_vclocks **vclocks; + + for (vclocks = phc_vclocks; *vclocks; vclocks++) { + if (set_phc_n_vclocks((*vclocks)->pclock_index, + (*vclocks)->vclocks)) + return 1; + } + + return 0; +} + +static int remove_vclocks(struct phc_vclocks **phc_vclocks) +{ + struct phc_vclocks **vclocks; + + for (vclocks = phc_vclocks; *vclocks; vclocks++) { + if (set_phc_n_vclocks((*vclocks)->pclock_index, 0)) + return 1; + } + + return 0; +} + +static int get_vclock_index(int pindex, int vclock) +{ + char pattern[PATH_MAX], *s; + int n, vindex; + glob_t gl; + + snprintf(pattern, sizeof(pattern), "/sys/class/ptp/ptp%d/ptp[0-9]*", + pindex); + + if (glob(pattern, 0, NULL, &gl)) { + pr_err("glob(%s) failed", pattern); + return -1; + } + + if (vclock >= gl.gl_pathc || + !(s = strrchr(gl.gl_pathv[vclock], '/')) || + sscanf(s + 1, "ptp%d%n", &vindex, &n) != 1 || + n != strlen(s + 1)) { + pr_err("missing vclock %d:%d", pindex, vclock); + globfree(&gl); + return -1; + } + + globfree(&gl); + + return vindex; +} + +static int translate_vclock_options(char ***commands) +{ + int n, pindex, vclock, vindex, blen; + char **command; + + for (; *commands; commands++) { + for (command = *commands; *command; command++) { + if (sscanf(*command, "%%PHC%d-%d%%%n", + &pindex, &vclock, &n) != 2 || + n != strlen(*command)) + continue; + vindex = get_vclock_index(pindex, vclock); + if (vindex < 0) + return 1; + + /* overwrite the string with the vclock PHC index */ + blen = strlen(*command) + 1; + if (snprintf(*command, blen, "%d", vindex) >= blen) + return 1; + } + } + + return 0; +} + static int script_run(struct script *script) { struct timespec ts_start, ts_now; @@ -1135,6 +1297,12 @@ static int script_run(struct script *script) if (create_config_files(script->configs)) return 1; + if (create_vclocks(script->vclocks)) + return 1; + + if (translate_vclock_options(script->commands)) + return 1; + sigemptyset(&mask); sigaddset(&mask, SIGCHLD); sigaddset(&mask, SIGTERM); @@ -1278,6 +1446,9 @@ static int script_run(struct script *script) free(pids); + if (remove_vclocks(script->vclocks)) + return 1; + if (remove_config_files(script->configs)) return 1; @@ -1289,13 +1460,20 @@ static void script_print(struct script *script) char ***commands, **command; int **groups; struct config_file *config, **configs; + struct phc_vclocks **vclocks; for (configs = script->configs; *configs; configs++) { config = *configs; fprintf(stderr, "%s:\n\n%s\n", config->path, config->content); } - fprintf(stderr, "commands:\n\n"); + fprintf(stderr, "virtual clocks:\n\n"); + for (vclocks = script->vclocks; *vclocks; vclocks++) { + fprintf(stderr, "PHC%d: %d\n", + (*vclocks)->pclock_index, (*vclocks)->vclocks); + } + + fprintf(stderr, "\ncommands:\n\n"); for (commands = script->commands, groups = script->command_groups; *commands; commands++, groups++) { fprintf(stderr, "[%d] ", **groups); -- 2.35.1 _______________________________________________ Linuxptp-devel mailing list Linuxptp-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linuxptp-devel