Commit 2883853e authored by Joanne Hugé's avatar Joanne Hugé

Update ptp / phc related code

Add GPIO support to software-pps
Update synchronize-phc
Add run-software-pps script
Add sanity check for server
Sleep at beginning of the loop in client
Add sanity check in client too
Add mode to synchronize-phc
Fix mistake in get-ptp-time
Add kill ptp and phc scripts
Add test-ptp
Add option to print time on each line for ptp-get-time
Add nowait option to gettime
Add logging option in test-ptp
Add error correction in test-ptp
Fix encode decode mistake
Don't use strcpy
Fix packet data transfer, add error correction
Add 68us jitter 20mn A20-shuttle signal measure
Update phc script
Change default parameters for test-board-synchro
Update kill scripts to work when called outside of script dir
Update parse-saleae-measure script to include hex exports
parent 39fc5e7a
...@@ -8,6 +8,8 @@ packet-exchange/build/server ...@@ -8,6 +8,8 @@ packet-exchange/build/server
packet-exchange/build/client packet-exchange/build/client
gettime/build/gettime gettime/build/gettime
software-pps/build/software-pps software-pps/build/software-pps
test-ptp/build/server
test-ptp/build/client
scripts/packet-histogram_stop-options* scripts/packet-histogram_stop-options*
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <unistd.h> #include <unistd.h>
#define NSEC_PER_SEC UINT64_C(1000000000) #define NSEC_PER_SEC UINT64_C(1000000000)
#define MAX_BUFFER_SIZE 10000000
// Structs // Structs
...@@ -42,8 +43,13 @@ static uint64_t ts_to_uint(struct timespec t); ...@@ -42,8 +43,13 @@ static uint64_t ts_to_uint(struct timespec t);
static thread_param_t thread_params; static thread_param_t thread_params;
static uint64_t ptptime; static uint64_t ptptime;
static int quit; static int quit;
static int nowait;
static int print_newline;
static int print_period;
static clockid_t clock_id; static clockid_t clock_id;
static uint64_t ts_buffer[MAX_BUFFER_SIZE];
static void help(char *argv[]) { static void help(char *argv[]) {
printf( printf(
"Usage: %s [-h] [-i USEC]\n" "Usage: %s [-h] [-i USEC]\n"
...@@ -52,6 +58,9 @@ static void help(char *argv[]) { ...@@ -52,6 +58,9 @@ static void help(char *argv[]) {
" -r Get time from CLOCK_REALTIME (default)\n" " -r Get time from CLOCK_REALTIME (default)\n"
" -t Get time from CLOCK_TAI\n" " -t Get time from CLOCK_TAI\n"
" -q Get time then quit\n" " -q Get time then quit\n"
" -L Print new line for each value\n"
" -R USEC Print period\n"
" -F Read clock and store in buffer as fast as possible and dump the buffer at the end\n"
" -i USEC RT thread wake-up interval (default: 10ms)\n" " -i USEC RT thread wake-up interval (default: 10ms)\n"
"\n", "\n",
argv[0]); argv[0]);
...@@ -74,13 +83,25 @@ static void *rt_thread(void *p) { ...@@ -74,13 +83,25 @@ static void *rt_thread(void *p) {
clock_gettime(CLOCK_MONOTONIC, &next); clock_gettime(CLOCK_MONOTONIC, &next);
// Time lookup loop // Time lookup loop
for (;;) { for (int i = 0;; i++) {
clock_gettime(clock_id, &ptptime_ts); clock_gettime(clock_id, &ptptime_ts);
ptptime = ts_to_uint(ptptime_ts); ptptime = ts_to_uint(ptptime_ts);
add_ns(&next, thread_params.interval); if(!nowait) {
add_ns(&next, thread_params.interval);
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next, NULL);
}
else {
ts_buffer[i] = ptptime;
if((i+1) >= MAX_BUFFER_SIZE) {
for(int j = 0; j < MAX_BUFFER_SIZE; j++)
printf("%" PRIu64 "\n", ts_buffer[j]);
exit(EXIT_SUCCESS);
}
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next, NULL); }
} }
return NULL; return NULL;
...@@ -99,6 +120,8 @@ int main(int argc, char *argv[]) { ...@@ -99,6 +120,8 @@ int main(int argc, char *argv[]) {
thread_params.interval = 100000 * 1000; thread_params.interval = 100000 * 1000;
thread_params.priority = 98; thread_params.priority = 98;
clock_id = CLOCK_REALTIME; clock_id = CLOCK_REALTIME;
print_newline = 0;
print_period = 100000;
/* 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 */
...@@ -149,9 +172,12 @@ int main(int argc, char *argv[]) { ...@@ -149,9 +172,12 @@ int main(int argc, char *argv[]) {
// Verbose loop // Verbose loop
for (;;) { for (;;) {
usleep(100000); usleep(print_period);
printf("%9" PRIu64 ": \n", ptptime); if(!nowait) {
printf("\033[%dA", 1); printf("%9" PRIu64 "\n", ptptime);
if(!print_newline)
printf("\033[%dA", 1);
}
} }
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
...@@ -161,7 +187,7 @@ int main(int argc, char *argv[]) { ...@@ -161,7 +187,7 @@ int main(int argc, char *argv[]) {
static void process_options(int argc, char *argv[]) { static void process_options(int argc, char *argv[]) {
for (;;) { for (;;) {
int c = getopt(argc, argv, "hi:qmrt"); int c = getopt(argc, argv, "hi:qmrtLR:F");
if (c == -1) break; if (c == -1) break;
...@@ -185,6 +211,15 @@ static void process_options(int argc, char *argv[]) { ...@@ -185,6 +211,15 @@ static void process_options(int argc, char *argv[]) {
case 'q': case 'q':
quit = 1; quit = 1;
break; break;
case 'L':
print_newline = 1;
break;
case 'R':
print_period = atoi(optarg);
break;
case 'F':
nowait = 1;
break;
} }
} }
} }
......
...@@ -64,6 +64,8 @@ no, 0h0, 1000us (10949) | 0.0000us | 239.0000us | 140.4619us ...@@ -64,6 +64,8 @@ no, 0h0, 1000us (10949) | 0.0000us | 239.0000us | 140.4619us
no, 15h1, 1000us (54075815) | 0.0000us | 550.0000us | 299.5232us | 7.1155us | 0 no, 15h1, 1000us (54075815) | 0.0000us | 550.0000us | 299.5232us | 7.1155us | 0
no, 15h0, 500us (108100303) | 0.0000us | 457.0000us | 246.4996us | 7.5832us | 0 no, 15h0, 500us (108100303) | 0.0000us | 457.0000us | 246.4996us | 7.5832us | 0
no, 62h51, 1000us (226318412) | 0.0000us | 586.0000us | 268.5267us | 4.6224us | 0 no, 62h51, 1000us (226318412) | 0.0000us | 586.0000us | 268.5267us | 4.6224us | 0
no, 14h2, 1000us (50560753) | 0.0000us | 418.0000us | 192.4910us | 11.2624us | 0
no, 14h2, 1000us (50562481) | 0.0000us | 399.0000us | 169.4949us | 5.6921us | 0
![alt text](measures/graphs/packet_jitterPacket jitter.png "packet_jitter Graph") ![alt text](measures/graphs/packet_jitterPacket jitter.png "packet_jitter Graph")
...@@ -85,12 +87,12 @@ Minimum | Maximum | Average | Standard deviation | Lost packets ...@@ -85,12 +87,12 @@ Minimum | Maximum | Average | Standard deviation | Lost packets
Two A20 boards are connected end to end with an ethernet cable to a shuttle. All devices are synchronized with PTP. The shuttle sends packets containing timestamps to the boards, and the boards emit a signal at the given timestamp. A logic analyzer measures the variation difference between the signals of the two boards. Two A20 boards are connected end to end with an ethernet cable to a shuttle. All devices are synchronized with PTP. The shuttle sends packets containing timestamps to the boards, and the boards emit a signal at the given timestamp. A logic analyzer measures the variation difference between the signals of the two boards.
**Common test metadata:** Device: A20, Linux kernel version: 5.6, Task priority: 98, Interval: 1000us, Boot Parameters: isolcpus,rcu_nocbs,irqaffinity, ETF qdisc delta: None, Device and processor load: None, Test duration: 0h20, Speed (Mb/s): 1000, ETF offset: 500us, Packet route: E2E, qdisc: pfifo_fast, Client device: A20, XDP: no **Common test metadata:** Device: A20, Linux kernel version: 5.6, Task priority: 98, Interval: 1000us, Boot Parameters: isolcpus,rcu_nocbs,irqaffinity, ETF qdisc delta: None, Device and processor load: None, Test duration: 0h20, Speed (Mb/s): 1000, ETF offset: 500us, Packet route: E2E, qdisc: pfifo_fast, Client device: Shuttle, XDP: no
Minimum | Maximum | Average | Standard deviation | Lost packets Minimum | Maximum | Average | Standard deviation | Lost packets
------------------------------- | ------------------------------- | ------------------------------- | ------------------------------- | ------------ ------------------------------- | ------------------------------- | ------------------------------- | ------------------------------- | ------------
**edge jitter - period jitter** | **edge jitter - period jitter** | **edge jitter - period jitter** | **edge jitter - period jitter** | **edge jitter - period jitter** | **edge jitter - period jitter** | **edge jitter - period jitter** | **edge jitter - period jitter** |
0.0000us - 595.0000us | 216.0000us - 1432.0000us | 6.8536us - 999.5032us | 8.4292us - 45.7510us | 0 0.0000us - 974.0000us | 68.0000us - 1038.0000us | 6.2311us - 999.5382us | 7.9965us - 1.5777us | 0
![alt text](measures/graphs/shuttle_a20_signal_jitteredge jitter.png "shuttle_a20_signal_jitter Graph") ![alt text](measures/graphs/shuttle_a20_signal_jitteredge jitter.png "shuttle_a20_signal_jitter Graph")
......
{"cyclictest_wake-up_latency": {"ids": [3, 5], "next_id": 6}, "shuttle_signal_jitter": {"ids": [0], "next_id": 1}, "packet_jitter": {"ids": [0, 1, 2, 3, 4], "next_id": 5}, "ping_interval": {"ids": [0], "next_id": 1}, "shuttle_a20_signal_jitter": {"ids": [0], "next_id": 1}} {"cyclictest_wake-up_latency": {"ids": [3, 5], "next_id": 6}, "shuttle_signal_jitter": {"ids": [0], "next_id": 1}, "packet_jitter": {"ids": [0, 1, 2, 3, 4, 5, 6], "next_id": 7}, "ping_interval": {"ids": [0], "next_id": 1}, "shuttle_a20_signal_jitter": {"ids": [1], "next_id": 2}}
\ No newline at end of file \ No newline at end of file
{"measure_sets": [{"measure_type": "packet_jitter", "props_names": ["Packet jitter"], "units": ["us"], "middle": 0, "props": [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 2, 0, 2, 0, 0, 1, 2, 0, 2, 0, 1, 1, 2, 1, 2, 3, 6, 1, 4, 13, 9, 11, 21, 20, 29, 34, 46, 46, 57, 71, 109, 126, 131, 169, 189, 220, 268, 275, 299, 299, 317, 354, 411, 390, 415, 457, 452, 427, 441, 485, 503, 522, 466, 493, 520, 449, 515, 507, 527, 486, 544, 531, 480, 532, 543, 521, 592, 568, 600, 658, 713, 759, 792, 855, 856, 918, 1000, 1063, 1102, 1204, 1201, 1283, 1391, 1531, 1655, 1789, 1911, 2160, 2348, 2411, 2729, 2899, 3181, 3463, 3627, 3720, 3899, 3967, 3941, 4022, 4023, 3832, 3767, 3727, 3633, 3608, 3527, 3505, 3585, 3635, 3699, 3735, 3691, 3916, 4054, 4273, 4340, 4413, 4747, 5021, 5544, 6220, 7754, 9955, 15719, 27759, 45877, 53157, 38255, 21822, 15232, 14887, 16356, 18708, 22048, 26228, 31697, 40703, 56029, 81460, 111903, 129296, 133030, 158793, 217361, 258231, 252337, 242746, 257345, 297944, 360375, 458603, 588432, 763775, 1070415, 1624853, 2153920, 1955967, 1170896, 681061, 799046, 1917572, 5349987, 8482733, 5417178, 1517889, 387397, 163570, 147649, 411512, 1161623, 1966590, 1952419, 1369798, 916862, 698339, 560000, 440380, 350904, 290575, 252642, 242449, 259948, 264937, 218348, 163559, 145777, 142029, 118578, 85917, 62291, 49001, 41989, 36521, 32550, 28637, 25665, 22898, 21358, 24453, 39077, 56025, 52835, 34440, 20930, 13957, 10607, 8294, 7153, 6280, 5637, 5525, 5140, 4943, 4737, 4462, 4274, 4059, 3853, 3615, 3419, 3311, 3026, 2842, 2691, 2593, 2419, 2259, 2214, 2061, 2007, 1915, 1940, 1828, 1809, 1777, 1844, 1763, 1809, 1699, 1735, 1766, 1690, 1557, 1539, 1588, 1552, 1647, 1529, 1582, 1705, 1681, 1716, 1734, 1838, 1876, 1938, 1955, 2000, 2020, 1998, 2092, 2036, 2001, 1986, 1994, 1879, 1837, 1660, 1608, 1549, 1376, 1259, 1172, 1165, 1039, 943, 961, 800, 772, 712, 603, 571, 494, 498, 407, 397, 359, 304, 342, 359, 297, 279, 309, 257, 287, 285, 261, 260, 217, 211, 218, 210, 198, 195, 185, 164, 180, 182, 185, 178, 176, 169, 170, 173, 182, 163, 187, 152, 151, 130, 150, 148, 118, 112, 124, 89, 83, 47, 55, 51, 23, 22, 17, 10, 10, 10, 3, 1, 2, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], "props_type": "histogram", "metadata": {"dev": "A20", "ker": "5.6", "prio": "98", "i": "1000us", "boot_p": "isolcpus,rcu_nocbs,irqaffinity", "delta": "None", "load": "None", "duration": "14h2", "speed": "1000", "etf_offset": "500us", "route": "E2E", "qdisc": "pfifo_fast", "client": "Shuttle", "XDP": "no", "lost_packets": "0"}}]}
\ No newline at end of file
{"measure_sets": [{"measure_type": "packet_jitter", "props_names": ["Packet jitter"], "units": ["us"], "middle": 0, "props": [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 2, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 2, 2, 0, 3, 2, 5, 11, 12, 6, 11, 14, 26, 49, 62, 82, 116, 150, 166, 240, 287, 318, 333, 438, 482, 560, 545, 625, 631, 652, 784, 756, 891, 951, 949, 1019, 1052, 1174, 1158, 1263, 1345, 1314, 1460, 1495, 1539, 1609, 1667, 1735, 1766, 1761, 1903, 1869, 1894, 1813, 1873, 1861, 1927, 1917, 1932, 1947, 1896, 1939, 1952, 2004, 2031, 2063, 2181, 2125, 2113, 2141, 2151, 2277, 2366, 2461, 2644, 2639, 3030, 3180, 3544, 3941, 4225, 4740, 5278, 5804, 6491, 7126, 7873, 8760, 9745, 10643, 11338, 12214, 12865, 13979, 15097, 16756, 18636, 20813, 23639, 26388, 30614, 36174, 42745, 52804, 66042, 84070, 108778, 145761, 209566, 328782, 592280, 1323125, 3558947, 8368140, 12154871, 10217858, 6205413, 3236053, 1397915, 580786, 293172, 183589, 133625, 104761, 84672, 70795, 59837, 51392, 45220, 38994, 33418, 28457, 23831, 19982, 16473, 14339, 12128, 10386, 9083, 7988, 7397, 7096, 6658, 6550, 6955, 7476, 8167, 8966, 9863, 10326, 10319, 9970, 9328, 8137, 6836, 5735, 4886, 4077, 3355, 3080, 2736, 2622, 2333, 2341, 2236, 2184, 2037, 1946, 1867, 1724, 1539, 1408, 1382, 1258, 1113, 1077, 979, 950, 905, 880, 890, 869, 879, 839, 877, 858, 837, 848, 857, 817, 853, 862, 832, 906, 864, 911, 833, 880, 910, 998, 996, 1099, 1109, 1200, 1315, 1357, 1394, 1464, 1543, 1592, 1546, 1535, 1456, 1487, 1449, 1430, 1304, 1304, 1206, 1156, 1168, 1060, 1058, 1026, 1099, 1089, 991, 956, 872, 896, 905, 755, 619, 548, 450, 355, 308, 224, 158, 123, 93, 59, 39, 29, 28, 23, 10, 13, 10, 2, 2, 3, 0, 2, 1, 1, 0, 0, 1, 2, 1, 1, 1, 1, 0, 1, 2, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 2, 0, 1, 2, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]], "props_type": "histogram", "metadata": {"dev": "A20", "ker": "5.6", "prio": "98", "i": "1000us", "boot_p": "isolcpus,rcu_nocbs,irqaffinity", "delta": "None", "load": "None", "duration": "14h2", "speed": "1000", "etf_offset": "500us", "route": "E2E", "qdisc": "pfifo_fast", "client": "Shuttle", "XDP": "no", "lost_packets": "0"}}]}
\ No newline at end of file
...@@ -150,6 +150,7 @@ static void *packet_sending_thread(void *p) { ...@@ -150,6 +150,7 @@ static void *packet_sending_thread(void *p) {
} }
next = uint_to_ts(thread_params.start_ts); next = uint_to_ts(thread_params.start_ts);
} }
add_ns(&next, thread_params.interval);
// Set txtime and TX data timestamp if required // Set txtime and TX data timestamp if required
if (enable_etf || thread_params.send_tx_delay) { if (enable_etf || thread_params.send_tx_delay) {
...@@ -170,9 +171,21 @@ static void *packet_sending_thread(void *p) { ...@@ -170,9 +171,21 @@ static void *packet_sending_thread(void *p) {
nb_cycles >= ((unsigned int)thread_params.max_cycles)) nb_cycles >= ((unsigned int)thread_params.max_cycles))
break; break;
clock_gettime(CLOCK_REALTIME, &current);
// Sanity check
if(current.tv_sec > next.tv_sec)
goto invalid_ts;
if(current.tv_sec == next.tv_sec && current.tv_nsec > next.tv_nsec)
goto invalid_ts;
ret = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &next, NULL);
if (ret) {
fprintf(stderr, "clock_nanosleep returned error: %d, aborting...\n", ret);
exit(EXIT_FAILURE);
}
// TX data // TX data
*((uint64_t *)send_data) = encode(thread_params.send_tx_delay ? tx_data : 0xdeadbeef, send_data);
thread_params.send_tx_delay ? tx_data : 0xdeadbeef;
// Get timestamp before TSN task for stats // Get timestamp before TSN task for stats
clock_gettime(CLOCK_REALTIME, &current); clock_gettime(CLOCK_REALTIME, &current);
...@@ -181,7 +194,10 @@ static void *packet_sending_thread(void *p) { ...@@ -181,7 +194,10 @@ static void *packet_sending_thread(void *p) {
// Update txtime and TX data timestamp if required // Update txtime and TX data timestamp if required
if (enable_etf) next_txtime += thread_params.interval; if (enable_etf) next_txtime += thread_params.interval;
if (thread_params.send_tx_delay) tx_data += thread_params.interval; if (thread_params.send_tx_delay) {
tx_data = next.tv_sec * NSEC_PER_SEC + next.tv_nsec;
tx_data += thread_params.send_tx_delay;
}
// Update statistics // Update statistics
if (nb_cycles) { if (nb_cycles) {
...@@ -206,15 +222,15 @@ static void *packet_sending_thread(void *p) { ...@@ -206,15 +222,15 @@ static void *packet_sending_thread(void *p) {
} }
previous = current; previous = current;
ret = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &next, NULL);
if (ret) {
fprintf(stderr, "clock_nanosleep returned error: %d, aborting...\n", ret);
exit(EXIT_FAILURE);
}
} }
pthread_exit(NULL); pthread_exit(NULL);
invalid_ts:
fprintf(stderr, "Invalid timestamp : %" PRIu64 "\n", ts_to_uint(next));
fprintf(stderr, "Current time is : %" PRIu64 "\n", ts_to_uint(current));
fprintf(stderr, "Difference with timestamp : %" PRIi64 "\n\n\n", ((int64_t)ts_to_uint(next)) - ((int64_t)ts_to_uint(current)));
exit(EXIT_FAILURE);
} }
/* /*
......
...@@ -128,3 +128,21 @@ void init_signals(void (*_sighand)(int)) { ...@@ -128,3 +128,21 @@ void init_signals(void (*_sighand)(int)) {
for (int i = 0; i < NSIG; i++) signal(i, sighand_wrapper); for (int i = 0; i < NSIG; i++) signal(i, sighand_wrapper);
} }
static int get_bit(uint64_t t, int i) {
return (t & (((uint64_t) 1) << i)) > 0;
}
void encode(uint64_t t, char * b) {
for(int i = 0; i < 64; i++)
b[i] = get_bit(t, i) * 0xff;
}
uint64_t decode(char * b) {
uint64_t ret = 0;
for(int i = 0; i < 64; i++) {
int sum_bits = 0;
for(int j = 0; j < 8; j++)
sum_bits += (b[i] & (1 << j)) > 0;
//printf("Sum: %d (%x)\n", sum_bits, b[i]);
ret |= (sum_bits >= 4) * (((uint64_t) 1) << i);
}
return ret;
}
...@@ -54,4 +54,7 @@ int histogram_max(uint64_t *histogram, int max_value); ...@@ -54,4 +54,7 @@ int histogram_max(uint64_t *histogram, int max_value);
extern void (*previous_handlers[NSIG])(int); extern void (*previous_handlers[NSIG])(int);
void encode(uint64_t t, char * b);
uint64_t decode(char * b);
#endif #endif
...@@ -201,7 +201,8 @@ void recv_udp_packet() { ...@@ -201,7 +201,8 @@ void recv_udp_packet() {
} }
} }
strcpy(stats->data, rx_buffer); for(int i = 0; i < MAX_BUFFER_SIZE; i++)
stats->data[i] = rx_buffer[i];
} }
#ifdef WITH_XDP #ifdef WITH_XDP
......
...@@ -134,7 +134,8 @@ void send_udp_packet(char *data, uint64_t txtime) { ...@@ -134,7 +134,8 @@ void send_udp_packet(char *data, uint64_t txtime) {
packets_sent++; packets_sent++;
strcpy(tx_buffer, data); for(int i = 0; i < (int)params->tx_buffer_len; i++)
tx_buffer[i] = data[i];
memset(&sin, 0, sizeof(sin)); memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET; sin.sin_family = AF_INET;
......
...@@ -102,6 +102,7 @@ static void help(char *argv[]) { ...@@ -102,6 +102,7 @@ static void help(char *argv[]) {
static void *emit_signal_thread(void *p) { static void *emit_signal_thread(void *p) {
(void)p; (void)p;
cpu_set_t mask; cpu_set_t mask;
struct timespec current;
// Set thread CPU affinity // Set thread CPU affinity
if (thread_params.affinity_cpu) { if (thread_params.affinity_cpu) {
...@@ -114,8 +115,18 @@ static void *emit_signal_thread(void *p) { ...@@ -114,8 +115,18 @@ static void *emit_signal_thread(void *p) {
pthread_mutex_lock(&emit_signal_mutex); pthread_mutex_lock(&emit_signal_mutex);
for (;;) { for (int i = 0;;i++) {
pthread_cond_wait(&emit_signal_ts_received, &emit_signal_mutex); pthread_cond_wait(&emit_signal_ts_received, &emit_signal_mutex);
clock_gettime(CLOCK_REALTIME, &current);
// Sanity check
if(current.tv_sec > emit_signal_next.tv_sec)
goto invalid_ts;
if(current.tv_sec == emit_signal_next.tv_sec && current.tv_nsec > emit_signal_next.tv_nsec)
goto invalid_ts;
if(current.tv_sec + 15 < emit_signal_next.tv_sec)
goto invalid_ts;
clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &emit_signal_next, NULL); clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &emit_signal_next, NULL);
toggle_gpio(); toggle_gpio();
...@@ -123,6 +134,12 @@ static void *emit_signal_thread(void *p) { ...@@ -123,6 +134,12 @@ static void *emit_signal_thread(void *p) {
pthread_mutex_unlock(&emit_signal_mutex); pthread_mutex_unlock(&emit_signal_mutex);
pthread_exit(NULL); pthread_exit(NULL);
invalid_ts:
fprintf(stderr, "Invalid timestamp : %" PRIu64 "\n", ts_to_uint(emit_signal_next));
fprintf(stderr, "Current time is : %" PRIu64 "\n", ts_to_uint(current));
fprintf(stderr, "Difference with timestamp : %" PRIi64 "\n\n\n", ((int64_t)ts_to_uint(emit_signal_next)) - ((int64_t)ts_to_uint(current)));
exit(EXIT_FAILURE);
} }
/* Real-time thread: /* Real-time thread:
...@@ -167,12 +184,20 @@ static void *tsn_thread(void *p) { ...@@ -167,12 +184,20 @@ static void *tsn_thread(void *p) {
// Emit signal // Emit signal
if (thread_params.emit_signal) { if (thread_params.emit_signal) {
uint64_t emit_signal_t = *((uint64_t *)ingress_stats.data); uint64_t emit_signal_t = decode(ingress_stats.data);
pthread_mutex_lock(&emit_signal_mutex); pthread_mutex_lock(&emit_signal_mutex);
emit_signal_next = uint_to_ts(emit_signal_t); emit_signal_next = uint_to_ts(emit_signal_t);
pthread_cond_signal(&emit_signal_ts_received); pthread_cond_signal(&emit_signal_ts_received);
pthread_mutex_unlock(&emit_signal_mutex); pthread_mutex_unlock(&emit_signal_mutex);
// Sanity check
if(current.tv_sec > emit_signal_next.tv_sec)
goto invalid_ts;
if(current.tv_sec == emit_signal_next.tv_sec && current.tv_nsec > emit_signal_next.tv_nsec)
goto invalid_ts;
if(current.tv_sec + 15 < emit_signal_next.tv_sec)
goto invalid_ts;
} }
// Update stats // Update stats
...@@ -217,6 +242,12 @@ static void *tsn_thread(void *p) { ...@@ -217,6 +242,12 @@ static void *tsn_thread(void *p) {
} }
return NULL; return NULL;
invalid_ts:
fprintf(stderr, "Invalid timestamp at reception : %" PRIu64 "\n", ts_to_uint(emit_signal_next));
fprintf(stderr, "Current time is : %" PRIu64 "\n", ts_to_uint(current));
fprintf(stderr, "Difference with timestamp : %" PRIi64 "\n\n\n", ((int64_t)ts_to_uint(emit_signal_next)) - ((int64_t)ts_to_uint(current)));
exit(EXIT_FAILURE);
} }
static void create_thread(void *(*thread_function)(void *)) { static void create_thread(void *(*thread_function)(void *)) {
......
...@@ -27,19 +27,19 @@ while getopts "hls:m:u:n:" opt; do ...@@ -27,19 +27,19 @@ while getopts "hls:m:u:n:" opt; do
;; ;;
s ) s )
new_offset=$((OPTARG * 1000000000)) new_offset=$((OPTARG * 1000000000))
offset+=$((offset + new_offset)) offset=$((offset + new_offset))
;; ;;
m ) m )
new_offset=$((OPTARG * 1000000)) new_offset=$((OPTARG * 1000000))
offset+=$((offset + new_offset)) offset=$((offset + new_offset))
;; ;;
u ) u )
new_offset=$((OPTARG * 1000)) new_offset=$((OPTARG * 1000))
offset+=$((offset + new_offset)) offset=$((offset + new_offset))
;; ;;
n ) n )
new_offset=${OPTARG} new_offset=${OPTARG}
offset+=$((offset + new_offset)) offset=$((offset + new_offset))
;; ;;
* ) * )
usage usage
......
#!/bin/bash
usage() {
cat << ENDUSAGE
Usage: $0 [-h]
Kills all processes started by test-board-synchro (clients and servers)
-h Show help
ENDUSAGE
1>&2;
exit 1;
}
while getopts "h" opt; do
case "${opt}" in
h )
usage
;;
* )
usage
;;
esac
done
shift $((OPTIND-1))
killall phc2sys;
#!/bin/bash
script_dir=$(dirname $(realpath $0))
usage() {
cat << ENDUSAGE
Usage: $0 [-h] BOARD1_HOSTNAME BOARD2_HOSTNAME
Kills all processes started by test-board-synchro (clients and servers)
-h Show help
BOARD_HOSTNAME Uses /etc/hosts to find the IP address associated to the hostname
ENDUSAGE
1>&2;
exit 1;
}
while getopts "h" opt; do
case "${opt}" in
h )
usage
;;
* )
usage
;;
esac
done
shift $((OPTIND-1))
if [ $# -ne 2 ]; then
usage
fi
board1=$1
board2=$2
killall ptp4l;
$script_dir/sudossh $board1 "killall ptp4l";
$script_dir/sudossh $board2 "killall ptp4l";
#!/bin/bash #!/bin/bash
script_dir=$(dirname $(realpath $0))
usage() { usage() {
cat << ENDUSAGE cat << ENDUSAGE
Usage: $0 [-h] BOARD1_HOSTNAME BOARD2_HOSTNAME Usage: $0 [-h] BOARD1_HOSTNAME BOARD2_HOSTNAME
Kills all processes started by test-board-synchro (clients and servers) Kills all processes started by test-board-synchro (clients, servers, software-pps)
-h Show help -h Show help
BOARD_HOSTNAME Uses /etc/hosts to find the IP address associated to the hostname
ENDUSAGE ENDUSAGE
1>&2; 1>&2;
exit 1; exit 1;
} }
while getopts "hc:i:o:rsT" opt; do while getopts "h" opt; do
case "${opt}" in case "${opt}" in
h ) h )
usage usage
...@@ -24,10 +27,14 @@ done ...@@ -24,10 +27,14 @@ done
shift $((OPTIND-1)) shift $((OPTIND-1))
if [ $# -ne 2 ]; then
usage
fi
board1=$1 board1=$1
board2=$2 board2=$2
killall client; killall client;
killall run-client; killall run-client;
./sudossh $board1 "killall server"; $script_dir/sudossh $board1 "killall server software-pps";
./sudossh $board2 "killall server"; $script_dir/sudossh $board2 "killall server software-pps";
...@@ -3,9 +3,14 @@ ...@@ -3,9 +3,14 @@
import sys import sys
import argparse import argparse
def parseLine(l): def parseLine(l, hex_export=False):
(ts, ch0, ch1, *x) = l.split(", ") if hex_export:
(ts, ch, *x) = l.split(", ")
ch = int(ch)
ch0, ch1 = ch & 1, (ch & 2) >> 1
else:
(ts, ch0, ch1, *x) = l.split(", ")
ts = int(float(ts) * 10**9) ts = int(float(ts) * 10**9)
...@@ -53,12 +58,12 @@ def parsePulses(filename): ...@@ -53,12 +58,12 @@ def parsePulses(filename):
print("IO error") print("IO error")
exit() exit()
def parseToggles(filename): def parseToggles(filename, hex_import=False):
with open(filename, "r") as f: with open(filename, "r") as f:
lines = [l for l in f] lines = [l for l in f]
lines = lines[3:] lines = lines[3:]
values = [ parseLine(l) for l in lines] values = [ parseLine(l, hex_import) for l in lines]
values_c1 = [] values_c1 = []
values_c2 = [] values_c2 = []
...@@ -128,12 +133,15 @@ def parse_args(): ...@@ -128,12 +133,15 @@ def parse_args():
parser = argparse.ArgumentParser(description='Measure analysis') parser = argparse.ArgumentParser(description='Measure analysis')
parser.add_argument('-t', nargs=1, required=False, help='import shuttle-a20 json measures') parser.add_argument('-t', nargs=1, required=False, help='import shuttle-a20 json measures')
parser.add_argument('-p', nargs=1, required=False, help='import shuttle json measures') parser.add_argument('-p', nargs=1, required=False, help='import shuttle json measures')
parser.add_argument('-H', action='store_true', required=False, help='Import measures exported with hex values')
args = parser.parse_args() args = parser.parse_args()
hex_export=bool(args.H)
if args.t is not None: if args.t is not None:
filename = args.t[0] filename = args.t[0]
(deltas, periods_c1, periods_c2) = parseToggles(filename) (deltas, periods_c1, periods_c2) = parseToggles(filename, hex_export)
print("# Shuttle-A20 synchronization signal jitter histogram") print("# Shuttle-A20 synchronization signal jitter histogram")
generateHistogram(deltas) generateHistogram(deltas)
generateHistogram(periods_c1 + periods_c2) generateHistogram(periods_c1 + periods_c2)
......
#!/bin/bash
script_dir=$(dirname $(realpath $0))
usage () {
cat << ENDUSAGE
Usage: $0 [-h] [-i USEC -g -s -t MSEC] BOARD1_HOSTNAME BOARD2_HOSTNAME
-h Show help
-i USEC Signal period
-g Use GPIO instead of serial
-t MSEC Set the start timestamp offset
BOARD_HOSTNAME Uses /etc/hosts to find the IP address associated to the hostname
ENDUSAGE
1>&2;
exit 1;
}
interval=1000
pp_opts=""
ts_offset=6000
while getopts "hi:gt:" opt; do
case "$opt" in
h)
usage
;;
i)
interval=${OPTARG}
;;
g)
pps_opts+=" -g "
;;
t )
ts_offset=${OPTARG}
;;
*)
usage
;;
esac
done
shift $((OPTIND-1))
if [ $# -ne 2 ]; then
usage
fi
board1=$1
board2=$2
ts=$($script_dir/get-ptp-time -m $ts_offset)
echo "Timestamp: $ts";
ssh $board1 "cd tsn-measures/software-pps/build;make";
$script_dir/exec-ssh-nohup $board1 "tsn-measures/software-pps/build/software-pps -a1 -p97 -i $interval $pps_opts -s $ts";
ssh $board2 "cd tsn-measures/software-pps/build;make";
$script_dir/exec-ssh-nohup $board2 "tsn-measures/software-pps/build/software-pps -a1 -p97 -i $interval $pps_opts -s $ts";
...@@ -6,17 +6,21 @@ usage() { ...@@ -6,17 +6,21 @@ usage() {
cat << ENDUSAGE cat << ENDUSAGE
Usage: $0 [-h] Usage: $0 [-h]
-h Show help -h Show help
-s use CLOCK_REALTIME as slave (might not work well)
ENDUSAGE ENDUSAGE
1>&2; 1>&2;
exit 1; exit 1;
} }
while getopts "h" opt; do while getopts "hs" opt; do
case "${opt}" in case "${opt}" in
h ) h )
usage usage
;; ;;
s )
rt_slave=1
;;
* ) * )
usage usage
;; ;;
...@@ -27,5 +31,14 @@ shift $((OPTIND-1)) ...@@ -27,5 +31,14 @@ shift $((OPTIND-1))
# Run phc2sys # Run phc2sys
chrt -f 95 phc2sys -m -c /dev/ptp0 -s CLOCK_REALTIME -w &> ~/phc2sys_sys_log &
chrt -f 95 phc2sys -m -c /dev/ptp1 -s CLOCK_REALTIME -w &> ~/phc2sys_sys_log & pmc -u -b 0 -i enp1s0 "SET GRANDMASTER_SETTINGS_NP clockClass 248 clockAccuracy 0xfe offsetScaledLogVariance 0xffff currentUtcOffset 37 leap61 0 leap59 0 currentUtcOffsetValid 1 ptpTimescale 1 timeTraceable 1 frequencyTraceable 0 timeSource 0xa0"
pmc -u -b 0 -i enp2s0 "SET GRANDMASTER_SETTINGS_NP clockClass 248 clockAccuracy 0xfe offsetScaledLogVariance 0xffff currentUtcOffset 37 leap61 0 leap59 0 currentUtcOffsetValid 1 ptpTimescale 1 timeTraceable 1 frequencyTraceable 0 timeSource 0xa0"
if [ -n "$rt_slave" ]; then
chrt -f 95 phc2sys -m -c /dev/ptp0 -s /dev/ptp1 --step_threshold=1 -w &> ~/phc2sys_ptps_log &
chrt -f 95 phc2sys -m -s /dev/ptp0 -c CLOCK_REALTIME --step_threshold=1 -w &> ~/phc2sys_rt_log &
else
chrt -f 95 phc2sys -m -c /dev/ptp0 -s CLOCK_REALTIME --step_threshold=1 -w &> ~/phc2sys_ptp0_log &
chrt -f 95 phc2sys -m -c /dev/ptp1 -s CLOCK_REALTIME --step_threshold=1 -w &> ~/phc2sys_ptp1_log &
fi
...@@ -17,9 +17,9 @@ ENDUSAGE ...@@ -17,9 +17,9 @@ ENDUSAGE
exit 1; exit 1;
} }
interval=500000 interval=1000
server_opts="" server_opts=""
ts_offset=6000 ts_offset=2000
while getopts "hc:i:o:rt:T" opt; do while getopts "hc:i:o:rt:T" opt; do
case "${opt}" in case "${opt}" in
...@@ -57,8 +57,8 @@ if [ -n "$enable_tracing" ]; then ...@@ -57,8 +57,8 @@ if [ -n "$enable_tracing" ]; then
server_opts+=" -T $((interval*2)) -E \"-e irq -e sched -e net -e napi -e gpio\" -B 100000" server_opts+=" -T $((interval*2)) -E \"-e irq -e sched -e net -e napi -e gpio\" -B 100000"
fi fi
./sudossh $board1 "killall server"; $script_dir/sudossh $board1 "killall server";
./sudossh $board2 "killall server"; $script_dir/sudossh $board2 "killall server";
killall client; killall client;
killall run-client; killall run-client;
......
...@@ -4,6 +4,7 @@ SRCDIR = ../src ...@@ -4,6 +4,7 @@ SRCDIR = ../src
SRCS = software-pps.c SRCS = software-pps.c
SRCS += common.c SRCS += common.c
SRCS += pulse.c SRCS += pulse.c
SRCS += gpio.c
OBJS = $(SRCS:%.c=%.o) OBJS = $(SRCS:%.c=%.o)
......
#include "gpio.h"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
static int gpio_state;
static char cmd[128];
static char * one = "1";
static char * zero = "0";
static int fd;
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);
sprintf(cmd, "/sys/class/gpio/gpio%d/value", gpio_index);
fd = open(cmd, O_WRONLY);
}
void toggle_gpio() {
if(gpio_state)
write(fd, one, 1);
else
write(fd, zero, 1);
gpio_state = !gpio_state;
}
#ifndef GPIO_H
#define GPIO_H
void enable_gpio(int gpio_index);
void toggle_gpio();
#endif
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <unistd.h> #include <unistd.h>
#include "pulse.h" #include "pulse.h"
#include "gpio.h"
#include "common.h" #include "common.h"
// Structs // Structs
...@@ -48,6 +49,7 @@ static uint64_t nb_cycles; ...@@ -48,6 +49,7 @@ 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 int use_gpio;
static void help(char *argv[]) { static void help(char *argv[]) {
printf( printf(
...@@ -59,6 +61,7 @@ static void help(char *argv[]) { ...@@ -59,6 +61,7 @@ static void help(char *argv[]) {
" -r USEC non-RT main thread refresh interval\n" " -r USEC non-RT main thread refresh interval\n"
" -l N_CYCLES RT thread cycles amount (default: infinite)\n" " -l N_CYCLES RT thread cycles amount (default: infinite)\n"
" -s NS Common start time reference\n" " -s NS Common start time reference\n"
" -g Use GPIO instead of serial\n"
" -v Verbose\n" " -v Verbose\n"
"\n", "\n",
argv[0]); argv[0]);
...@@ -103,7 +106,11 @@ static void *pps_thread(void *p) { ...@@ -103,7 +106,11 @@ static void *pps_thread(void *p) {
break; break;
send_pulse(); if(use_gpio)
toggle_gpio();
else
send_pulse();
add_ns(&next, thread_params.interval); add_ns(&next, thread_params.interval);
ret = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &next, NULL); ret = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &next, NULL);
...@@ -134,6 +141,7 @@ int main(int argc, char *argv[]) { ...@@ -134,6 +141,7 @@ int main(int argc, char *argv[]) {
thread_params.start_ts = 0; thread_params.start_ts = 0;
main_params.refresh_rate = 50000; main_params.refresh_rate = 50000;
main_params.verbose = 0; main_params.verbose = 0;
use_gpio = 0;
// 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
...@@ -145,7 +153,10 @@ int main(int argc, char *argv[]) { ...@@ -145,7 +153,10 @@ int main(int argc, char *argv[]) {
// Process bash options // Process bash options
process_options(argc, argv); process_options(argc, argv);
enable_pulse(); if(use_gpio)
enable_gpio(86);
else
enable_pulse();
// Initialize pthread attributes (default values) // Initialize pthread attributes (default values)
if (pthread_attr_init(&attr)) { if (pthread_attr_init(&attr)) {
...@@ -198,7 +209,7 @@ int main(int argc, char *argv[]) { ...@@ -198,7 +209,7 @@ int main(int argc, char *argv[]) {
*/ */
static void process_options(int argc, char *argv[]) { static void process_options(int argc, char *argv[]) {
for (;;) { for (;;) {
int c = getopt(argc, argv, "a:hi:l:p:r:s:v"); int c = getopt(argc, argv, "a:hgi:l:p:r:s:v");
if (c == -1) break; if (c == -1) break;
...@@ -210,6 +221,9 @@ static void process_options(int argc, char *argv[]) { ...@@ -210,6 +221,9 @@ static void process_options(int argc, char *argv[]) {
help(argv); help(argv);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
break; break;
case 'g':
use_gpio = 1;
break;
case 'i': case 'i':
thread_params.interval = strtoull(optarg, NULL, 10) * 1000; thread_params.interval = strtoull(optarg, NULL, 10) * 1000;
break; break;
......
SERVER_PROG = server
CLIENT_PROG = client
SRCDIR = ../src
SERVER_SRCS = server.c
SERVER_SRCS += recv_packet.c
SERVER_SRCS += send_packet.c
SERVER_SRCS += common.c
SERVER_SRCS += tracer.c
SERVER_SRCS += gpio.c
CLIENT_SRCS = client.c
CLIENT_SRCS += recv_packet.c
CLIENT_SRCS += send_packet.c
CLIENT_SRCS += common.c
CLIENT_SRCS += tracer.c
SERVER_OBJS = $(SERVER_SRCS:%.c=%.o)
CLIENT_OBJS = $(CLIENT_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
ifneq ($(WITH_XDP),)
CFLAGS += -D WITH_XDP
LDFLAGS += -L/usr/lib -lbpf
else ifneq ($(WITH_GIT_XDP),)
IFLAGS += -I ${HOME}/libbpf/include
CFLAGS += -D WITH_XDP $(IFLAGS)
LDIRS += -L${HOME}/libbpf/src
LLIBS += -lelf -lz -l:libbpf.a
endif
vpath %.c $(SRCDIR)
xdp_kern.o: xdp_kern.c
clang $(IFLAGS) -isystem /usr/include/arm-linux-gnueabihf -S -target bpf -D __BPF_TRACING__ -Wall -O2 -emit-llvm -c -g -o xdp_kern.ll $^
llc -march=bpf -filetype=obj -o $@ xdp_kern.ll
ifneq ($(WITH_GIT_XDP),)
$(SERVER_PROG): $(SERVER_OBJS) xdp_kern.o
$(CC) $(LDFLAGS) $(LDIRS) $(SERVER_OBJS) $(LLIBS) -o $@
else
$(SERVER_PROG): $(SERVER_OBJS)
$(CC) $(LDFLAGS) $(LDIRS) $^ $(LLIBS) -o $@
endif
$(CLIENT_PROG): $(CLIENT_OBJS)
$(CC) $(LDFLAGS) $(LDIRS) $^ $(LLIBS) -o $@
-include $(subst .c,.d,$(SERVER_SRCS))
-include $(subst .c,.d,$(CLIENT_SRCS))
clean:
$(RM) $(SERVER_OBJS) $(SERVER_PROG) $(subst .c,.d,$(SERVER_SRCS))
$(RM) $(CLIENT_OBJS) $(CLIENT_PROG) $(subst .c,.d,$(CLIENT_SRCS))
.PHONY: clean
This diff is collapsed.
This diff is collapsed.
#define _GNU_SOURCE
#include "common.h"
#include <errno.h>
#include <error.h>
#include <fcntl.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 <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
static int latency_target_fd = -1;
static int32_t latency_target_value = 0;
void (*previous_handlers[NSIG])(int);
static void (*sighand)(int);
uint64_t ts_to_uint(struct timespec t) {
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;
}
/* Latency trick
* if the file /dev/cpu_dma_latency exists,
* open it and write a zero into it. This will tell
* the power management system not to transition to
* a high cstate (in fact, the system acts like idle=poll)
* When the fd to /dev/cpu_dma_latency is closed, the behavior
* goes back to the system default.
*
* Documentation/power/pm_qos_interface.txt
*/
void set_latency_target(void) {
struct stat s;
int err;
errno = 0;
err = stat("/dev/cpu_dma_latency", &s);
if (err == -1) {
error(EXIT_FAILURE, errno, "WARN: stat /dev/cpu_dma_latency failed");
return;
}
errno = 0;
latency_target_fd = open("/dev/cpu_dma_latency", O_RDWR);
if (latency_target_fd == -1) {
error(EXIT_FAILURE, errno, "WARN: open /dev/cpu_dma_latency");
return;
}
errno = 0;
err = write(latency_target_fd, &latency_target_value, 4);
if (err < 1) {
error(EXIT_FAILURE, errno, "# error setting cpu_dma_latency to %d!",
latency_target_value);
close(latency_target_fd);
return;
}
printf("# /dev/cpu_dma_latency set to %dus\n", latency_target_value);
}
void add_ns(struct timespec *t, uint64_t ns) {
t->tv_nsec += ns;
while (t->tv_nsec >= ((int64_t)NSEC_PER_SEC)) {
t->tv_sec += 1;
t->tv_nsec -= NSEC_PER_SEC;
}
}
uint64_t calcdiff_ns(struct timespec t1, struct timespec t2) {
uint64_t diff;
diff = NSEC_PER_SEC * (uint64_t)((int)t1.tv_sec - (int)t2.tv_sec);
diff += ((int)t1.tv_nsec - (int)t2.tv_nsec);
return diff;
}
int64_t _max_(int64_t a, int64_t b) { return a > b ? a : b; }
int64_t _min_(int64_t a, int64_t b) { return a < b ? a : b; }
int histogram_min(uint64_t *histogram, int max_value) {
int ret = max_value;
for (int i = max_value; i >= 0; i--) ret = histogram[i] ? i : ret;
return ret;
}
int histogram_max(uint64_t *histogram, int max_value) {
int ret = 0;
for (int i = 0; i <= max_value; i++) ret = histogram[i] ? i : ret;
return ret;
}
static void sighand_wrapper(int sig) {
// If we get un unexpected signal, report it, if not print the histogram
if (sig == SIGINT || sig == SIGTERM)
(*sighand)(sig); // Will print the histogram
else
printf("Uknown signal interrupt: %s (%d)\n", strsignal(sig), sig);
// Execute the default handler
if (previous_handlers[sig] == SIG_DFL) {
signal(sig, SIG_DFL);
raise(sig);
} else if (previous_handlers[sig] == SIG_IGN) {
return;
} else {
(*previous_handlers[sig])(sig);
}
}
void init_signals(void (*_sighand)(int)) {
sighand = _sighand;
for (int i = 0; i < NSIG; i++) signal(i, sighand_wrapper);
}
static int get_bit(uint64_t t, int i) {
return (t & (((uint64_t) 1) << i)) > 0;
}
void encode(uint64_t t, char * b) {
for(int i = 0; i < 64; i++)
b[i] = get_bit(t, i) * 0xff;
}
uint64_t decode(char * b) {
uint64_t ret = 0;
for(int i = 0; i < 64; i++) {
int sum_bits = 0;
for(int j = 0; j < 8; j++)
sum_bits += (b[i] & (1 << j)) > 0;
//printf("Sum: %d (%x)\n", sum_bits, b[i]);
ret |= (sum_bits >= 4) * (((uint64_t) 1) << i);
}
return ret;
}
#ifndef UTILITIES_H
#define UTILITIES_H
#define _GNU_SOURCE
#include <inttypes.h>
#include <signal.h>
#include <stdint.h>
#include <time.h>
#include <unistd.h>
#ifdef WITH_XDP
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <bpf/xsk.h>
#include <linux/if_ether.h>
#include <linux/if_link.h>
#include <linux/if_xdp.h>
#include <linux/ip.h>
#include <linux/udp.h>
#endif
#define NSEC_PER_SEC UINT64_C(1000000000)
#define SERVER_PORT "50000"
#define SERVER_PORT_INT 50000
#define MAX_KERNEL_LATENCY 1000
#define MAX_RTT 1000
#define MAX_JITTER 1000
#define MAX_BUFFER_SIZE 1024
#define TIMESTAMP_BUFFER_SIZE 4096
#define err(...) \
do { \
fprintf(stderr, __VA_ARGS__); \
exit(EXIT_FAILURE); \
} while (0)
#define err_errno(...) error(EXIT_FAILURE, errno, __VA_ARGS__);
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);
uint64_t calcdiff_ns(struct timespec t1, struct timespec t2);
void set_latency_target(void);
void init_signals(void (*_sighand)(int));
int64_t _min_(int64_t a, int64_t b);
int64_t _max_(int64_t a, int64_t b);
int histogram_min(uint64_t *histogram, int max_value);
int histogram_max(uint64_t *histogram, int max_value);
extern void (*previous_handlers[NSIG])(int);
void encode(uint64_t t, char * b);
uint64_t decode(char * b);
#endif
#include "gpio.h"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
static int gpio_state;
static char cmd[128];
static char * one = "1";
static char * zero = "0";
static int fd;
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);
sprintf(cmd, "/sys/class/gpio/gpio%d/value", gpio_index);
fd = open(cmd, O_WRONLY);
}
void toggle_gpio() {
if(gpio_state)
write(fd, one, 1);
else
write(fd, zero, 1);
gpio_state = !gpio_state;
}
#ifndef GPIO_H
#define GPIO_H
void enable_gpio(int gpio_index);
void toggle_gpio();
#endif
This diff is collapsed.
#ifndef RECV_PACKET
#define RECV_PACKET
#include "common.h"
typedef struct ingress_param {
char network_if[16];
int use_timestamps;
int xdp_polling_mode;
uint64_t interval;
size_t tx_buffer_len;
} ingress_param_t;
typedef struct ingress_stat {
int min_kernel_latency;
int avg_kernel_latency;
int max_kernel_latency;
int min_interval;
int avg_interval;
int max_interval;
uint64_t packets_received;
uint64_t high_kernel_latency;
uint64_t high_jitter;
char data[MAX_BUFFER_SIZE];
} ingress_stat_t;
void init_udp_recv(ingress_param_t *_params, ingress_stat_t *stats,
int use_histogram, uint64_t *_kernel_latency_hist);
void recv_udp_packet(void);
#ifdef WITH_XDP
#define NUM_FRAMES 4096
#define FRAME_SIZE XSK_UMEM__DEFAULT_FRAME_SIZE
struct xsk_umem_info {
struct xsk_ring_prod fq;
struct xsk_ring_cons cq;
struct xsk_umem *umem;
void *buffer;
};
struct xdpsock {
struct xsk_ring_cons rx;
struct xsk_ring_prod tx;
struct xsk_umem_info umem;
struct xsk_socket *xsk;
int fd;
};
#endif
void init_xdp_recv(ingress_param_t * _params);
int recv_xdp_packet(void);
void recv_xdp_cleanup(void);
void setup_poll_fd(void);
void close_xdp_socket(void);
#endif
/*
*
* UDP packet sending functions
*
* Large portions taken from scheduled tx tools gist
*
*/
#define _GNU_SOURCE
#include "send_packet.h"
#include <arpa/inet.h>
#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <ifaddrs.h>
#include <inttypes.h>
#include <linux/errqueue.h>
#include <linux/ethtool.h>
#include <linux/net_tstamp.h>
#include <linux/sockios.h>
#include <net/if.h>
#include <netdb.h>
#include <netinet/in.h>
#include <poll.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "common.h"
#include "tracer.h"
static void *poll_thread(void *p);
static void process_error_queue();
static void init_tx_buffer();
static int set_if();
static int so_timestamping_flags =
SOF_TIMESTAMPING_TX_SOFTWARE | SOF_TIMESTAMPING_SOFTWARE;
static egress_param_t *params;
static egress_stat_t *stats;
static uint64_t *kernel_latency_hist;
static int use_histogram;
static int stop_tracing;
static uint64_t packets_sent = 0;
static struct sock_txtime sk_txtime;
static char *tx_buffer;
static int sock_fd;
static int64_t timestamps_buffer[TIMESTAMP_BUFFER_SIZE];
static int ts_buf_read_index = 0;
static int ts_buf_write_index = 0;
static int log_timestamps = 0;
static FILE * log_file;
/*
* Init UDP socket
*/
void init_udp_send(egress_param_t *_params, egress_stat_t *_stats,
int _use_histogram, int _stop_tracing, int _log_timestamps, uint64_t *_kernel_latency_hist) {
int set_if_err;
pthread_t thread;
params = _params;
stats = _stats;
kernel_latency_hist = _kernel_latency_hist;
use_histogram = _use_histogram;
stop_tracing = _stop_tracing;
log_timestamps = _log_timestamps;
if(log_timestamps)
log_file = fopen("timestamps_log", "w+");
init_tx_buffer();
sock_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock_fd < 0) error(EXIT_FAILURE, errno, "Socket creation failed\n");
set_if_err = set_if();
if (set_if_err < 0) error(EXIT_FAILURE, errno, "Couldn't set interface\n");
if (setsockopt(sock_fd, SOL_SOCKET, SO_PRIORITY, &params->packet_priority,
sizeof(params->packet_priority)))
error(EXIT_FAILURE, errno, "Couldn't set socket priority\n");
if (setsockopt(sock_fd, SOL_SOCKET, SO_BINDTODEVICE, params->network_if,
strlen(params->network_if)))
error(EXIT_FAILURE, errno, "setsockopt SO_BINDTODEVICE failed\n");
if (params->use_etf) {
sk_txtime.clockid = CLOCK_REALTIME;
sk_txtime.flags = SOF_TXTIME_REPORT_ERRORS;
if (setsockopt(sock_fd, SOL_SOCKET, SO_TXTIME, &sk_txtime,
sizeof(sk_txtime)))
error(EXIT_FAILURE, errno, "setsockopt SO_TXTIME failed\n");
}
if (params->use_timestamps) {
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &so_timestamping_flags,
sizeof(so_timestamping_flags)))
error(EXIT_FAILURE, errno, "setsockopt SO_TIMESTAMPING failed\n");
}
// Create poll thread
if (pthread_create(&thread, NULL, poll_thread, NULL))
error(EXIT_FAILURE, errno, "Couldn't create poll thread");
}
/*
* Sends udp packets
*/
void send_udp_packet(char *data, uint64_t txtime) {
struct msghdr msg; // Message hardware, sent to the socket
struct cmsghdr *cmsg; // Control message hardware, for txtime
char control[CMSG_SPACE(sizeof(txtime))] = {}; // Stores txtime
struct iovec iov; // The iovec structures stores the TX buffer
int sendmsgerr;
struct sockaddr_in sin; // Server address
struct timespec ts; // timestamp for userspace timestamping
if (params->use_timestamps) {
clock_gettime(CLOCK_REALTIME, &ts);
timestamps_buffer[ts_buf_write_index] = ts_to_uint(ts);
ts_buf_write_index = (ts_buf_write_index + 1) % TIMESTAMP_BUFFER_SIZE;
}
packets_sent++;
clock_gettime(CLOCK_REALTIME, &ts);
encode(ts_to_uint(ts), tx_buffer);
if(log_timestamps) {
fprintf(log_file, "%" PRIi64 "\n", ts_to_uint(ts));
}
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr(params->server_ip);
sin.sin_port = htons(SERVER_PORT_INT);
iov.iov_base = tx_buffer;
iov.iov_len = params->tx_buffer_len;
memset(&msg, 0, sizeof(msg));
msg.msg_name = &sin;
msg.msg_namelen = sizeof(sin);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
if (params->use_etf) {
// We specify the transmission time in the CMSG.
msg.msg_control = control;
msg.msg_controllen = sizeof(control);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_TXTIME;
cmsg->cmsg_len = CMSG_LEN(sizeof(uint64_t));
*((uint64_t *)CMSG_DATA(cmsg)) = txtime;
msg.msg_controllen = cmsg->cmsg_len;
}
sendmsgerr = sendmsg(sock_fd, &msg, 0);
if (sendmsgerr < 0)
error(EXIT_FAILURE, errno, "sendmsg failed, ret value: %d\n", sendmsgerr);
}
static void *poll_thread(void *p) {
(void)p;
// Poll file descriptor
struct pollfd poll_fd = {.fd = sock_fd};
while (1) {
int ret;
ret = poll(&poll_fd, 1, -1);
if (ret == 1 && poll_fd.revents & POLLERR) {
process_error_queue();
}
}
return NULL;
}
static void process_error_queue() {
int recv_ret;
// IO vector
unsigned char data_buffer[256]; // Buffer in io vector
struct iovec iov = {.iov_base = data_buffer, .iov_len = sizeof(data_buffer)};
// Control data, will store error or timestamps
unsigned char msg_control[CMSG_SPACE(sizeof(struct sock_extended_err)) +
CMSG_SPACE(sizeof(struct timespec))];
// Message hardware structure, containts IO vector and control message
// hardware
struct msghdr msg = {.msg_iov = &iov,
.msg_iovlen = 1,
.msg_control = msg_control,
.msg_controllen = sizeof(msg_control)};
struct cmsghdr *cmsg;
// Timestamps and errors are received in the error queue
recv_ret = recvmsg(sock_fd, &msg, MSG_ERRQUEUE | MSG_DONTWAIT);
if (recv_ret == -1) {
fprintf(stderr, "recvmsg() failed\n");
return;
}
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
// If a timestamp was received
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_TIMESTAMPING) {
struct timespec *stamp = (struct timespec *)CMSG_DATA(cmsg);
int kernel_latency =
(ts_to_uint(*stamp) - timestamps_buffer[ts_buf_read_index]) / 1000;
ts_buf_read_index = (ts_buf_read_index + 1) % TIMESTAMP_BUFFER_SIZE;
stats->min_kernel_latency =
_min_(kernel_latency, stats->min_kernel_latency);
stats->max_kernel_latency =
_max_(kernel_latency, stats->max_kernel_latency);
stats->avg_kernel_latency =
(stats->max_kernel_latency * packets_sent + kernel_latency) /
(packets_sent + 1);
if (use_histogram) {
if (kernel_latency > MAX_KERNEL_LATENCY)
stats->high_kernel_latency++;
else
kernel_latency_hist[kernel_latency]++;
}
}
// If an error was received
else {
struct sock_extended_err *serr = (void *)CMSG_DATA(cmsg);
if (serr->ee_origin != SO_EE_ORIGIN_TXTIME) continue;
switch (serr->ee_code) {
case SO_EE_CODE_TXTIME_INVALID_PARAM:
stats->invalid_parameter++;
break;
case SO_EE_CODE_TXTIME_MISSED:
if(stop_tracing) {
tracemark("Deadline missed\n");
tracing(0);
exit(EXIT_FAILURE);
}
stats->missed_deadline++;
break;
default:
fprintf(stderr, "Uknown TxTime error\n");
}
}
}
}
// Sets the interface
static int set_if() {
struct ifreq ifreq;
memset(&ifreq, 0, sizeof(ifreq));
strncpy(ifreq.ifr_name, params->network_if, sizeof(ifreq.ifr_name) - 1);
if (ioctl(sock_fd, SIOCGIFINDEX, &ifreq))
error(EXIT_FAILURE, errno, "ioctl SIOCGIFINDEX failed\n");
return ifreq.ifr_ifindex;
}
static void init_tx_buffer() {
if (params->tx_buffer_len < 1) {
fprintf(stderr, "tx buffer length should be greater than 1\n");
exit(EXIT_FAILURE);
}
tx_buffer = malloc(params->tx_buffer_len);
}
#ifndef SEND_PACKET_H
#define SEND_PACKET_H
#include "common.h"
typedef struct egress_param {
int packet_priority;
size_t tx_buffer_len;
char server_ip[45];
char network_if[16];
int use_etf;
int use_timestamps;
} egress_param_t;
typedef struct egress_stat {
uint64_t high_kernel_latency;
uint64_t invalid_parameter;
uint64_t missed_deadline;
int min_kernel_latency;
int avg_kernel_latency;
int max_kernel_latency;
int min_interval;
int avg_interval;
int max_interval;
} egress_stat_t;
void init_udp_send(egress_param_t *_params,
egress_stat_t *_stats,
int _use_histogram, int _stop_tracing, int log_timestamps,
uint64_t *_kernel_latency_hist);
void send_udp_packet(char *data, uint64_t txtime);
#endif
This diff is collapsed.
#include "tracer.h"
#define _GNU_SOURCE
#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <getopt.h>
#include <limits.h>
#include <linux/unistd.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/sysinfo.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <time.h>
#include <unistd.h>
/*
* From cyclictest code source
*/
#define KVARS 32
#define KVARNAMELEN 32
#define KVALUELEN 32
#define MAX_PATH 255
static char *fileprefix;
static char *procfileprefix = "/proc/sys/kernel/";
static char *debugfileprefix = "/sys/kernel/debug/tracing/";
static int trace_fd = -1;
static int tracemark_fd = -1;
static int kernvar(int mode, const char *name, char *value,
size_t sizeofvalue) {
char filename[128];
int retval = 1;
int path;
size_t len_prefix = strlen(fileprefix), len_name = strlen(name);
memcpy(filename, fileprefix, len_prefix);
memcpy(filename + len_prefix, name, len_name + 1);
path = open(filename, mode);
if (path >= 0) {
if (mode == O_RDONLY) {
int got;
if ((got = read(path, value, sizeofvalue)) > 0) {
retval = 0;
value[got - 1] = '\0';
}
} else if (mode == O_WRONLY) {
if (write(path, value, sizeofvalue) == sizeofvalue) retval = 0;
}
close(path);
}
return retval;
}
static void setkernvar(const char *name, char *value) {
if (kernvar(O_WRONLY, name, value, strlen(value)))
fprintf(stderr, "could not set %s to %s\n", name, value);
}
void open_fds(void) {
fileprefix = debugfileprefix;
char trace_path[MAX_PATH];
char tracemark_path[MAX_PATH];
// Open tracing_on file
strcpy(trace_path, fileprefix);
strcat(trace_path, "tracing_on");
if ((trace_fd = open(trace_path, O_WRONLY)) == -1)
printf("unable to open %s for tracing", trace_path);
// Open trace mark file
strcpy(tracemark_path, fileprefix);
strcat(tracemark_path, "trace_marker");
if ((tracemark_fd = open(tracemark_path, O_WRONLY)) == -1)
printf("unable to open %s for tracing", tracemark_path);
}
void setup_tracer(int enable_graph) {
fileprefix = procfileprefix;
setkernvar("ftrace_enabled", "1");
fileprefix = debugfileprefix;
// Clear old traces by setting tracer to nop first
setkernvar("current_tracer", "nop");
if(enable_graph)
setkernvar("current_tracer", "function_graph");
else
setkernvar("current_tracer", "function");
open_fds();
tracing(0);
}
void tracing(int on) {
if (on)
write(trace_fd, "1", 1);
else
write(trace_fd, "0", 1);
}
void tracemark(char * s) {
write(tracemark_fd, s, strlen(s));
}
#ifndef TRACER_H
#define TRACER_H
void setup_tracer(int enable_graph);
void open_fds(void);
void tracing(int on);
void tracemark(char * s);
#endif
#define KBUILD_MODNAME "blub"
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/if_link.h>
#include <linux/if_xdp.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include "bpf_helpers.h"
#define bpf_printk(fmt, ...) \
({ \
char ____fmt[] = fmt; \
bpf_trace_printk(____fmt, sizeof(____fmt), ##__VA_ARGS__); \
})
struct bpf_map_def SEC("maps") xsks_map = {
.type = BPF_MAP_TYPE_XSKMAP,
.key_size = sizeof(int),
.value_size = sizeof(int),
.max_entries = 64,
};
static inline int parse_ipv4(void *data, unsigned long long nh_off,
void *data_end) {
struct iphdr *iph = data + nh_off;
if ((void *)(iph + 1) > data_end) return 0;
return iph->protocol;
}
SEC("xdp_sock")
int xdp_sock_prog(struct xdp_md *ctx) {
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct ethhdr *eth = data;
int idx = ctx->rx_queue_index;
unsigned int ipproto = 0;
unsigned long long nh_off;
/* Check if it's a UDP frame: If UDP -> Redirect to active xsk for user
* space. If not -> pass to stack.
*/
nh_off = sizeof(*eth);
if (data + nh_off > data_end) return XDP_PASS;
if (eth->h_proto == __builtin_bswap16(ETH_P_IP))
ipproto = parse_ipv4(data, nh_off, data_end);
if (ipproto != IPPROTO_UDP) return XDP_PASS;
/* If socket bound to rx_queue then redirect to user space */
if (bpf_map_lookup_elem(&xsks_map, &idx))
return bpf_redirect_map(&xsks_map, idx, 0);
/* Else pass to Linux' network stack */
return XDP_PASS;
}
char _license[] SEC("license") = "GPL";
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