Commit 08bfdac7 authored by Joanne Hugé's avatar Joanne Hugé

Add test-board-synchro

Clean up client and add comments
add send_tx_data option
Clean up server and add emit signal option
Delete line breaks between static variables
Update ptp script
Add run-phc2sys script
Add gettime program
Add test-board-synchro script
parent 28675252
PROG = ptptime
SRCDIR = ../src
SRCS = main.c
OBJS = $(SRCS:%.c=%.o)
ifeq ($(DEBUG),)
CFLAGS = -O2
else
CFLAGS = -Og -g -Wall -Wextra
endif
CFLAGS += -MD -MP
CFLAGS += -I $(SRCDIR)
CFLAGS += -std=gnu99
LLIBS = -pthread
vpath %.c $(SRCDIR)
$(PROG): $(OBJS)
$(CC) $(LDFLAGS) $(LDIRS) $^ $(LLIBS) -o $@
-include $(subst .c,.d,$(SRCS))
clean:
$(RM) $(OBJS) $(PROG) $(subst .c,.d,$(SRCS))
.PHONY: clean
/*
* Get PTP time
*
* Large portions taken from cyclictest
*
*/
#define _GNU_SOURCE
#include <errno.h>
#include <error.h>
#include <inttypes.h>
#include <limits.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#define NSEC_PER_SEC UINT64_C(1000000000)
// Structs
typedef struct thread_param {
int interval;
int priority;
} thread_param_t;
// Static functions
static void process_options(int argc, char *argv[]);
static void add_ns(struct timespec *t, uint64_t ns);
static uint64_t ts_to_uint(struct timespec t);
// Static variables
static thread_param_t thread_params;
static uint64_t ptptime;
static int quit;
static clockid_t clock_id;
static void help(char *argv[]) {
printf(
"Usage: %s [-h] [-i USEC]\n"
" -h Show help\n"
" -m Get time from CLOCK_MONOTONIC\n"
" -r Get time from CLOCK_REALTIME (default)\n"
" -t Get time from CLOCK_TAI\n"
" -q Get time then quit\n"
" -i USEC RT thread wake-up interval (default: 10ms)\n"
"\n",
argv[0]);
}
/*
* Real-time thread: Sends packets at a regular intervall
*/
static void *packet_sending_thread(void *p) {
(void)p;
struct timespec next, ptptime_ts;
cpu_set_t mask;
// Set thread CPU affinity
CPU_ZERO(&mask);
CPU_SET(1, &mask);
if (sched_setaffinity(0, sizeof(mask), &mask))
error(EXIT_FAILURE, errno, "Could not set CPU affinity to CPU #1\n");
clock_gettime(CLOCK_MONOTONIC, &next);
// Packet sending loop
for (;;) {
clock_gettime(clock_id, &ptptime_ts);
ptptime = ts_to_uint(ptptime_ts);
add_ns(&next, thread_params.interval);
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next, NULL);
}
return NULL;
}
/*
* Main thread, has non-real time priority
* Handles the IO and creates the real time thread
*/
int main(int argc, char *argv[]) {
pthread_t thread;
struct sched_param param;
pthread_attr_t attr;
// Default configuration values
thread_params.interval = 100000 * 1000;
thread_params.priority = 99;
clock_id = CLOCK_REALTIME;
/* Lock all current and future pages from preventing of being paged to
* swap */
if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
perror("mlockall failed");
/* exit(-1) or do error handling */
}
// Process bash options
process_options(argc, argv);
if (quit) {
struct timespec ts;
clock_gettime(clock_id, &ts);
printf("%" PRIu64 "\n", ts_to_uint(ts));
exit(EXIT_SUCCESS);
}
/* Initialize pthread attributes (default values) */
if (pthread_attr_init(&attr)) {
fprintf(stderr, "init pthread attributes failed\n");
exit(EXIT_FAILURE);
}
/* Set a specific stack size */
if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN)) {
fprintf(stderr, "pthread setstacksize failed\n");
exit(EXIT_FAILURE);
}
/* Set scheduler policy and priority of pthread */
if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO)) {
fprintf(stderr, "pthread setschedpolicy failed\n");
exit(EXIT_FAILURE);
}
param.sched_priority = thread_params.priority;
if (pthread_attr_setschedparam(&attr, &param)) {
fprintf(stderr, "pthread setschedparam failed\n");
exit(EXIT_FAILURE);
}
/* Use scheduling parameters of attr */
if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)) {
fprintf(stderr, "pthread setinheritsched failed\n");
exit(EXIT_FAILURE);
}
// Create the real time thread
if (pthread_create(&thread, &attr, packet_sending_thread, NULL))
error(EXIT_FAILURE, errno, "Couldn't create packet sending thread");
// Verbose loop
for (;;) {
usleep(100000);
printf("%9" PRIu64 ": \n", ptptime);
printf("\033[%dA", 1);
}
exit(EXIT_SUCCESS);
}
// Process bash options
static void process_options(int argc, char *argv[]) {
for (;;) {
int c = getopt(argc, argv, "hi:qmrt");
if (c == -1) break;
switch (c) {
case 'm':
clock_id = CLOCK_MONOTONIC;
break;
case 'r':
clock_id = CLOCK_REALTIME;
break;
case 't':
clock_id = CLOCK_TAI;
break;
case 'h':
help(argv);
exit(EXIT_SUCCESS);
break;
case 'i':
thread_params.interval = atoi(optarg) * 1000;
break;
case 'q':
quit = 1;
break;
}
}
}
static void add_ns(struct timespec *t, uint64_t ns) {
t->tv_nsec += ns;
while ((unsigned int)t->tv_nsec >= NSEC_PER_SEC) {
t->tv_sec += 1;
t->tv_nsec -= NSEC_PER_SEC;
}
}
static uint64_t ts_to_uint(struct timespec t) {
return t.tv_sec * NSEC_PER_SEC + t.tv_nsec;
}
...@@ -7,6 +7,7 @@ SERVER_SRCS += recv_packet.c ...@@ -7,6 +7,7 @@ SERVER_SRCS += recv_packet.c
SERVER_SRCS += send_packet.c SERVER_SRCS += send_packet.c
SERVER_SRCS += common.c SERVER_SRCS += common.c
SERVER_SRCS += tracer.c SERVER_SRCS += tracer.c
SERVER_SRCS += gpio.c
CLIENT_SRCS = client.c CLIENT_SRCS = client.c
CLIENT_SRCS += recv_packet.c CLIENT_SRCS += recv_packet.c
......
...@@ -30,25 +30,28 @@ ...@@ -30,25 +30,28 @@
enum TSNTask { SEND_PACKET_TASK, RTT_TASK }; enum TSNTask { SEND_PACKET_TASK, RTT_TASK };
typedef struct rtt_stat { typedef struct rtt_stat {
int min_rtt; int min_rtt;
int avg_rtt; int avg_rtt;
int max_rtt; int max_rtt;
} rtt_stat_t; } rtt_stat_t;
typedef struct thread_param { typedef struct thread_param {
int interval; int interval;
unsigned int max_cycles; unsigned int max_cycles;
int priority; int priority;
int etf_offset; int etf_offset;
int send_tx_delay;
int affinity_cpu;
uint64_t start_ts;
} thread_param_t; } thread_param_t;
typedef struct main_param { typedef struct main_param {
int refresh_rate; int refresh_rate;
int verbose; int verbose;
int enable_tracing; int enable_tracing;
int enable_graph; int enable_graph;
} main_param_t; } main_param_t;
// Static functions // Static functions
...@@ -62,433 +65,421 @@ static void sighand(int sig_num); ...@@ -62,433 +65,421 @@ static void sighand(int sig_num);
static uint64_t kernel_latency_hist[MAX_KERNEL_LATENCY]; static uint64_t kernel_latency_hist[MAX_KERNEL_LATENCY];
static uint64_t rtt_hist[MAX_RTT]; static uint64_t rtt_hist[MAX_RTT];
static uint64_t nb_cycles; static uint64_t nb_cycles;
static main_param_t main_params; static main_param_t main_params;
static thread_param_t thread_params; static thread_param_t thread_params;
static egress_param_t egress_params; static egress_param_t egress_params;
static ingress_param_t ingress_params; static ingress_param_t ingress_params;
static rtt_stat_t rtt_stats = {.min_rtt = INT_MAX}; static rtt_stat_t rtt_stats = {.min_rtt = INT_MAX};
static egress_stat_t egress_stats = {.min_kernel_latency = INT_MAX, static egress_stat_t egress_stats = {.min_kernel_latency = INT_MAX,
.min_interval = INT_MAX}; .min_interval = INT_MAX};
static ingress_stat_t ingress_stats = {.min_kernel_latency = INT_MAX, static ingress_stat_t ingress_stats = {.min_kernel_latency = INT_MAX,
.min_interval = INT_MAX}; .min_interval = INT_MAX};
static int enable_histograms; static int enable_histograms;
static int enable_affinity;
static int enable_etf; static int enable_etf;
static int enable_timestamps; static int enable_timestamps;
static enum TSNTask tsn_task; static enum TSNTask tsn_task;
static struct timespec measures_start; static struct timespec measures_start;
static struct timespec measures_end; static struct timespec measures_end;
static char send_data[MAX_BUFFER_SIZE]; static char send_data[MAX_BUFFER_SIZE];
static void help(char *argv[]) { static void help(char *argv[]) {
printf( printf(
"Usage: %s -f IF [-abthgv] [-e ETF_OFFSET] [-d BUF_LEN] [-i USEC] " "Usage: %s [-a CPU -p PRIO -i USEC -r USEC -l N] [-d BUF_LEN | -c "
"[-l N]" "DELAY -s NS] [-b -e OFFSET -q PK_PRIO -gtvT] IF IP\n\n"
"[-p PRIO] [-q PACKET_PRIO] [-r USEC] [-T LATENCY_THRESHOLD -G]\n\n" " -h Show help\n"
" -a Run the real time thread on CPU1\n" " -f IF Set the network interface to be used\n"
" -b Measure RTT\n" " -a CPU Pin the real time thread to CPU\n"
" -d BUF_LEN Set the length of tx buffer\n" " -p PRIO RT thread priority\n"
" -e ETF_OFFSET Set a txtime with an offset of " " -i USEC RT thread wake-up interval (default: 10ms)\n"
"ETF_OFFSET " " -r USEC non-RT main thread refresh interval\n"
"us (to be used in an ETF qdisc)\n" " -l N_CYCLES RT thread cycles amount (default: infinite)\n"
" -f IF Set the network interface to be " " -d BUF_LEN TX buffer len\n"
"used\n" " -c USEC Send timestamp with the specified delay\n"
" -g Print histograms to sdtout on exit\n" " -s NS Common start time reference\n"
" -h Show help\n" " -b Measure RTT\n"
" -i USEC Wake up the real time thread every " " -e USEC txtime userspace offset (for ETF qdisc)\n"
"USEC " " -q PK_PRIO Packet priority \n"
"microseconds (Default: 10ms)\n" " -g Print histograms to sdtout on exit\n"
" -l N Wake up the real time thread N times " " -t Enable TX timestamps\n"
"(Default: 0)\n" " -v Verbose\n"
" -p PRIO Run the real time thread at priority " " -T Enable tracing until deadline is missed\n"
"PRIO\n" "\n",
" -q PACKET_PRIO Send packets with PACKET_PRIO " argv[0]);
"priority\n"
" -r USEC Refresh the non real time main "
"thread "
"every USEC microseconds (Default: 50ms)\n"
" -t Enable timestamps\n"
" -v Verbose\n"
" -T Enable tracing until deadline is "
"missed\n"
"\n",
argv[0]);
} }
/* /*
* Real-time thread: Sends packets at a regular intervall * Real-time thread:
* - Sends packets at a regular intervall
*/ */
static void *packet_sending_thread(void *p) { static void *packet_sending_thread(void *p) {
(void)p; (void)p;
struct timespec next, current, previous; struct timespec next, current, previous;
uint64_t next_txtime; uint64_t next_txtime = 0, tx_data = 0;
cpu_set_t mask; cpu_set_t mask;
// Set thread CPU affinity // Set thread CPU affinity
if (enable_affinity) { if (thread_params.affinity_cpu != -1) {
CPU_ZERO(&mask); CPU_ZERO(&mask);
CPU_SET(1, &mask); CPU_SET(thread_params.affinity_cpu, &mask);
if (sched_setaffinity(0, sizeof(mask), &mask)) if (sched_setaffinity(0, sizeof(mask), &mask))
error(EXIT_FAILURE, errno, error(EXIT_FAILURE, errno, "Could not set CPU affinity to CPU %d\n",
"Could not set CPU affinity to CPU #1\n"); thread_params.affinity_cpu);
} }
if (enable_etf) { clock_gettime(CLOCK_MONOTONIC, &measures_start);
// Measure from CLOCK_TAI to generate timestamp
clock_gettime(CLOCK_TAI, &next); if(thread_params.start_ts)
next_txtime = next.tv_sec * NSEC_PER_SEC + next.tv_nsec; next = uint_to_ts(thread_params.start_ts);
next_txtime += thread_params.etf_offset; else
} else { clock_gettime(CLOCK_REALTIME, &next);
next_txtime = 0;
} // Set txtime and TX data timestamp if required
if (enable_etf || thread_params.send_tx_delay) {
clock_gettime(CLOCK_MONOTONIC, &next); if (enable_etf) {
clock_gettime(CLOCK_MONOTONIC, &measures_start); next_txtime = next.tv_sec * NSEC_PER_SEC + next.tv_nsec;
next_txtime += thread_params.etf_offset;
// Packet sending loop }
for (nb_cycles = 0;; nb_cycles++) {
if (thread_params.max_cycles && if (thread_params.send_tx_delay) {
nb_cycles >= ((unsigned int)thread_params.max_cycles)) tx_data = next.tv_sec * NSEC_PER_SEC + next.tv_nsec;
break; tx_data += thread_params.send_tx_delay;
}
clock_gettime(CLOCK_MONOTONIC, &current); }
sprintf(send_data, "%d", (int)(nb_cycles % 1000)); // Packet sending loop
do_tsn_task(send_data, next_txtime); for (nb_cycles = 0;; nb_cycles++) {
if (thread_params.max_cycles &&
add_ns(&next, thread_params.interval); nb_cycles >= ((unsigned int)thread_params.max_cycles))
break;
if (enable_etf) next_txtime += thread_params.interval;
// TX data
if (nb_cycles) { *((uint64_t *)send_data) =
int interval_us = calcdiff_ns(current, previous) / 1000; thread_params.send_tx_delay ? tx_data : 0xdeadbeef;
egress_stats.min_interval = // Get timestamp before TSN task for stats
_min_(interval_us, egress_stats.min_interval); clock_gettime(CLOCK_REALTIME, &current);
egress_stats.max_interval = do_tsn_task(send_data, next_txtime);
_max_(interval_us, egress_stats.max_interval); add_ns(&next, thread_params.interval);
egress_stats.avg_interval =
(egress_stats.avg_interval * nb_cycles + // Update txtime and TX data timestamp if required
interval_us) / if (enable_etf) next_txtime += thread_params.interval;
(nb_cycles + 1); if (thread_params.send_tx_delay) tx_data += thread_params.interval;
}
// Update statistics
previous = current; if (nb_cycles) {
int interval_us = calcdiff_ns(current, previous) / 1000;
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next, NULL);
} egress_stats.min_interval = _min_(interval_us, egress_stats.min_interval);
egress_stats.max_interval = _max_(interval_us, egress_stats.max_interval);
return NULL; egress_stats.avg_interval =
(egress_stats.avg_interval * nb_cycles + interval_us) /
(nb_cycles + 1);
}
previous = current;
clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &next, NULL);
}
pthread_exit(NULL);
} }
/* /*
* Main thread, has non-real time priority * Main thread:
* Handles the IO and creates the real time thread * - Has non-real time priority
* - Handles the IO and creates the real time thread
*/ */
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
pthread_t thread; pthread_t thread;
struct sched_param param; struct sched_param param;
pthread_attr_t attr; pthread_attr_t attr;
// Default configuration values // Default configuration values
thread_params.interval = 100000 * 1000; thread_params.interval = 100000 * 1000;
thread_params.max_cycles = 0; thread_params.max_cycles = 0;
thread_params.priority = 99; thread_params.priority = 99;
thread_params.send_tx_delay = 0;
main_params.refresh_rate = 50000; thread_params.affinity_cpu = -1;
main_params.verbose = 0; thread_params.start_ts = 0;
main_params.enable_tracing = 0; main_params.refresh_rate = 50000;
main_params.enable_graph = 0; main_params.verbose = 0;
main_params.enable_tracing = 0;
enable_affinity = 0; main_params.enable_graph = 0;
enable_etf = 0; egress_params.packet_priority = 3;
enable_timestamps = 0; egress_params.tx_buffer_len = 1024;
enable_histograms = 0; enable_etf = 0;
tsn_task = SEND_PACKET_TASK; enable_timestamps = 0;
enable_histograms = 0;
egress_params.packet_priority = 3; tsn_task = SEND_PACKET_TASK;
egress_params.tx_buffer_len = 1024;
// Lock all current and future pages from preventing of being paged to
/* Lock all current and future pages from preventing of being paged to // swap
* swap */ if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
if (mlockall(MCL_CURRENT | MCL_FUTURE)) { fprintf(stderr, "mlockall failed");
perror("mlockall failed"); exit(EXIT_FAILURE);
/* exit(-1) or do error handling */ }
}
// Process bash options
// Process bash options process_options(argc, argv);
process_options(argc, argv);
egress_params.use_etf = enable_etf;
egress_params.use_etf = enable_etf; egress_params.use_timestamps = enable_timestamps;
egress_params.use_timestamps = enable_timestamps;
if (enable_histograms) {
if (enable_histograms) { // Init histograms
// Init histograms memset(kernel_latency_hist, 0, sizeof(kernel_latency_hist));
memset(kernel_latency_hist, 0, sizeof(kernel_latency_hist)); memset(rtt_hist, 0, sizeof(rtt_hist));
memset(rtt_hist, 0, sizeof(rtt_hist)); }
}
if (main_params.enable_tracing) {
if (main_params.enable_tracing) { // Enable ftrace
// Enable ftrace open_fds();
open_fds(); }
}
// Catch breaks with sighand to print the histograms
// Catch breaks with sighand to print the histograms init_signals(sighand);
init_signals(sighand);
// Initialize the UDP packet sending socket
// Initialize the UDP packet sending socket init_udp_send(&egress_params, &egress_stats, enable_histograms,
init_udp_send(&egress_params, &egress_stats, enable_histograms, main_params.enable_tracing, kernel_latency_hist);
main_params.enable_tracing, kernel_latency_hist);
// Initialize the UDP packet receiving socket if RTT is measured
// Initialize the UDP packet receiving socket if RTT is measured if (tsn_task == RTT_TASK)
if (tsn_task == RTT_TASK) init_udp_recv(&ingress_params, &ingress_stats, enable_histograms,
init_udp_recv(&ingress_params, &ingress_stats, kernel_latency_hist);
enable_histograms, kernel_latency_hist);
// Initialize pthread attributes (default values)
/* Initialize pthread attributes (default values) */ if (pthread_attr_init(&attr)) {
if (pthread_attr_init(&attr)) { fprintf(stderr, "init pthread attributes failed\n");
fprintf(stderr, "init pthread attributes failed\n"); exit(EXIT_FAILURE);
exit(EXIT_FAILURE); }
} // Set a specific stack size
/* Set a specific stack size */ if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN)) {
if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN)) { fprintf(stderr, "pthread setstacksize failed\n");
fprintf(stderr, "pthread setstacksize failed\n"); exit(EXIT_FAILURE);
exit(EXIT_FAILURE); }
} // Set scheduler policy and priority of pthread
/* Set scheduler policy and priority of pthread */ if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO)) {
if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO)) { fprintf(stderr, "pthread setschedpolicy failed\n");
fprintf(stderr, "pthread setschedpolicy failed\n"); exit(EXIT_FAILURE);
exit(EXIT_FAILURE); }
} param.sched_priority = thread_params.priority;
param.sched_priority = thread_params.priority; if (pthread_attr_setschedparam(&attr, &param)) {
if (pthread_attr_setschedparam(&attr, &param)) { fprintf(stderr, "pthread setschedparam failed\n");
fprintf(stderr, "pthread setschedparam failed\n"); exit(EXIT_FAILURE);
exit(EXIT_FAILURE); }
} /* Use scheduling parameters of attr */
/* Use scheduling parameters of attr */ if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)) {
if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)) { fprintf(stderr, "pthread setinheritsched failed\n");
fprintf(stderr, "pthread setinheritsched failed\n"); exit(EXIT_FAILURE);
exit(EXIT_FAILURE); }
}
// Create the real time thread
// Create the real time thread if (pthread_create(&thread, &attr, packet_sending_thread, NULL))
if (pthread_create(&thread, &attr, packet_sending_thread, NULL)) error(EXIT_FAILURE, errno, "Couldn't create packet sending thread");
error(EXIT_FAILURE, errno,
"Couldn't create packet sending thread"); // Verbose loop
for (;;) {
// Verbose loop usleep(main_params.refresh_rate);
for (;;) {
usleep(main_params.refresh_rate); if (main_params.verbose) {
if (main_params.verbose) { // RTT stats printing
if (tsn_task == RTT_TASK) { if (tsn_task == RTT_TASK) {
printf("%9" PRIu64 ": RTT: %4d %4d %4d\n",
nb_cycles, rtt_stats.min_rtt, // N_CYCLES, RTT min / avg / max
rtt_stats.avg_rtt, rtt_stats.max_rtt); printf("%9" PRIu64 ": RTT: %4d %4d %4d\n", nb_cycles, rtt_stats.min_rtt,
printf("\033[%dA", 1); rtt_stats.avg_rtt, rtt_stats.max_rtt);
} else { printf("\033[%dA", 1);
printf("%9" PRIu64
": [%4d, %4d], I (10us): %3d %3d %3d", // Packet send stats printing
nb_cycles, } else {
(int)egress_stats.invalid_parameter,
(int)egress_stats.missed_deadline, // N_CYCLES, error queue, measured send interval min / avg / max
egress_stats.min_interval / 10, printf("%9" PRIu64 ": [%4d, %4d], I (10us): %3d %3d %3d", nb_cycles,
egress_stats.avg_interval / 10, (int)egress_stats.invalid_parameter,
egress_stats.max_interval / 10); (int)egress_stats.missed_deadline,
egress_stats.min_interval / 10, egress_stats.avg_interval / 10,
if (enable_timestamps) { egress_stats.max_interval / 10);
printf(", K: %4d %4d %4d [%4d]\n",
egress_stats.min_kernel_latency, // SOF_TX timestamps
egress_stats.avg_kernel_latency, if (enable_timestamps)
egress_stats.max_kernel_latency, printf(", K: %4d %4d %4d [%4d]\n", egress_stats.min_kernel_latency,
(int)egress_stats egress_stats.avg_kernel_latency,
.high_kernel_latency); egress_stats.max_kernel_latency,
} else { (int)egress_stats.high_kernel_latency);
printf("\n"); else
} printf("\n");
printf("\033[%dA", 1);
} printf("\033[%dA", 1);
} }
}
if (thread_params.max_cycles)
if (thread_params.max_cycles == nb_cycles) break; if (thread_params.max_cycles)
} if (thread_params.max_cycles == nb_cycles) break;
}
if (enable_histograms) print_histograms();
if (enable_histograms) print_histograms();
exit(EXIT_SUCCESS);
exit(EXIT_SUCCESS);
} }
// Critical TSN task /* Critical TSN task
*/
static void do_tsn_task(char *data, uint64_t next_txtime) { static void do_tsn_task(char *data, uint64_t next_txtime) {
struct timespec t1, t2; struct timespec t1, t2;
int rtt_us; int rtt_us;
// One way packet sending // One way packet sending
if (tsn_task == SEND_PACKET_TASK) { if (tsn_task == SEND_PACKET_TASK) {
send_udp_packet(data, next_txtime); send_udp_packet(data, next_txtime);
// Round Trip Time measurement // Round Trip Time measurement
} else if (tsn_task == RTT_TASK) { } else if (tsn_task == RTT_TASK) {
clock_gettime(CLOCK_MONOTONIC, &t1); clock_gettime(CLOCK_MONOTONIC, &t1);
send_udp_packet(data, next_txtime); send_udp_packet(data, next_txtime);
recv_udp_packet(); recv_udp_packet();
clock_gettime(CLOCK_MONOTONIC, &t2); clock_gettime(CLOCK_MONOTONIC, &t2);
rtt_us = calcdiff_ns(t2, t1) / 1000; rtt_us = calcdiff_ns(t2, t1) / 1000;
rtt_stats.min_rtt = _min_(rtt_us, rtt_stats.min_rtt); rtt_stats.min_rtt = _min_(rtt_us, rtt_stats.min_rtt);
rtt_stats.max_rtt = _max_(rtt_us, rtt_stats.max_rtt); rtt_stats.max_rtt = _max_(rtt_us, rtt_stats.max_rtt);
rtt_stats.avg_rtt = rtt_stats.avg_rtt =
(((uint64_t)rtt_stats.avg_rtt) * (nb_cycles - 1) + rtt_us) / (((uint64_t)rtt_stats.avg_rtt) * (nb_cycles - 1) + rtt_us) / nb_cycles;
nb_cycles;
if (rtt_us > MAX_RTT)
if (rtt_us > MAX_RTT) fprintf(stderr, "RTT value higher than MAX_RTT : %d ( > %d)\n", rtt_us,
fprintf(stderr, MAX_RTT);
"RTT value higher than MAX_RTT : %d ( > %d)\n", else
rtt_us, MAX_RTT); rtt_hist[rtt_us]++;
else }
rtt_hist[rtt_us]++;
}
} }
// Print histograms in .json format /* Print histograms in .json format
*/
static void print_histograms() { static void print_histograms() {
uint64_t duration; uint64_t duration;
int duration_hour, duration_minutes, interval; int duration_hour, duration_minutes, interval;
int max_hist_val; int max_hist_val;
uint64_t *histogram; uint64_t *histogram;
clock_gettime(CLOCK_MONOTONIC, &measures_end); clock_gettime(CLOCK_MONOTONIC, &measures_end);
duration = calcdiff_ns(measures_end, measures_start); duration = calcdiff_ns(measures_end, measures_start);
duration_hour = duration / NSEC_PER_SEC / 3600; duration_hour = duration / NSEC_PER_SEC / 3600;
duration_minutes = duration / NSEC_PER_SEC / 60 - duration_hour * 60; duration_minutes = duration / NSEC_PER_SEC / 60 - duration_hour * 60;
interval = thread_params.interval / 1000; interval = thread_params.interval / 1000;
printf( printf(
"{\"measure_sets\": [{" "{\"measure_sets\": [{"
"\"measure_type\": \"%s\"," "\"measure_type\": \"%s\","
"\"props_names\": [\"%s\"]," "\"props_names\": [\"%s\"],"
"\"units\": [\"us\"]," "\"units\": [\"us\"],"
"\"props_type\": \"histogram\"," "\"props_type\": \"histogram\","
"\"metadata\": {\"i\": \"%dus\", \"duration\": \"%dh%d\", " "\"metadata\": {\"i\": \"%dus\", \"duration\": \"%dh%d\", "
"\"etf_offset\": \"%dus\"}," "\"etf_offset\": \"%dus\"},"
"\"props\": [[", "\"props\": [[",
(tsn_task == SEND_PACKET_TASK) ? "packet_send_timestamps" (tsn_task == SEND_PACKET_TASK) ? "packet_send_timestamps" : "packet_rtt",
: "packet_rtt", (tsn_task == SEND_PACKET_TASK) ? "kernel_space" : "rtt", interval,
(tsn_task == SEND_PACKET_TASK) ? "kernel_space" : "rtt", interval, duration_hour, duration_minutes, thread_params.etf_offset);
duration_hour, duration_minutes, thread_params.etf_offset);
if (tsn_task == SEND_PACKET_TASK) {
if (tsn_task == SEND_PACKET_TASK) { histogram = kernel_latency_hist;
histogram = kernel_latency_hist; max_hist_val = histogram_max(histogram, MAX_KERNEL_LATENCY - 1);
max_hist_val = histogram_max(histogram, MAX_KERNEL_LATENCY - 1); } else {
} else { histogram = rtt_hist;
histogram = rtt_hist; max_hist_val = histogram_max(histogram, MAX_RTT - 1);
max_hist_val = histogram_max(histogram, MAX_RTT - 1); }
}
for (int j = 0; j < max_hist_val; j++)
for (int j = 0; j < max_hist_val; j++) printf("%" PRIi64 "%s", histogram[j], (j + 1 < max_hist_val ? ", " : ""));
printf("%" PRIi64 "%s", histogram[j], printf("]]}]}\n");
(j + 1 < max_hist_val ? ", " : ""));
printf("]]}]}\n");
} }
static void sighand(int sig_num) { static void sighand(int sig_num) {
(void)sig_num; (void)sig_num;
if (enable_histograms) print_histograms(); if (enable_histograms) print_histograms();
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
// Process bash options /* Process bash options
*/
static void process_options(int argc, char *argv[]) { static void process_options(int argc, char *argv[]) {
int network_if_specified = 0;
for (;;) {
for (;;) { int c = getopt(argc, argv, "a:bc:d:e:ghi:l:p:q:r:s:tvT");
int c = getopt(argc, argv, "abd:e:f:ghi:l:p:q:r:tvT");
if (c == -1) break;
if (c == -1) break;
switch (c) {
switch (c) { case 'a':
case 'a': thread_params.affinity_cpu = atoi(optarg);
enable_affinity = 1; break;
break; case 'b':
case 'b': tsn_task = RTT_TASK;
tsn_task = RTT_TASK; break;
break; case 'c':
case 'd': thread_params.send_tx_delay = atoi(optarg) * 1000;
egress_params.tx_buffer_len = atoi(optarg); break;
if (egress_params.tx_buffer_len < 1) { case 'd':
fprintf(stderr, egress_params.tx_buffer_len = atoi(optarg);
"BUF_LEN should be greater " if (egress_params.tx_buffer_len < 1) {
"than 1\n"); fprintf(stderr, "BUF_LEN should be greater than 1\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
break; break;
case 'e': case 'e':
enable_etf = 1; enable_etf = 1;
thread_params.etf_offset = atoi(optarg) * 1000; thread_params.etf_offset = atoi(optarg) * 1000;
break; break;
case 'f': case 'g':
network_if_specified = 1; enable_histograms = 1;
strcpy(egress_params.network_if, optarg); break;
break; case 'h':
case 'g': help(argv);
enable_histograms = 1; exit(EXIT_SUCCESS);
break; break;
case 'h': case 'i':
help(argv); thread_params.interval = atoi(optarg) * 1000;
exit(EXIT_SUCCESS); break;
break; case 'l':
case 'i': thread_params.max_cycles = atoi(optarg);
thread_params.interval = atoi(optarg) * 1000; break;
break; case 'p':
case 'l': thread_params.priority = atoi(optarg);
thread_params.max_cycles = atoi(optarg); break;
break; case 'q':
case 'p': egress_params.packet_priority = atoi(optarg);
thread_params.priority = atoi(optarg); break;
break; case 's':
case 'q': thread_params.start_ts = strtoull(optarg, NULL, 10);
egress_params.packet_priority = atoi(optarg); break;
break; case 'r':
case 'r': main_params.refresh_rate = atoi(optarg);
main_params.refresh_rate = atoi(optarg); break;
break; case 't':
case 't': enable_timestamps = 1;
enable_timestamps = 1; break;
break; case 'v':
case 'v': main_params.verbose = 1;
main_params.verbose = 1; break;
break; case 'T':
case 'T': main_params.enable_tracing = 1;
main_params.enable_tracing = 1; break;
break; }
} }
}
if (argc != optind + 2) {
if (!network_if_specified) { if (argc < optind + 2)
fprintf(stderr, "You need to specifiy an network interface\n"); fprintf(stderr, "You need to specifiy an interface and IP address\n");
help(argv); else
exit(EXIT_FAILURE); fprintf(stderr, "Too many arguments\n");
} help(argv);
exit(EXIT_FAILURE);
if (argc != optind + 1) { }
if (argc < optind + 1)
fprintf(stderr, "You need to specifiy an IP address\n"); strcpy(egress_params.network_if, argv[optind]);
else strcpy(egress_params.server_ip, argv[optind + 1]);
fprintf(stderr, "Too many arguments\n");
help(argv);
exit(EXIT_FAILURE);
}
strcpy(egress_params.server_ip, argv[optind]);
} }
...@@ -13,61 +13,66 @@ void (*previous_handlers[NSIG])(int); ...@@ -13,61 +13,66 @@ void (*previous_handlers[NSIG])(int);
static void (*sighand)(int); static void (*sighand)(int);
uint64_t ts_to_uint(struct timespec t) { uint64_t ts_to_uint(struct timespec t) {
return t.tv_sec * NSEC_PER_SEC + t.tv_nsec; return t.tv_sec * NSEC_PER_SEC + t.tv_nsec;
}
struct timespec uint_to_ts(uint64_t t) {
struct timespec ts;
ts.tv_sec = t / NSEC_PER_SEC;
ts.tv_nsec = t - (ts.tv_sec * NSEC_PER_SEC);
return ts;
} }
void add_ns(struct timespec *t, uint64_t ns) { void add_ns(struct timespec *t, uint64_t ns) {
t->tv_nsec += ns; t->tv_nsec += ns;
while ((unsigned int)t->tv_nsec >= NSEC_PER_SEC) { while ((unsigned int)t->tv_nsec >= NSEC_PER_SEC) {
t->tv_sec += 1; t->tv_sec += 1;
t->tv_nsec -= NSEC_PER_SEC; t->tv_nsec -= NSEC_PER_SEC;
} }
} }
uint64_t calcdiff_ns(struct timespec t1, struct timespec t2) { uint64_t calcdiff_ns(struct timespec t1, struct timespec t2) {
uint64_t diff; uint64_t diff;
diff = NSEC_PER_SEC * (uint64_t)((int)t1.tv_sec - (int)t2.tv_sec); diff = NSEC_PER_SEC * (uint64_t)((int)t1.tv_sec - (int)t2.tv_sec);
diff += ((int)t1.tv_nsec - (int)t2.tv_nsec); diff += ((int)t1.tv_nsec - (int)t2.tv_nsec);
return diff; return diff;
} }
int _max_(int a, int b) { return a > b ? a : b; } int _max_(int a, int b) { return a > b ? a : b; }
int _min_(int a, int b) { return a < b ? a : b; } int _min_(int a, int b) { return a < b ? a : b; }
int histogram_min(uint64_t *histogram, int max_value) { int histogram_min(uint64_t *histogram, int max_value) {
int ret = max_value; int ret = max_value;
for (int i = max_value; i >= 0; i--) ret = histogram[i] ? i : ret; for (int i = max_value; i >= 0; i--) ret = histogram[i] ? i : ret;
return ret; return ret;
} }
int histogram_max(uint64_t *histogram, int max_value) { int histogram_max(uint64_t *histogram, int max_value) {
int ret = 0; int ret = 0;
for (int i = 0; i <= max_value; i++) ret = histogram[i] ? i : ret; for (int i = 0; i <= max_value; i++) ret = histogram[i] ? i : ret;
return ret; return ret;
} }
static void sighand_wrapper(int sig) { static void sighand_wrapper(int sig) {
// If we get un unexpected signal, report it, if not print the histogram // If we get un unexpected signal, report it, if not print the histogram
if (sig == SIGINT || sig == SIGTERM) if (sig == SIGINT || sig == SIGTERM)
(*sighand)(sig); // Will print the histogram (*sighand)(sig); // Will print the histogram
else else
printf("Uknown signal interrupt: %s (%d)\n", strsignal(sig), printf("Uknown signal interrupt: %s (%d)\n", strsignal(sig), sig);
sig);
// Execute the default handler // Execute the default handler
if (previous_handlers[sig] == SIG_DFL) { if (previous_handlers[sig] == SIG_DFL) {
signal(sig, SIG_DFL); signal(sig, SIG_DFL);
raise(sig); raise(sig);
} else if (previous_handlers[sig] == SIG_IGN) { } else if (previous_handlers[sig] == SIG_IGN) {
return; return;
} else { } else {
(*previous_handlers[sig])(sig); (*previous_handlers[sig])(sig);
} }
} }
void init_signals(void (*_sighand)(int)) { void init_signals(void (*_sighand)(int)) {
sighand = _sighand; sighand = _sighand;
for (int i = 0; i < NSIG; i++) signal(i, sighand_wrapper); for (int i = 0; i < NSIG; i++) signal(i, sighand_wrapper);
} }
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#define err_errno(...) error(EXIT_FAILURE, errno, __VA_ARGS__); #define err_errno(...) error(EXIT_FAILURE, errno, __VA_ARGS__);
uint64_t ts_to_uint(struct timespec t); uint64_t ts_to_uint(struct timespec t);
struct timespec uint_to_ts(uint64_t t);
void add_ns(struct timespec *t, uint64_t ns); void add_ns(struct timespec *t, uint64_t ns);
uint64_t calcdiff_ns(struct timespec t1, struct timespec t2); uint64_t calcdiff_ns(struct timespec t1, struct timespec t2);
......
#include "gpio.h"
#include <stdlib.h>
#include <stdio.h>
static int gpio_state;
static char cmd[128];
void enable_gpio(int gpio_index) {
sprintf(cmd, "echo %d > /sys/class/gpio/export", gpio_index);
system(cmd);
sprintf(cmd, "echo out > /sys/class/gpio/gpio%d/direction", gpio_index);
system(cmd);
}
void toggle_gpio(int gpio_index) {
sprintf(cmd, "echo %d > /sys/class/gpio/gpio%d/value", gpio_state, gpio_index);
system(cmd);
gpio_state = !gpio_state;
}
#ifndef GPIO_H
#define GPIO_H
void enable_gpio(int gpio_index);
void toggle_gpio(int gpio_index);
#endif
...@@ -26,7 +26,6 @@ typedef struct ingress_stat { ...@@ -26,7 +26,6 @@ typedef struct ingress_stat {
uint64_t packets_received; uint64_t packets_received;
uint64_t high_kernel_latency; uint64_t high_kernel_latency;
uint64_t high_jitter; uint64_t high_jitter;
int lost_packets;
char data[MAX_BUFFER_SIZE]; char data[MAX_BUFFER_SIZE];
} ingress_stat_t; } ingress_stat_t;
......
...@@ -22,32 +22,34 @@ ...@@ -22,32 +22,34 @@
#include <string.h> #include <string.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h> #include <unistd.h>
#include "common.h" #include "common.h"
#include "recv_packet.h" #include "recv_packet.h"
#include "send_packet.h" #include "send_packet.h"
#include "tracer.h" #include "tracer.h"
#include "gpio.h"
// Structs // Structs
enum TSNTask { RECV_PACKET_TASK, RTT_TASK, XDP_TASK }; enum TSNTask { RECV_PACKET_TASK, RTT_TASK, XDP_TASK };
typedef struct thread_param { typedef struct thread_param {
int interval; int interval;
int priority; int priority;
uint64_t latency_threshold; int affinity_cpu;
uint64_t latency_threshold;
int emit_signal;
} thread_param_t; } thread_param_t;
typedef struct main_params { typedef struct main_params {
int refresh_rate; int refresh_rate;
int verbose; int verbose;
int enable_tracing; int enable_tracing;
int enable_xdp_tracing; int enable_xdp_tracing;
int enable_graph; int enable_graph;
} main_param_t; } main_param_t;
static void process_options(int argc, char *argv[]); static void process_options(int argc, char *argv[]);
...@@ -58,459 +60,464 @@ static void sighand(int sig_num); ...@@ -58,459 +60,464 @@ static void sighand(int sig_num);
static uint64_t kernel_latency_hist[MAX_KERNEL_LATENCY]; static uint64_t kernel_latency_hist[MAX_KERNEL_LATENCY];
static uint64_t jitter_hist[MAX_JITTER]; static uint64_t jitter_hist[MAX_JITTER];
static main_param_t main_params; static main_param_t main_params;
static thread_param_t thread_params; static thread_param_t thread_params;
static ingress_param_t ingress_params; static ingress_param_t ingress_params;
static egress_param_t egress_params; static egress_param_t egress_params;
static egress_stat_t egress_stats = {.min_kernel_latency = INT_MAX}; static egress_stat_t egress_stats = {.min_kernel_latency = INT_MAX};
static ingress_stat_t ingress_stats = {.min_kernel_latency = INT_MAX, static ingress_stat_t ingress_stats = {.min_kernel_latency = INT_MAX,
.min_interval = INT_MAX}; .min_interval = INT_MAX};
static int enable_histograms; static int enable_histograms;
static int enable_affinity;
static int enable_timestamps; static int enable_timestamps;
static enum TSNTask tsn_task; static enum TSNTask tsn_task;
static struct timespec measures_start; static struct timespec measures_start;
static struct timespec measures_end; static struct timespec measures_end;
static struct timespec emit_signal_next;
static pthread_mutex_t emit_signal_mutex;
static pthread_cond_t emit_signal_ts_received;
static void help(char *argv[]) { static void help(char *argv[]) {
printf( printf(
"Usage: %s [-aghtv] [-b CLIENT_IP] [-d BUF_LEN] [-f IF] [-i USEC] " "Usage: %s [-f IF -a CPU -p PRIO -i USEC -r USEC] [-b CLIENT_IP] [-d "
"[-p " "BUF_LEN -cgtv] [-T LATENCY_THRESHOLD -G] [-x POLL_MODE -X]\n\n"
"PRIO] [-T LATENCY_THRESHOLD -G]" " -h Show help\n"
" [-r USEC]\n\n" " -f IF Set the network interface to be used\n"
" -a Run the real time thread on CPU1\n" " -a CPU Pin the real time thread to CPU\n"
" -b CLIENT_IP Server side RTT\n" " -p PRIO RT thread priority\n"
" -d BUF_LEN Set the length of tx buffer\n" " -i USEC RT thread wake-up interval (default: 10ms)\n"
" -f IF Set the network interface to be " " -r USEC non-RT main thread refresh interval\n"
"used\n" " -d BUF_LEN Set the length of tx buffer\n"
" -g Print histograms to sdtout on exit\n" " -c Receive timestamp and emit signal\n"
" -h Show help\n" " -b CLIENT_IP Server side RTT\n"
" -i USEC Wake up the real time thread every " " -g Print histograms to sdtout on exit\n"
"USEC " " -t Enable timestamps\n"
"microseconds (Default: 10ms)\n" " -x POLL_MODE Use AF_XDP sockets\n"
" -p PRIO Run the real time thread at priority " " POLL_MODE: 0 for polling, 1 for combination of both\n"
"PRIO\n" " -X Trace during XDP packet reception\n"
" -r USEC Refresh the non real time main " " -T THRESHOLD Enable tracing until THRESHOLD is reached\n"
"thread " " -G Enable function_graph tracer, used with -T\n"
"every USEC microseconds\n" " -v Verbose\n"
" -t Enable timestamps\n" "\n",
" -v Verbose\n" argv[0]);
" -x POLL_MODE Use AF_XDP sockets\n"
" POLL_MODE: 0 for polling, 1 for combination of both\n"
" -X Trace during XDP packet reception\n"
" -T LATENCY_THRESHOLD Enable tracing until "
"LATENCY_THRESHOLD is "
"reached\n"
" -G Enable function_graph tracer, used "
"with "
"-T\n"
"\n",
argv[0]);
} }
// Real-time thread static void *emit_signal_thread(void *p) {
// Measures intervals between packet receptions (void)p;
static void *packet_receiving_thread(void *p) { cpu_set_t mask;
(void)p;
struct timespec current, previous; // Set thread CPU affinity
cpu_set_t mask; if (thread_params.affinity_cpu) {
int prev_packet_id = 0; CPU_ZERO(&mask);
char tracemark_message[128]; CPU_SET(thread_params.affinity_cpu, &mask);
if (sched_setaffinity(0, sizeof(mask), &mask))
// Set thread CPU affinity error(EXIT_FAILURE, errno, "Could not set CPU affinity to CPU %d\n",
if (enable_affinity) { thread_params.affinity_cpu);
CPU_ZERO(&mask); }
CPU_SET(1, &mask);
if (sched_setaffinity(0, sizeof(mask), &mask)) pthread_mutex_lock(&emit_signal_mutex);
error(EXIT_FAILURE, errno,
"Could not set CPU affinity to CPU #1\n"); for (;;) {
}
pthread_cond_wait(&emit_signal_ts_received, &emit_signal_mutex);
clock_gettime(CLOCK_MONOTONIC, &measures_start); clock_nanosleep(CLOCK_TAI, TIMER_ABSTIME, &emit_signal_next, NULL);
if(tsn_task == XDP_TASK) toggle_gpio(49);
setup_poll_fd(); printf("Toggled GPIO\n");
// Packet receiving loop }
for (ingress_stats.packets_received = 0;;
ingress_stats.packets_received++) { pthread_mutex_unlock(&emit_signal_mutex);
if (tsn_task == RTT_TASK) { pthread_exit(NULL);
recv_udp_packet(); }
send_udp_packet("", 0);
/* Real-time thread:
} else if (tsn_task == RECV_PACKET_TASK || * - Measures intervals between packet receptions
tsn_task == XDP_TASK) { */
int current_packet_id; static void *tsn_thread(void *p) {
(void)p;
if (tsn_task == RECV_PACKET_TASK) struct timespec current, previous;
recv_udp_packet(); cpu_set_t mask;
else char tracemark_message[128];
recv_xdp_packet();
// Set thread CPU affinity
clock_gettime(CLOCK_MONOTONIC, &current); if (thread_params.affinity_cpu) {
CPU_ZERO(&mask);
recv_xdp_cleanup(); CPU_SET(thread_params.affinity_cpu, &mask);
if (sched_setaffinity(0, sizeof(mask), &mask))
if (ingress_stats.packets_received) { error(EXIT_FAILURE, errno, "Could not set CPU affinity to CPU %d\n", thread_params.affinity_cpu);
int interval_us = }
calcdiff_ns(current, previous) / 1000;
clock_gettime(CLOCK_MONOTONIC, &measures_start);
ingress_stats.min_interval = _min_(
interval_us, ingress_stats.min_interval); if (tsn_task == XDP_TASK) setup_poll_fd();
ingress_stats.max_interval = _max_(
interval_us, ingress_stats.max_interval); // Packet receiving loop
ingress_stats.avg_interval = for (ingress_stats.packets_received = 0;; ingress_stats.packets_received++) {
(ingress_stats.avg_interval * // RTT
ingress_stats.packets_received + if (tsn_task == RTT_TASK) {
interval_us) / recv_udp_packet();
(ingress_stats.packets_received + 1); send_udp_packet("", 0);
if (tsn_task == RECV_PACKET_TASK) { // Receive packet
// Check if packets were lost } else if (tsn_task == RECV_PACKET_TASK || tsn_task == XDP_TASK) {
current_packet_id = atoi(ingress_stats.data); // Receive UDP or XDP packet
ingress_stats.lost_packets += if (tsn_task == RECV_PACKET_TASK)
(current_packet_id - recv_udp_packet();
prev_packet_id - 1) % else
1000; recv_xdp_packet();
prev_packet_id = current_packet_id;
} clock_gettime(CLOCK_MONOTONIC, &current);
recv_xdp_cleanup();
if (enable_histograms) {
int dist_to_interval = // Emit signal
interval_us - if (thread_params.emit_signal) {
(thread_params.interval / 1000); uint64_t emit_signal_t = *((uint64_t *) ingress_stats.data);
dist_to_interval += MAX_JITTER / 2;
pthread_mutex_lock(&emit_signal_mutex);
if (dist_to_interval > emit_signal_next = uint_to_ts(emit_signal_t);
((int)MAX_JITTER) || pthread_cond_signal(&emit_signal_ts_received);
dist_to_interval < 0) pthread_mutex_unlock(&emit_signal_mutex);
ingress_stats.high_jitter++; }
else
jitter_hist[dist_to_interval]++; // Update stats
} if (ingress_stats.packets_received) {
} int interval_us = calcdiff_ns(current, previous) / 1000;
// If the latency hits the tracing threshold, stop ingress_stats.min_interval =
// tracing _min_(interval_us, ingress_stats.min_interval);
if (main_params.enable_tracing && ingress_stats.max_interval =
(ingress_stats.max_interval > _max_(interval_us, ingress_stats.max_interval);
((int)thread_params.latency_threshold))) { ingress_stats.avg_interval =
sprintf(tracemark_message, (ingress_stats.avg_interval * ingress_stats.packets_received +
"Jitter threshold hit: %dus\n", interval_us) /
ingress_stats.max_interval); (ingress_stats.packets_received + 1);
tracemark(tracemark_message);
tracing(0); // Histogram
break; if (enable_histograms) {
} int dist_to_interval = interval_us - (thread_params.interval / 1000);
dist_to_interval += MAX_JITTER / 2;
previous = current;
} if (dist_to_interval > ((int)MAX_JITTER) || dist_to_interval < 0)
} ingress_stats.high_jitter++;
else
return NULL; jitter_hist[dist_to_interval]++;
}
}
// If the latency hits the tracing threshold, stop tracing
if (main_params.enable_tracing &&
(ingress_stats.max_interval >
((int)thread_params.latency_threshold))) {
sprintf(tracemark_message, "Jitter threshold hit: %dus\n",
ingress_stats.max_interval);
tracemark(tracemark_message);
tracing(0);
break;
}
previous = current;
}
}
return NULL;
} }
// Main thread, has non-real time priority static void create_thread(void * (*thread_function)(void *)) {
// Handles the IO and creates real time threads
pthread_t thread;
pthread_attr_t attr;
struct sched_param param;
// Initialize pthread attributes (default values)
if (pthread_attr_init(&attr)) {
fprintf(stderr, "init pthread attributes failed\n");
exit(EXIT_FAILURE);
}
// Set a specific stack size
if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN)) {
fprintf(stderr, "pthread setstacksize failed\n");
exit(EXIT_FAILURE);
}
// Set scheduler policy and priority of pthread
if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO)) {
fprintf(stderr, "pthread setschedpolicy failed\n");
exit(EXIT_FAILURE);
}
param.sched_priority = thread_params.priority;
if (pthread_attr_setschedparam(&attr, &param)) {
fprintf(stderr, "pthread setschedparam failed\n");
exit(EXIT_FAILURE);
}
// Use scheduling parameters of attr
if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)) {
fprintf(stderr, "pthread setinheritsched failed\n");
exit(EXIT_FAILURE);
}
// Create the real time thread
if (pthread_create(&thread, &attr, thread_function, NULL))
error(EXIT_FAILURE, errno, "Couldn't create packet receiving thread");
}
/* Main thread:
* - Has non-real time priority
* - Handles the IO and creates real time threads
*/
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
pthread_t thread;
struct sched_param param; // Initial values
pthread_attr_t attr; ingress_stats.min_interval = INT_MAX;
ingress_stats.avg_interval = 0;
ingress_stats.min_interval = INT_MAX; ingress_stats.max_interval = 0;
ingress_stats.avg_interval = 0; ingress_stats.min_kernel_latency = INT_MAX;
ingress_stats.max_interval = 0; ingress_stats.avg_kernel_latency = 0;
ingress_stats.max_kernel_latency = 0;
ingress_stats.min_kernel_latency = INT_MAX; ingress_stats.packets_received = 0;
ingress_stats.avg_kernel_latency = 0;
ingress_stats.max_kernel_latency = 0; // Default configuration values
thread_params.interval = 100000 * 1000;
ingress_stats.packets_received = 0; thread_params.priority = 99;
thread_params.emit_signal = 0;
// Default configuration values thread_params.affinity_cpu = 0;
thread_params.interval = 100000 * 1000; main_params.refresh_rate = 50000;
thread_params.priority = 99; main_params.verbose = 0;
main_params.refresh_rate = 50000; main_params.enable_tracing = 0;
main_params.verbose = 0; main_params.enable_graph = 0;
main_params.enable_tracing = 0; enable_timestamps = 0;
main_params.enable_graph = 0; enable_histograms = 0;
tsn_task = RECV_PACKET_TASK;
enable_affinity = 0; ingress_params.tx_buffer_len = 1024;
enable_timestamps = 0;
enable_histograms = 0; // Process bash options
tsn_task = RECV_PACKET_TASK; process_options(argc, argv);
ingress_params.tx_buffer_len = 1024; ingress_params.use_timestamps = enable_timestamps;
ingress_params.interval = thread_params.interval;
// Process bash options
process_options(argc, argv); // Init histograms
if (enable_histograms) {
ingress_params.use_timestamps = enable_timestamps; memset(kernel_latency_hist, 0, sizeof(kernel_latency_hist));
ingress_params.interval = thread_params.interval; memset(jitter_hist, 0, sizeof(jitter_hist));
}
if (enable_histograms) {
// Init histograms // Enable ftrace
memset(kernel_latency_hist, 0, sizeof(kernel_latency_hist)); if (main_params.enable_tracing || main_params.enable_xdp_tracing) open_fds();
memset(jitter_hist, 0, sizeof(jitter_hist));
} // Catch breaks with sighand to print the histograms or exit XDP
init_signals(sighand);
if (main_params.enable_tracing || main_params.enable_xdp_tracing) {
// Enable ftrace // Initialize the XDP or UDP packet receiving socket
open_fds(); if (tsn_task == XDP_TASK)
} init_xdp_recv(&ingress_params);
else
// Catch breaks with sighand to print the histograms or exit XDP init_udp_recv(&ingress_params, &ingress_stats, enable_histograms,
init_signals(sighand); kernel_latency_hist);
if (tsn_task == XDP_TASK) { // Initialize the UDP packet sending socket if RTT is measured
// Initialize the XDP packet receiving socket if (tsn_task == RTT_TASK)
init_xdp_recv(&ingress_params); init_udp_send(&egress_params, &egress_stats, 0, 0, NULL);
} else {
// Initialize the UDP packet receiving socket if(thread_params.emit_signal) {
init_udp_recv(&ingress_params, &ingress_stats, pthread_mutex_init(&emit_signal_mutex, NULL);
enable_histograms, kernel_latency_hist); pthread_cond_init(&emit_signal_ts_received, NULL);
}
enable_gpio(49);
// Initialize the UDP packet sending socket if RTT is measured }
if (tsn_task == RTT_TASK)
init_udp_send(&egress_params, &egress_stats, 0, 0, NULL); create_thread(tsn_thread);
create_thread(emit_signal_thread);
/* Initialize pthread attributes (default values) */
if (pthread_attr_init(&attr)) { // Verbose loop
fprintf(stderr, "init pthread attributes failed\n"); for (;;) {
exit(EXIT_FAILURE); usleep(main_params.refresh_rate);
}
/* Set a specific stack size */ // Stats printing
if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN)) { if (main_params.verbose && ingress_stats.packets_received > 1 &&
fprintf(stderr, "pthread setstacksize failed\n"); (tsn_task == RECV_PACKET_TASK || tsn_task == XDP_TASK)) {
exit(EXIT_FAILURE); int jitter = ingress_stats.max_interval - ingress_stats.min_interval;
}
/* Set scheduler policy and priority of pthread */ printf("%9" PRIu64 ": J: %5d, I (10us): %3d %3d %3d [%3d]",
if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO)) { ingress_stats.packets_received, jitter,
fprintf(stderr, "pthread setschedpolicy failed\n"); ingress_stats.min_interval / 10, ingress_stats.avg_interval / 10,
exit(EXIT_FAILURE); ingress_stats.max_interval / 10, (int)ingress_stats.high_jitter);
}
param.sched_priority = thread_params.priority; if (enable_timestamps)
if (pthread_attr_setschedparam(&attr, &param)) { printf(", K: %4d %4d %4d [%3d]\n", ingress_stats.min_kernel_latency,
fprintf(stderr, "pthread setschedparam failed\n"); ingress_stats.avg_kernel_latency,
exit(EXIT_FAILURE); ingress_stats.max_kernel_latency,
} (int)ingress_stats.high_kernel_latency);
/* Use scheduling parameters of attr */ else
if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)) { printf("\n");
fprintf(stderr, "pthread setinheritsched failed\n");
exit(EXIT_FAILURE); printf("\033[%dA", 1);
} }
// Create the real time thread if (main_params.enable_tracing &&
if (pthread_create(&thread, &attr, packet_receiving_thread, NULL)) (ingress_stats.max_interval >= ((int)thread_params.latency_threshold)))
error(EXIT_FAILURE, errno, break;
"Couldn't create packet receiving thread"); }
// Verbose loop pthread_mutex_destroy(&emit_signal_mutex);
for (;;) { pthread_cond_destroy(&emit_signal_ts_received);
usleep(main_params.refresh_rate); close_xdp_socket();
if (main_params.verbose && ingress_stats.packets_received > 1) {
if (tsn_task == RECV_PACKET_TASK ||
tsn_task == XDP_TASK) {
int jitter = ingress_stats.max_interval -
ingress_stats.min_interval;
printf(
"%9" PRIu64
": J: %5d, I (10us): %3d %3d %3d [%3d,%3d]",
ingress_stats.packets_received, jitter,
ingress_stats.min_interval / 10,
ingress_stats.avg_interval / 10,
ingress_stats.max_interval / 10,
(int)ingress_stats.high_jitter,
(int)ingress_stats.lost_packets);
if (enable_timestamps) {
printf(", K: %4d %4d %4d [%3d]\n",
ingress_stats.min_kernel_latency,
ingress_stats.avg_kernel_latency,
ingress_stats.max_kernel_latency,
(int)ingress_stats
.high_kernel_latency);
} else {
printf("\n");
}
printf("\033[%dA", 1);
}
}
if (main_params.enable_tracing &&
(ingress_stats.max_interval >=
((int)thread_params.latency_threshold)))
break;
}
close_xdp_socket();
} }
// Print histograms in .json format /* Print histograms in .json format
*/
static void print_histograms() { static void print_histograms() {
uint64_t duration; uint64_t duration;
int duration_hour, duration_minutes, interval; int duration_hour, duration_minutes, interval;
int max_latency, max_jitter, min_jitter; int max_latency, max_jitter, min_jitter;
clock_gettime(CLOCK_MONOTONIC, &measures_end); clock_gettime(CLOCK_MONOTONIC, &measures_end);
duration = calcdiff_ns(measures_end, measures_start); duration = calcdiff_ns(measures_end, measures_start);
duration_hour = duration / NSEC_PER_SEC / 3600; duration_hour = duration / NSEC_PER_SEC / 3600;
duration_minutes = duration / NSEC_PER_SEC / 60 - duration_hour * 60; duration_minutes = duration / NSEC_PER_SEC / 60 - duration_hour * 60;
interval = thread_params.interval / 1000; interval = thread_params.interval / 1000;
if (enable_timestamps) { if (enable_timestamps) {
printf( printf(
"{\"measure_sets\": [{" "{\"measure_sets\": [{"
"\"measure_type\": \"packet_recv_timestamps\"," "\"measure_type\": \"packet_recv_timestamps\","
"\"props_names\": [\"kernel_space\"]," "\"props_names\": [\"kernel_space\"],"
"\"units\": [\"us\"]," "\"units\": [\"us\"],"
"\"props_type\": \"histogram\"," "\"props_type\": \"histogram\","
"\"metadata\": {\"i\": \"%dus\", \"duration\": \"%dh%d\"," "\"metadata\": {\"i\": \"%dus\", \"duration\": \"%dh%d\","
"\"lost_packets\": \"%d\"}," "\"lost_packets\": \"%d\"},"
"\"props\": [[", "\"props\": [[",
interval, duration_hour, duration_minutes, interval, duration_hour, duration_minutes,
ingress_stats.lost_packets + ((int)ingress_stats.high_jitter));
((int)ingress_stats.high_jitter));
max_latency = histogram_max(kernel_latency_hist, MAX_KERNEL_LATENCY - 1);
max_latency =
histogram_max(kernel_latency_hist, MAX_KERNEL_LATENCY - 1); for (int j = 0; j < max_latency; j++)
printf("%" PRIi64 "%s", kernel_latency_hist[j],
for (int j = 0; j < max_latency; j++) (j + 1 < max_latency ? ", " : ""));
printf("%" PRIi64 "%s", kernel_latency_hist[j], printf("]]");
(j + 1 < max_latency ? ", " : "")); }
printf("]]");
} max_jitter = histogram_max(jitter_hist, MAX_JITTER - 1);
min_jitter = histogram_min(jitter_hist, MAX_JITTER - 1);
max_jitter = histogram_max(jitter_hist, MAX_JITTER - 1);
min_jitter = histogram_min(jitter_hist, MAX_JITTER - 1); printf("%s", enable_timestamps ? "}, {" : "{\"measure_sets\": [{");
printf("%s", enable_timestamps ? "}, {" : "{\"measure_sets\": [{"); printf(
"\"measure_type\": \"packet_jitter\","
printf( "\"props_names\": [\"jitter\"],"
"\"measure_type\": \"packet_jitter\"," "\"units\": [\"us\"],"
"\"props_names\": [\"jitter\"]," "\"props_type\": \"histogram\","
"\"units\": [\"us\"]," "\"middle\": \"%d\","
"\"props_type\": \"histogram\"," "\"metadata\": {\"i\": \"%dus\", \"duration\": \"%dh%d\","
"\"middle\": \"%d\"," "\"lost_packets\": \"%d\"},"
"\"metadata\": {\"i\": \"%dus\", \"duration\": \"%dh%d\"," "\"props\": [[",
"\"lost_packets\": \"%d\"}," MAX_JITTER / 2 - min_jitter, interval, duration_hour, duration_minutes,
"\"props\": [[", ((int)ingress_stats.high_jitter));
MAX_JITTER / 2 - min_jitter, interval, duration_hour,
duration_minutes, for (int j = min_jitter; j < max_jitter; j++)
ingress_stats.lost_packets + ((int)ingress_stats.high_jitter)); printf("%" PRIi64 "%s", jitter_hist[j], (j + 1 < max_jitter ? ", " : ""));
printf("]]}]}\n");
for (int j = min_jitter; j < max_jitter; j++)
printf("%" PRIi64 "%s", jitter_hist[j],
(j + 1 < max_jitter ? ", " : ""));
printf("]]}]}\n");
} }
static void sighand(int sig_num) { static void sighand(int sig_num) {
(void)sig_num; (void)sig_num;
if (enable_histograms) { if (enable_histograms) {
print_histograms(); print_histograms();
} }
if (tsn_task == XDP_TASK) { if (tsn_task == XDP_TASK) {
close_xdp_socket(); close_xdp_socket();
} }
if (ingress_stats.lost_packets) if (ingress_stats.high_jitter)
fprintf(stderr, "%d packets were lost\n", fprintf(stderr, "%d packets were lost\n", ((int)ingress_stats.high_jitter));
ingress_stats.lost_packets);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
// Process bash options /* Process bash options
*/
static void process_options(int argc, char *argv[]) { static void process_options(int argc, char *argv[]) {
int network_if_specified = 0; int network_if_specified = 0;
for (;;) { for (;;) {
int c = getopt(argc, argv, "ab:d:f:ghi:p:r:tvx:XT:G"); int c = getopt(argc, argv, "a:b:cd:f:ghi:p:r:tvx:XT:G");
if (c == -1) break; if (c == -1) break;
switch (c) { switch (c) {
case 'a': case 'a':
enable_affinity = 1; thread_params.affinity_cpu = atoi(optarg);
break; break;
case 'b': case 'b':
tsn_task = RTT_TASK; tsn_task = RTT_TASK;
strcpy(egress_params.server_ip, optarg); strcpy(egress_params.server_ip, optarg);
break; break;
case 'd': case 'c':
ingress_params.tx_buffer_len = atoi(optarg); thread_params.emit_signal = 1;
if (ingress_params.tx_buffer_len < 1) { break;
fprintf(stderr, case 'd':
"BUF_LEN should be greater " ingress_params.tx_buffer_len = atoi(optarg);
"than 1\n"); if (ingress_params.tx_buffer_len < 1) {
exit(EXIT_FAILURE); fprintf(stderr, "BUF_LEN should be greater than 1\n");
} exit(EXIT_FAILURE);
break; }
case 'f': break;
network_if_specified = 1; case 'f':
strcpy(ingress_params.network_if, optarg); network_if_specified = 1;
break; strcpy(ingress_params.network_if, optarg);
case 'h': break;
help(argv); case 'h':
exit(EXIT_SUCCESS); help(argv);
break; exit(EXIT_SUCCESS);
case 'i': break;
thread_params.interval = atoi(optarg) * 1000; case 'i':
break; thread_params.interval = atoi(optarg) * 1000;
case 'g': break;
enable_histograms = 1; case 'g':
break; enable_histograms = 1;
case 'p': break;
thread_params.priority = atoi(optarg); case 'p':
break; thread_params.priority = atoi(optarg);
case 'r': break;
main_params.refresh_rate = atoi(optarg); case 'r':
break; main_params.refresh_rate = atoi(optarg);
case 't': break;
enable_timestamps = 1; case 't':
break; enable_timestamps = 1;
case 'v': break;
main_params.verbose = 1; case 'v':
break; main_params.verbose = 1;
case 'x': break;
tsn_task = XDP_TASK; case 'x':
ingress_params.xdp_polling_mode = atoi(optarg); tsn_task = XDP_TASK;
break; ingress_params.xdp_polling_mode = atoi(optarg);
case 'X': break;
main_params.enable_xdp_tracing = 1; case 'X':
break; main_params.enable_xdp_tracing = 1;
case 'T': break;
main_params.enable_tracing = 1; case 'T':
thread_params.latency_threshold = atoi(optarg); main_params.enable_tracing = 1;
break; thread_params.latency_threshold = atoi(optarg);
case 'G': break;
main_params.enable_graph = 1; case 'G':
break; main_params.enable_graph = 1;
} break;
} }
}
if (!network_if_specified) {
fprintf(stderr, "You need to specifiy an network interface\n"); if (!network_if_specified) {
help(argv); fprintf(stderr, "You need to specifiy an network interface\n");
exit(EXIT_FAILURE); help(argv);
} exit(EXIT_FAILURE);
}
if (argc != optind) {
fprintf(stderr, "Too many arguments\n"); if (argc != optind) {
help(argv); fprintf(stderr, "Too many arguments\n");
exit(EXIT_FAILURE); help(argv);
} exit(EXIT_FAILURE);
}
} }
...@@ -5,8 +5,8 @@ script_dir=$(dirname $(realpath $0)) ...@@ -5,8 +5,8 @@ script_dir=$(dirname $(realpath $0))
usage() { usage() {
cat << ENDUSAGE cat << ENDUSAGE
Usage: $0 QDISC_OPT [CLIENT_OPTS] BOARD Usage: $0 QDISC_OPT [CLIENT_OPTS] BOARD
QDISC_OPTS: (-e delta [-o etf_offset] -H | -p) QDISC_OPTS: (-e delta [-o etf_offset] -H | -p) [-q]
CLIENT_OPTS: -bgt -i INTERVAL -I if -d TX_BUF_LEN [TRACE_OPTS] CLIENT_OPTS: -bgt -c DELAY -s NS -i INTERVAL -I if -a CPU -d TX_BUF_LEN [TRACE_OPTS]
TRACE_OPTS: [-T -P TRACER -E EVENTS -B SIZE] TRACE_OPTS: [-T -P TRACER -E EVENTS -B SIZE]
default tracer opts: irq, sched, net_dev_start_xmit, default tracer opts: irq, sched, net_dev_start_xmit,
net_dev_xmit, net_dev_xmit_timeout net_dev_xmit, net_dev_xmit_timeout
...@@ -21,17 +21,24 @@ interval=100000 ...@@ -21,17 +21,24 @@ interval=100000
# Default options # Default options
interface="eth0" interface="eth0"
client_options="-a -p 99" cpu="1"
client_options="-p 99"
qdisc_options="" qdisc_options=""
etf_offset=500 etf_offset=500
tracecmd_events="-e irq -e sched -e net_dev_start_xmit -e net_dev_xmit -e net_dev_xmit_timeout" tracecmd_events="-e irq -e sched -e net_dev_start_xmit -e net_dev_xmit -e net_dev_xmit_timeout"
tracecmd_opts="" tracecmd_opts=""
while getopts "bd:e:o:ghi:ptB:E:I:HP:T" opt; do while getopts "a:bc:d:e:o:ghi:pqs:tB:E:I:HP:T" opt; do
case "${opt}" in case "${opt}" in
a )
cpu=${OPTARG}
;;
b ) b )
client_options+=" -b" client_options+=" -b"
;; ;;
c )
client_options+=" -c ${OPTARG}"
;;
d ) d )
client_options+=" -d ${OPTARG}" client_options+=" -d ${OPTARG}"
;; ;;
...@@ -59,6 +66,12 @@ while getopts "bd:e:o:ghi:ptB:E:I:HP:T" opt; do ...@@ -59,6 +66,12 @@ while getopts "bd:e:o:ghi:ptB:E:I:HP:T" opt; do
client_options+=" -q 1" client_options+=" -q 1"
qdisc_options+="-p" qdisc_options+="-p"
;; ;;
q )
dont_create_qdisc=1
;;
s )
client_options+=" -s ${OPTARG}"
;;
t ) t )
use_timestamps=1 use_timestamps=1
client_options+=" -t" client_options+=" -t"
...@@ -88,14 +101,15 @@ while getopts "bd:e:o:ghi:ptB:E:I:HP:T" opt; do ...@@ -88,14 +101,15 @@ while getopts "bd:e:o:ghi:ptB:E:I:HP:T" opt; do
esac esac
done done
shift $((OPTIND-1)) shift $((OPTIND-1))
if [ -z "$1" ]; then if [ -z "$1" ]; then
usage usage
fi fi
client_options+=" -f $interface"
qdisc_options+=" -I $interface" qdisc_options+=" -I $interface"
client_options+=" -a $cpu"
board_name=$1 board_name=$1
...@@ -131,8 +145,10 @@ fi ...@@ -131,8 +145,10 @@ fi
client_options+=" -i $interval" client_options+=" -i $interval"
echo "create-qdisc $qdisc_options"; if [ -z "$dont_create_qdisc" ]; then
$script_dir/create-qdisc $qdisc_options; echo "create-qdisc $qdisc_options";
$script_dir/create-qdisc $qdisc_options;
fi
echo "make client"; echo "make client";
cd $script_dir/../packet-exchange/build; cd $script_dir/../packet-exchange/build;
...@@ -140,13 +156,13 @@ make client; ...@@ -140,13 +156,13 @@ make client;
cd $script_dir; cd $script_dir;
if [ -n "${use_histogram}" ]; then if [ -n "${use_histogram}" ]; then
echo "client $client_options $board_ip > $output;mv $output ~/"; echo "client $client_options $interface $board_ip > $output;mv $output ~/";
$script_dir/../packet-exchange/build/client $client_options $board_ip > $output; $script_dir/../packet-exchange/build/client $client_options $interface $board_ip > $output;
mv $output ~/; mv $output ~/;
elif [ -n "${use_tracer}" ]; then elif [ -n "${use_tracer}" ]; then
echo "trace-cmd record $tracecmd_opts $tracecmd_events ./client $client_options $board_ip"; echo "trace-cmd record $tracecmd_opts $tracecmd_events ./client $client_options $interface $board_ip";
trace-cmd record $tracecmd_opts $tracecmd_events $script_dir/../packet-exchange/build/client $client_options $board_ip; trace-cmd record $tracecmd_opts $tracecmd_events $script_dir/../packet-exchange/build/client $client_options $interface $board_ip;
else else
echo "client $client_options $board_ip"; echo "client $client_options $interface $board_ip";
$script_dir/../packet-exchange/build/client $client_options $board_ip; $script_dir/../packet-exchange/build/client $client_options $interface $board_ip;
fi fi
#!/bin/bash
linuxptp_dir=/home/oli/linuxptp
opts=""
interface="eth0"
usage() {
echo "Usage: $0 [-r -i IF -o OPTS]" 1>&2;
exit 1;
}
while getopts "i:o:r" opt; do
case "${opt}" in
i )
interface=${OPTARG}
;;
o )
opts+=${OPTARG}
;;
r )
reset=1
;;
* )
usage
;;
esac
done
shift $((OPTIND-1))
opts+="-s $interface"
log=phc2sys_${interface}_log
if [ -n "$reset" ]; then
killall phc2sys;
fi
echo "phc2sys $opts -c CLOCK_REALTIME --step_threshold=1 --transportSpecific=1 -w -m &> ~/$log &";
phc2sys $opts -c CLOCK_REALTIME --step_threshold=1 --transportSpecific=1 -w -m &> ~/$log &
...@@ -6,20 +6,30 @@ telecom_profile="G.8265.1.cfg" ...@@ -6,20 +6,30 @@ telecom_profile="G.8265.1.cfg"
gPTP_profile="gPTP.cfg" gPTP_profile="gPTP.cfg"
profile_name="none" profile_name="none"
opts="" opts=""
interface="eth0"
usage() { usage() {
echo "Usage: $0 [-p telecom|gPTP|OTHER -s -o OPTS]" 1>&2; echo "Usage: $0 [-r -p telecom|gPTP|OTHER -s -H -i IF -o OPTS]" 1>&2;
exit 1; exit 1;
} }
while getopts "o:p:s" opt; do while getopts "i:o:p:rsH" opt; do
case "${opt}" in case "${opt}" in
i )
interface=${OPTARG}
;;
H )
opts+=" -H "
;;
o ) o )
opts+=${OPTARG} opts+=${OPTARG}
;; ;;
p ) p )
profile_name=${OPTARG} profile_name=${OPTARG}
;; ;;
r )
reset=1
;;
s ) s )
opts+=" -s " opts+=" -s "
;; ;;
...@@ -31,6 +41,8 @@ done ...@@ -31,6 +41,8 @@ done
shift $((OPTIND-1)) shift $((OPTIND-1))
opts+="-i $interface"
if [ $profile_name == "none" ]; then if [ $profile_name == "none" ]; then
opts+="" opts+=""
elif [ $profile_name == "telecom" ]; then elif [ $profile_name == "telecom" ]; then
...@@ -41,6 +53,10 @@ else ...@@ -41,6 +53,10 @@ else
opts=" -f $profile_name " opts=" -f $profile_name "
fi fi
killall ptp4l; log=ptp4l_${interface}_log
echo "ptp4l $opts -i eth0 --step_threshold=1 -m -S &> ~/ptp4l_log &";
ptp4l $opts -i eth0 --step_threshold=1 -m -S &> ~/ptp4l_log & if [ -n "$reset" ]; then
killall ptp4l;
fi
echo "ptp4l $opts --step_threshold=1 -m -S &> ~/$log &";
ptp4l $opts --step_threshold=1 -m -S &> ~/$log &
...@@ -9,7 +9,7 @@ usage() { ...@@ -9,7 +9,7 @@ usage() {
usage() { usage() {
cat << ENDUSAGE cat << ENDUSAGE
Usage: $0 [-I if] SERVER | TCPDUMP [TRACE_OPTS] Usage: $0 [-I if] SERVER | TCPDUMP [TRACE_OPTS]
SERVER: [-bt] [(-x | -X) POLL] [-g INTERVAL] SERVER: [-bct] [(-x | -X) POLL] [-g INTERVAL] [-a CPU]
TCPDUMP: -d NB_PACKETS [-i INTERVAL] TCPDUMP: -d NB_PACKETS [-i INTERVAL]
TRACE_OPTS: (-T LATENCY_THRESHOLD -G) | -E LATENCY_THRESHOLD TRACE_OPTS: (-T LATENCY_THRESHOLD -G) | -E LATENCY_THRESHOLD
ENDUSAGE ENDUSAGE
...@@ -20,18 +20,25 @@ ENDUSAGE ...@@ -20,18 +20,25 @@ ENDUSAGE
# Default options # Default options
interface="eth0" interface="eth0"
server_options="-a -p 99" server_options="-p 99"
make_opts="" make_opts=""
ip="10.100.21." ip="10.100.21."
tcpdump_interval=1000000 tcpdump_interval=1000000
tracecmd_events="-e irq -e sched -e xdp -e net -e page_pool -e preemptirq -e napi" tracecmd_events="-e irq -e sched -e xdp -e net -e page_pool -e preemptirq -e napi"
cpu=1
while getopts "b:htx:X:d:i:g:I:T:GE:" opt; do while getopts "a:b:chtx:X:d:i:g:I:T:GE:" opt; do
case "${opt}" in case "${opt}" in
a )
cpu=${OPTARG}
;;
b ) b )
use_rtt=1 use_rtt=1
board_name=${OPTARG} board_name=${OPTARG}
;; ;;
c )
server_options+=" -c"
;;
d ) d )
use_tcpdump=1 use_tcpdump=1
nb_packets=${OPTARG} nb_packets=${OPTARG}
...@@ -80,7 +87,7 @@ done ...@@ -80,7 +87,7 @@ done
shift $((OPTIND-1)) shift $((OPTIND-1))
server_options+=" -f $interface" server_options+=" -f $interface -a $cpu"
if [ -n "${use_rtt}" ]; then if [ -n "${use_rtt}" ]; then
if [ $board_name != "emerald" ] && [ $board_name != "onyx" ] && [ $board_name != "slate" ]; then if [ $board_name != "emerald" ] && [ $board_name != "onyx" ] && [ $board_name != "slate" ]; then
......
#!/bin/bash
usage() {
echo "Usage: $0 [-r -i IF -o OPTS]" 1>&2;
exit 1;
}
while getopts "i:o:r" opt; do
case "${opt}" in
i )
interface=${OPTARG}
;;
o )
opts+=${OPTARG}
;;
r )
reset=1
;;
* )
usage
;;
esac
done
shift $((OPTIND-1))
# Setup irqs
ksoftirq1_pid=$(ps -eL -o pid,cmd | grep "ksoftirqd/1" | awk '{ print $1 }' | head -n1)
ksoftirq2_pid=$(ps -eL -o pid,cmd | grep "ksoftirqd/2" | awk '{ print $1 }' | head -n1)
# Set high priority for ksoftirq deamons
chrt -f -p 97 $ksoftirq1_pid;
chrt -f -p 97 $ksoftirq2_pid;
# Run linux-ptp
echo "Starting ptp4l on master";
killall ptp4l;
./run-ptp4l -i enp1s0 -H;
./run-ptp4l -i enp2s0 -H;
echo "Starting ptp4l on slaves";
./sudossh slate "killall ptp4l";
./sudossh onyx "killall ptp4l";
./sudossh slate "run-ptp4l -s";
./sudossh onyx "run-ptp4l -s";
echo "Waiting for ptp to take effect...";
sleep 10;
# Run phc2sys
./run-phc2sys -i enp1s0;
./run-phc2sys -i enp2s0;
echo "Waiting for phc2sys to take effect...";
sleep 2;
echo "Start servers on slave";
./sudossh slate "setup_irqs";
./sudossh onyx "setup_irqs";
./sudossh slate "killall server";
./sudossh onyx "killall server";
./exec-ssh-nohup slate "run-server -c";
./exec-ssh-nohup onyx "run-server -c";
cd ../gettime/build;
make;
time=$(./gettime -r -q)
echo "CLOCK_REALTIME time is $time";
time+=15000000000
echo "Start clients on master";
cd ../../scripts;
./run-client -a 1 -q -i 1000000 -p -I enp1s0 -c 500000 -s $time slate &> client_enp1s0_log&
./run-client -a 2 -q -i 1000000 -p -I enp2s0 -c 500000 -s $time onyx &> client_enp2s0_log&
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment