On Mon, Oct 03, 2016 at 09:08:55PM +0200, Richard Cochran wrote: > On Thu, Sep 15, 2016 at 01:23:20PM +0100, Kieran Tyrrell wrote: > > Signed-off-by: Kieran Tyrrell <kie...@sienda.com> > > Overall, this is looking much better. I will come back to this with > some comments, but I would like to run some tests first, hopefully > this week or next.
Once again, thanks for providing the proof-of-concept. The patch has some technical issues, and those would be fixable. However, a little while ago I ran some tests to see how well this works, and the result convinced me that the performance of the PHC timers doesn't justify the implementation effort. Here is what I did. I started with Linux 4.1.33-rt38 and backported your patch. In addition, I hacked the SDPs on the i210 to show up as gpios under /sys. Then I used a simple program (see below) running at RT priority to toggle a gpio every 10 milliseconds according to the PTP time. This gpio output was time stamped on an input to the PTP master. During the test, I ran hackbench to simulate a load on the host. I ran ptp4l on the i210 host and synchronized to an external master connected via a crossover cable. (Prior to running this test, I verified the host was synchronized to within 200 nanoseconds using the PPS output.) I used phc2sys to synchronize the Linux system time to the i210 PHC. I ran the test program in two ways. 1. using nanosleep(CLOCK_REALTIME) 2. using the PHC timer provided by your patch. The resulting graphic shows the offsets from the 100 PPS on the gpio over a ten minute test. http://linuxptp.sourceforge.net/phc-timer/phc-timer-vs-nanosleep.png These are the statistics. | | nanosleep | PHC Timer | |--------+---------------+---------------| | min | +8.933000e+03 | +2.080500e+04 | | max | +2.962100e+04 | +4.319700e+04 | | pk-pk | +2.068800e+04 | +2.239200e+04 | | mean | +1.517014e+04 | +2.845244e+04 | | stddev | +2.449972e+03 | +3.137972e+03 | Both results display jitter that is to be expected using a program running under RT Linux. What is really interesting is the average offset. There is an addition 13 microsecond delay when using the PHC timer on the i210, but why? Remember that reading the PCIe bus costs microseconds for each operation. The additional overhead of accessing the card over PCIe completely dwarfs any additional time accuracy. So the conclusion is, using PHC + phc2sys + nanosleep provides the best performance that we can offer under Linux for globally synchronized distributed applications. Thanks, Richard --- /* * This program tests synchronization to a global time source * in a distributed system. * * Portions taken from the sgd test program. * Copyright (C) 2015 linutronix GmbH * * Portions taken from linux/Documentation/ptp/testptp.c. * Copyright (C) 2010 OMICRON electronics GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #define _GNU_SOURCE /*for CPU_SET*/ #include <errno.h> #include <fcntl.h> #include <pthread.h> #include <sched.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #define PERIOD_SEC 0 #define PERIOD_NSEC 10000000 static int gpio_fd = -1, running = 1, toggle; static clockid_t get_clockid(int fd) { #define CLOCKFD 3 #define FD_TO_CLOCKID(fd) ((~(clockid_t) (fd) << 3) | CLOCKFD) return FD_TO_CLOCKID(fd); } static void gpio_pulse(void) { char c; c = '1'; write(gpio_fd, &c, sizeof(c)); c = '0'; write(gpio_fd, &c, sizeof(c)); } static void gpio_toggle(void) { static int even; char c; if (even) { c = '1'; write(gpio_fd, &c, sizeof(c)); } else { c = '0'; write(gpio_fd, &c, sizeof(c)); } even = 1 - even; } static void handle_alarm(int s) { if (toggle) gpio_toggle(); else gpio_pulse(); } static int install_handler(int signum, void (*handler)(int)) { struct sigaction action; sigset_t mask; /* Unblock the signal. */ sigemptyset(&mask); sigaddset(&mask, signum); sigprocmask(SIG_UNBLOCK, &mask, NULL); /* Install the signal handler. */ action.sa_handler = handler; action.sa_flags = 0; sigemptyset(&action.sa_mask); sigaction(signum, &action, NULL); return 0; } static int run_nanosleep(clockid_t clkid) { struct timespec ts; int err; clock_gettime(clkid, &ts); ts.tv_sec += 2; ts.tv_nsec = 0; while (running) { err = clock_nanosleep(clkid, TIMER_ABSTIME, &ts, NULL); switch (err) { case 0: if (toggle) { gpio_toggle(); } else { gpio_pulse(); } ts.tv_sec += PERIOD_SEC; ts.tv_nsec += PERIOD_NSEC; while (ts.tv_nsec > 999999999) { ts.tv_sec += 1; ts.tv_nsec -= 1000000000; } break; case EINTR: continue; default: fprintf(stderr, "clock_nanosleep returned %d: %s", err, strerror(err)); } } return 0; } static int run_timer(clockid_t clkid) { static timer_t timerid; struct itimerspec timeout; struct sigevent sigevent; install_handler(SIGALRM, handle_alarm); /* Create a timer. */ sigevent.sigev_notify = SIGEV_SIGNAL; sigevent.sigev_signo = SIGALRM; if (timer_create(clkid, &sigevent, &timerid)) { perror("timer_create"); return -1; } /* Start the timer. */ memset(&timeout, 0, sizeof(timeout)); timeout.it_interval.tv_sec = PERIOD_SEC; timeout.it_interval.tv_nsec = PERIOD_NSEC; clock_gettime(clkid, &timeout.it_value); timeout.it_value.tv_sec += 2; timeout.it_value.tv_nsec = 0; if (timer_settime(timerid, TIMER_ABSTIME, &timeout, NULL)) { perror("timer_settime"); return -1; } while (running) { pause(); } timer_delete(timerid); return 0; } static int set_realtime(pthread_t thread, int priority, int cpu) { cpu_set_t cpuset; struct sched_param sp; int err, policy; int min = sched_get_priority_min(SCHED_FIFO); int max = sched_get_priority_max(SCHED_FIFO); fprintf(stderr, "min %d max %d\n", min, max); if (priority < 0) { return 0; } err = pthread_getschedparam(thread, &policy, &sp); if (err) { fprintf(stderr, "pthread_getschedparam: %s\n", strerror(err)); return -1; } sp.sched_priority = priority; err = pthread_setschedparam(thread, SCHED_FIFO, &sp); if (err) { fprintf(stderr, "pthread_setschedparam: %s\n", strerror(err)); return -1; } if (cpu < 0) { return 0; } CPU_ZERO(&cpuset); CPU_SET(cpu, &cpuset); err = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset); if (err) { fprintf(stderr, "pthread_setaffinity_np: %s\n", strerror(err)); return -1; } return 0; } static void usage(char *progname) { fprintf(stderr, "\n" "usage: %s [options]\n" "\n" " -c [num] run on CPU 'num'\n" " -d [file] open device 'file'\n" " omit to use CLOCK_REALTIME instead\n" " -h prints this message and exits\n" " -n use clock_nanosleep() instead of timer_settime()\n" " -o [file] write to gpio 'file'\n" " -p [num] run with RT priorty 'num'\n" " -t toggle instead of making a pulse\n" "\n", progname); } int main(int argc, char *argv[]) { int c, cpu = -1, fd, err, nanosleep = 0, priority = -1; char *device = NULL, *gpio = NULL, *progname; clockid_t clkid; /* Process the command line arguments. */ progname = strrchr(argv[0], '/'); progname = progname ? 1 + progname : argv[0]; while (EOF != (c = getopt(argc, argv, "c:d:hno:p:t"))) { switch (c) { case 'c': cpu = atoi(optarg); break; case 'd': device = optarg; break; case 'h': usage(progname); return 0; case 'n': nanosleep = 1; break; case 'o': gpio = optarg; break; case 'p': priority = atoi(optarg); break; case 't': toggle = 1; break; case '?': usage(progname); return -1; } } if (!gpio) { usage(progname); return -1; } if (set_realtime(pthread_self(), priority, cpu)) { return -1; } gpio_fd = open(gpio, O_RDWR); if (gpio_fd < 0) { fprintf(stderr, "cannot open %s: %m\n", gpio); return -1; } if (device) { fd = open(device, O_RDWR); if (fd < 0) { fprintf(stderr, "cannot open %s: %m\n", device); return -1; } clkid = get_clockid(fd); } else { clkid = CLOCK_REALTIME; } if (nanosleep) { err = run_nanosleep(clkid); } else { err = run_timer(clkid); } if (device) { close(fd); } close(gpio_fd); return err; } ------------------------------------------------------------------------------ Developer Access Program for Intel Xeon Phi Processors Access to Intel Xeon Phi processor-based developer platforms. With one year of Intel Parallel Studio XE. Training and support from Colfax. Order your platform today.http://sdm.link/xeonphi _______________________________________________ Linuxptp-devel mailing list Linuxptp-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linuxptp-devel