{"app_name":"Debian Janitor","binary_packages":["rtc-testbench-dbgsym","rtc-testbench"],"branch_url":"git+ssh://git@salsa.debian.org/debian/rtc-testbench.git","build_distribution":"sid","build_log_highlight_lines":[],"build_log_line_count":2988,"build_log_name":"build.log","campaign":"control","change_set":"b2fbc97b-e394-4ed9-ae82-73a77c64de87","codebase":"rtc-testbench","codemod_log_name":"codemod.log","command":"brz up '--revision=git-v1:42aeb05bb69f8dad55b6644988cc53173d3c059d'","current_year":"2026","description":"","is_admin":false,"lintian_result":{"groups":[{"group_id":"rtc-testbench_5.4-1~jan+control1","input_files":[{"hints":[],"path":"rtc-testbench-dbgsym_5.4-1~jan+control1_amd64.deb"},{"hints":[{"experimental":false,"note":"","pointer":{"item":{"index":"rtc-testbench_5.4-1~jan+control1.dsc (patched)","name":"debian/changelog"},"line_position":1},"tag":"no-nmu-in-changelog","visibility":"warning"},{"experimental":false,"note":"5.4-1~jan+control1","pointer":{"item":{"index":"rtc-testbench_5.4-1~jan+control1.dsc (patched)","name":"debian/changelog"},"line_position":1},"tag":"source-nmu-has-incorrect-version-number","visibility":"warning"}],"path":"rtc-testbench_5.4-1~jan+control1.dsc"},{"hints":[],"path":"rtc-testbench_5.4-1~jan+control1_amd64.buildinfo"},{"hints":[{"experimental":false,"note":"sid control","tag":"distribution-and-changes-mismatch","visibility":"warning"}],"path":"rtc-testbench_5.4-1~jan+control1_amd64.changes"},{"hints":[{"experimental":false,"note":"control != sid","pointer":{"item":{"index":"rtc-testbench_5.4-1~jan+control1_amd64.deb (installed)","name":"usr/share/doc/rtc-testbench/changelog.Debian.gz"},"line_position":1},"tag":"changelog-distribution-does-not-match-changes-file","visibility":"warning"}],"path":"rtc-testbench_5.4-1~jan+control1_amd64.deb"}],"source_name":"rtc-testbench","source_version":"5.4-1~jan+control1"}],"lintian_version":"2.143.0"},"logs":{"build":{"description":"build","name":"build.log","url":"/control/c/rtc-testbench/b2fbc97b-e394-4ed9-ae82-73a77c64de87/logs/build.log"},"codemod":{"description":"codemod","name":"codemod.log","url":"/control/c/rtc-testbench/b2fbc97b-e394-4ed9-ae82-73a77c64de87/logs/codemod.log"},"worker":{"content":"[2026-11-08T05:47:49Z INFO  janitor_worker] Opening branch at https://salsa.debian.org/debian/rtc-testbench.git\n[2026-11-08T05:47:51Z INFO  janitor_worker] Cached branch URL http://janitor.debian.net/git/rtc-testbench missing: http://janitor.debian.net/git/rtc-testbench/\n[2026-11-08T05:47:51Z DEBUG janitor_worker] Workspace path: /srv/worker/output/1/janitor-worker-glf6y9/rtc-testbench\n[2026-11-08T05:47:51Z DEBUG silver_platter::workspace] Creating sprout from git+ssh://git@salsa.debian.org/debian/rtc-testbench.git\nTotal 1159 (delta 0), reused 0 (delta 0), pack-reused 1154 (from 1)\n[2026-11-08T05:47:55Z DEBUG silver_platter::utils] Colocated branch vendor not found, skipping\n[2026-11-08T05:47:55Z DEBUG silver_platter::utils] Colocated branch experimental not found, skipping\nTotal 16 (delta 0), reused 0 (delta 0), pack-reused 12 (from 1)\n\n[2026-11-08T05:47:59Z DEBUG silver_platter::utils] Colocated branch upstream not found, skipping\nNothing to delete.\n[2026-11-08T05:47:59Z DEBUG silver_platter::workspace] Fetching colocated branches: {\"vendor\": \"vendor\", \"upstream\": \"upstream\", \"pristine-tar\": \"pristine-tar\", \"experimental\": \"experimental\"}\nTotal 0 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)\n[2026-11-08T05:48:01Z DEBUG silver_platter::workspace] Successfully fetched colocated branch pristine-tar -> pristine-tar\n[2026-11-08T05:48:01Z INFO  janitor_worker] Workspace ready - starting.\n[2026-11-08T05:48:01Z INFO  janitor_worker::debian] Running [\"brz\", \"up\", \"--revision=git-v1:42aeb05bb69f8dad55b6644988cc53173d3c059d\"]\n[2026-11-08T05:48:02Z DEBUG debian_analyzer::detect_gbp_dch] Branch history analysis: changelog_only: 7, other_only: 40, mixed: 0, dch_references: 0, unreleased_references: 3\n[2026-11-08T05:48:02Z INFO  silver_platter::debian::codemod] Assuming changelog does not need to be updated, since changelog entries are always updated in separate commits.\nfound 557 deltas to reuse\n[2026-11-08T05:48:13Z INFO  ognibuild::debian::fix_build] Using fixers: [PgBuildExtOutOfDateControlFixer, MissingConfigureFixer, MissingAutomakeInputFixer, MissingConfigStatusInputFixer, MissingPerlFileFixer, DebcargoUnacceptablePredicateFixer, DebcargoUnacceptableComparatorFixer, RetryAptFetchFailure, PackageDependencyFixer]\n[2026-11-08T05:48:13Z DEBUG ognibuild::debian::build] Adding dummy changelog entry 5.4-1~jan+control1 for build\n[2026-11-08T05:48:13Z DEBUG ognibuild::debian::build] Writing build log to /srv/worker/output/1/janitor-worker-glf6y9/build.log\n[2026-11-08T05:48:13Z INFO  ognibuild::debian::build] Building debian packages, running sbuild --chroot-mode=unshare --no-clean-source -A -s -v -d sid.\n[2026-11-08T05:54:50Z INFO  ognibuild::debian::build] Build succeeded.\n[2026-11-08T05:54:50Z INFO  janitor_worker::debian::build] Built [\"/srv/worker/output/1/janitor-worker-glf6y9/rtc-testbench_5.4-1~jan+control1_amd64.deb\", \"/srv/worker/output/1/janitor-worker-glf6y9/rtc-testbench_5.4-1~jan+control1_amd64.changes\", \"/srv/worker/output/1/janitor-worker-glf6y9/rtc-testbench_5.4-1~jan+control1_amd64.buildinfo\"].\n[2026-11-08T05:54:50Z DEBUG janitor_worker::debian::lintian] Running lintian: cd \"/srv/worker/output/1/janitor-worker-glf6y9\" && \"lintian\" \"--exp-output=format=json\" \"--allow-root\" \"/srv/worker/output/1/janitor-worker-glf6y9/rtc-testbench_5.4-1~jan+control1_amd64.deb\" \"/srv/worker/output/1/janitor-worker-glf6y9/rtc-testbench_5.4-1~jan+control1_amd64.changes\" \"/srv/worker/output/1/janitor-worker-glf6y9/rtc-testbench_5.4-1~jan+control1_amd64.buildinfo\"\n[2026-11-08T05:54:57Z INFO  janitor_worker] Pushing result branch to http://janitor.debian.net/git/rtc-testbench\nfound 0 deltas to reuse\n[2026-11-08T05:54:57Z INFO  janitor_worker::vcs] Pushing packaging branch cache to http://janitor.debian.net/git/rtc-testbench,branch=debian%2Flatest\nfound 146 deltas to reuse\n[2026-11-08T05:54:58Z INFO  janitor_worker::vcs] Pushed packaging branch cache to http://janitor.debian.net/git/rtc-testbench,branch=debian%2Flatest\n","description":"worker","name":"worker.log","url":"/control/c/rtc-testbench/b2fbc97b-e394-4ed9-ae82-73a77c64de87/logs/worker.log"}},"my_review":null,"other_reviews":[],"primary_log":"worker","publish_history":[],"queue_position":1,"result_code":"success","resume_from":null,"reviews":[],"revision":"git-v1:42aeb05bb69f8dad55b6644988cc53173d3c059d","run":{"branch_url":"git+ssh://git@salsa.debian.org/debian/rtc-testbench.git","build_version":null,"change_set":"b2fbc97b-e394-4ed9-ae82-73a77c64de87","codebase":"rtc-testbench","command":"brz up '--revision=git-v1:42aeb05bb69f8dad55b6644988cc53173d3c059d'","context":null,"debian_build_distribution":"sid","debian_build_version":null,"description":"","duration_seconds":432,"failure_stage":null,"failure_transient":null,"finish_time":"2026-11-08T05:54:59.227552Z","id":"b2fbc97b-e394-4ed9-ae82-73a77c64de87","logfilenames":["worker.log","codemod.log","build.log"],"main_branch_revision":"git-v1:a6a77cda1d1cc439c05b9d17eb7e107e15efdb7e","publish_status":"unknown","result_branches":[{"absorbed":false,"base_revision":"git-v1:a6a77cda1d1cc439c05b9d17eb7e107e15efdb7e","diff":"diff --git a/.dir-locals.el b/.dir-locals.el\nindex 4d8d559..54bb149 100644\n--- a/.dir-locals.el\n+++ b/.dir-locals.el\n@@ -1,24 +1,10 @@\n ;;\n ;; Linux Kernel Coding Style.\n ;;\n-;; C/C++ is formatted with clang-format (.clang-format); Perl with perltidy\n-;; (.perltidyrc). The mode settings below keep Emacs editing consistent with\n-;; both: 8-column, tab-based indentation and a 100 column fill.\n-;;\n ((nil . ((indent-tabs-mode . t)\n          (fill-column . 100)\n          (tab-width . 8)))\n  (c-mode . ((c-file-style . \"linux\")\n             (c-basic-offset . 8)))\n  (c++-mode . ((c-file-style . \"linux\")\n-              (c-basic-offset . 8)))\n- (cperl-mode . ((cperl-indent-level . 8)\n-                (cperl-continued-statement-offset . 8)\n-                (cperl-close-paren-offset . -8)\n-                (cperl-indent-parens-as-block . t)\n-                (cperl-tab-always-indent . t)))\n- (perl-mode . ((perl-indent-level . 8)\n-               (perl-continued-statement-offset . 8)\n-               (perl-continued-brace-offset . 0)\n-               (perl-brace-offset . 0)\n-               (perl-label-offset . -8))))\n+              (c-basic-offset . 8))))\ndiff --git a/.github/workflows/build.yml b/.github/workflows/build.yml\nindex aab3d3d..c786264 100644\n--- a/.github/workflows/build.yml\n+++ b/.github/workflows/build.yml\n@@ -1,6 +1,6 @@\n ---\n #\n-# Copyright (C) 2024-2026 Linutronix GmbH\n+# Copyright (C) 2024 Linutronix GmbH\n # Author Kurt Kanzenbach <kurt@linutronix.de>\n #\n # SPDX-License-Identifier: BSD-2-Clause\n@@ -18,13 +18,13 @@ on:\n \n jobs:\n   build:\n-    runs-on: ubuntu-26.04\n+    runs-on: ubuntu-24.04\n     steps:\n       - name: Checkout code\n-        uses: actions/checkout@v7\n+        uses: actions/checkout@v6\n \n       - name: Install dependencies\n-        uses: awalsh128/cache-apt-pkgs-action@v1.6.3\n+        uses: awalsh128/cache-apt-pkgs-action@v1.6.0\n         with:\n           packages: >-\n             clang llvm cmake pkg-config libbpf-dev libyaml-dev libc6-dev build-essential libxdp-dev\n@@ -32,4 +32,4 @@ jobs:\n           version: 1.0\n \n       - name: Build code\n-        run: ./tools/ci/build.sh\n+        run: ./tools/ci/build.sh ubuntu\ndiff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml\nindex ebfd94d..3ba254a 100644\n--- a/.github/workflows/qa.yml\n+++ b/.github/workflows/qa.yml\n@@ -1,6 +1,6 @@\n ---\n #\n-# Copyright (C) 2024-2026 Linutronix GmbH\n+# Copyright (C) 2024 Linutronix GmbH\n # Author Kurt Kanzenbach <kurt@linutronix.de>\n #\n # SPDX-License-Identifier: BSD-2-Clause\n@@ -18,18 +18,18 @@ on:\n \n jobs:\n   build:\n-    runs-on: ubuntu-26.04\n+    runs-on: ubuntu-24.04\n     steps:\n       - name: Checkout code\n-        uses: actions/checkout@v7\n+        uses: actions/checkout@v6\n \n       - name: Install dependencies\n-        uses: awalsh128/cache-apt-pkgs-action@v1.6.3\n+        uses: awalsh128/cache-apt-pkgs-action@v1.6.0\n         with:\n           packages: >-\n             clang llvm cmake pkg-config libbpf-dev libyaml-dev libc6-dev build-essential libxdp-dev\n             libssl-dev libmosquitto-dev bash sphinx python3-sphinx-rtd-theme clang-tidy clang-format\n-            reuse libcmocka-dev perltidy\n+            reuse\n           version: 1.0\n \n       - name: Check code\ndiff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml\nindex b328182..b2bbe07 100644\n--- a/.github/workflows/sphinx.yml\n+++ b/.github/workflows/sphinx.yml\n@@ -10,7 +10,7 @@ jobs:\n   deploy:\n     runs-on: ubuntu-latest\n     steps:\n-      - uses: actions/checkout@v7\n+      - uses: actions/checkout@v6\n \n       - name: Setup Python\n         uses: actions/setup-python@v6\n@@ -27,7 +27,7 @@ jobs:\n         run: echo \"::set-output name=dir::$(pip cache dir)\"\n \n       - name: Cache dependencies\n-        uses: actions/cache@v6\n+        uses: actions/cache@v4\n         with:\n           path: ${{ steps.pip-cache.outputs.dir }}\n           key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}\ndiff --git a/.perltidyrc b/.perltidyrc\ndeleted file mode 100644\nindex 62340e6..0000000\n--- a/.perltidyrc\n+++ /dev/null\n@@ -1,20 +0,0 @@\n-# perltidy configuration for RTC-Testbench.\n-#\n-# clang-format does not support Perl, so perltidy is used for the Perl scripts\n-# in scripts/. These options reproduce the Linux kernel coding style used for\n-# the C sources (see .clang-format) as closely as perltidy allows.\n-\n---maximum-line-length=100        # -l   : ColumnLimit: 100\n---indent-columns=8               # -i   : IndentWidth: 8\n---continuation-indentation=8     # -ci  : ContinuationIndentWidth: 8\n---entab-leading-whitespace=8     # -et  : indent with tabs, TabWidth: 8\n-\n---opening-sub-brace-on-new-line  # -sbl : AfterFunction: true (sub brace on own line)\n-                                 #        control-statement braces stay cuddled (default -nbl)\n-\n---paren-tightness=2              # -pt  : SpacesInParentheses: false\n---square-bracket-tightness=2     # -sbt : SpacesInSquareBrackets: false\n---nospace-for-semicolon          # -nsfs: C-style for (;;) without space before ';'\n-\n---standard-error-output          # -se  : errors to stderr, do not create .ERR files\n---cuddled-else                   # -cd  : cuddled control braces\ndiff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml\nindex bc1b94e..6378c21 100644\n--- a/.pre-commit-config.yaml\n+++ b/.pre-commit-config.yaml\n@@ -81,21 +81,6 @@ repos:\n \n         types:\n           - shell\n-  - repo: https://github.com/pre-commit/mirrors-clang-format\n-    rev: v22.1.5\n-    hooks:\n-      - id: clang-format\n-        files: \\.(c|h)$\n-  - repo: local\n-    hooks:\n-      - id: perltidy\n-        name: perltidy\n-        entry: perltidy --pro=.perltidyrc -b -bext=/\n-        language: perl\n-        additional_dependencies:\n-          - \"Perl::Tidy@20250912\"\n-        types:\n-          - perl\n   - hooks:\n       - id: blacken-docs\n     repo: https://github.com/asottile/blacken-docs\ndiff --git a/.reuse/dep5 b/.reuse/dep5\nnew file mode 100644\nindex 0000000..cc457af\n--- /dev/null\n+++ b/.reuse/dep5\n@@ -0,0 +1,24 @@\n+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\n+Upstream-Name: RTC-Testbench\n+Upstream-Contact: Kurt Kanzenbach <kurt@linutronix.de>\n+Source: https://www.github.com/Linutronix/RTC-Testbench\n+\n+Files: .clang-format .clang-tidy .dir-locals.el CONTRIBUTING.md Documentation/Makefile Documentation/conf.py\n+Copyright: 2020-2024 Linutronix GmbH\n+License: BSD-2-Clause\n+\n+Files: Documentation/images/*.png Documentation/make.bat ISSUE_TEMPLATE.md README.md\n+Copyright: 2020-2024 Linutronix GmbH\n+License: BSD-2-Clause\n+\n+Files: .github/workflows/sphinx.yml devtools/conda-envs/docs_env.yaml requirements.txt .github/labels.yml .github/workflows/label.yml\n+Copyright: 2024 Pablo Iranzo Gómez <Pablo.Iranzo@gmail.com>\n+License: BSD-2-Clause\n+\n+Files: docker/mqtt-composer/*\n+Copyright: 2024 Intel Corporation\n+License: BSD-2-Clause\n+\n+Files: tests/workloads/README.md tests/workloads/pointer_chasing/README.md\n+Copyright: 2025 Intel Corporation\n+License: BSD-2-Clause\ndiff --git a/AGENTS.md b/AGENTS.md\ndeleted file mode 100644\nindex bf251f4..0000000\n--- a/AGENTS.md\n+++ /dev/null\n@@ -1,155 +0,0 @@\n-# AGENTS.md\n-\n-This file provides guidance to AI assistants when working with code in this repository.\n-\n-## What this is\n-\n-The **Linux RealTime Communication Testbench** validates real-time and non-real-time traffic on\n-converged (TSN) Ethernet networks. It generates, mirrors, and checks cyclic Ethernet frames to\n-measure round-trip latency, jitter and frame loss for middlewares like PROFINET and OPC/UA PubSub,\n-and to benchmark per-frame security (AES-256). It is a platform/driver/stack evaluation tool, **not**\n-a TSN conformance tester. All TSN mechanisms use mainline-Linux-only utilities.\n-\n-Two binaries are built:\n-\n-- **`reference`** — generates traffic and checks consistency + round-trip latency.\n-- **`mirror`** — reflects traffic generated by `reference` back to it.\n-\n-A test run needs two machines connected back-to-back or via a TSN network, synchronized with PTP.\n-The `mirror` must always be started **before** `reference` (internal sequence counting).\n-\n-## Build\n-\n-```bash\n-mkdir build && cd build\n-cmake -DCMAKE_BUILD_TYPE=Release -DWITH_MQTT=TRUE ..\n-make -j$(nproc)\n-sudo make install\n-```\n-\n-`CMAKE_BUILD_TYPE` matters — it sets the compiler flags (`Release`, `Debug`, `RelWithDebInfo`).\n-\n-Optional CMake flags:\n-\n-- `-DWITH_MQTT=ON` — MQTT metric export (needs `libmosquitto-dev`).\n-- `-DRX_TIMESTAMP=ON` — Rx latency display (libbpf >= 1.2).\n-- `-DTX_TIMESTAMP=ON` — Tx HW timestamping (libxdp >= 1.5.2 + kernel `XDP_TXMD_FLAGS_TIMESTAMP`).\n-- `-DWITH_ASAN=ON` — AddressSanitizer.\n-- `-DWITH_TESTS=ON` — build unit tests (needs `cmocka`).\n-\n-Build deps: `cmake`, `gcc`, `clang`, `llvm`, `pkg-config`, `libbpf-dev`, `libyaml-dev`, `libxdp-dev`,\n-`libssl-dev` (OpenSSL >= 3.0), optionally `libmosquitto-dev`, `libnl-3-dev` + `libnl-genl-3-dev`\n-(for the `napictl` helper, which also needs kernel v6.13+ headers).\n-\n-XDP eBPF programs (`src/xdp_kern_*.c`) are compiled separately by CMake with `clang -target bpf`.\n-`src/xdp_kern_tb.c` is compiled multiple times via `add_xdp_prog_tb()` in `CMakeLists.txt` with\n-different `-DEBPF_VID`/`-DEBPF_ETH_TYPE`/`-DEBPF_PRIORITY` defines, producing one `.o` per\n-VID/protocol (PROFINET, OPC/UA, AVTP). When changing XDP steering, edit both the source and the\n-`add_xdp_prog_tb()` invocations.\n-\n-## Unit tests\n-\n-```bash\n-cd build\n-cmake -DWITH_TESTS=ON .. && make -j$(nproc)\n-ctest --output-on-failure          # run all\n-ctest -R ring_buffer_test          # run a single test by name\n-```\n-\n-Tests use `cmocka` and live in `tests/unit/`. Each test compiles the unit under test directly\n-(e.g. `ring_buffer_test.c` + `src/ring_buffer.c`) — add new tests in `tests/unit/CMakeLists.txt`.\n-\n-## Running a test scenario\n-\n-Scenarios live in `tests/<name>/` as YAML configs (`reference.yaml`, `mirror.yaml`) plus shell\n-scripts (`flow.sh` configures the NIC qdiscs/filters, `ptp.sh` starts PTP, `ref.sh`/`mirror.sh`\n-wrappers). Typical flow on each of the two machines:\n-\n-```bash\n-cd tests/profinet\n-../../scripts/ptp.sh enp3s0        # 1. PTP sync (once); needs /etc/gPTP.cfg from linuxptp\n-./flow.sh enp3s0                   # 2. traffic flow / qdisc config (once)\n-cp ../../build/xdp_kern_*.o .      # XDP object files must sit next to the config\n-# host2 (mirror) first:\n-../../build/mirror -c mirror.yaml\n-# host1 (reference) second:\n-../../build/reference -c reference.yaml\n-```\n-\n-Results are written to `/var/log/{reference,mirror}_vid<NNN>.log`. For real-time classes the max\n-round-trip time should stay below 2× the cycle time, with zero lost or damaged frames. See\n-`Documentation/test.rst` for the full catalogue of scenarios (multi-middleware, PTM, netload,\n-busypolling, working clock, etc.) and their hardware requirements.\n-\n-Validate a kernel config against the testbench's requirements with:\n-\n-```bash\n-scripts/check_kconfig.pl /path/to/.config\n-```\n-\n-Required options include `CONFIG_PTP_1588_CLOCK`, `CONFIG_XDP_SOCKETS`, `CONFIG_NET_SCH_TAPRIO`,\n-`CONFIG_NET_SCH_ETF`, `CONFIG_DEBUG_INFO_BTF`. A `PREEMPT_RT` kernel (recent LTS) is required.\n-\n-## Architecture\n-\n-The applications perform cyclic Ethernet communication split into **traffic classes** ranging from\n-real-time Layer 2 up to UDP. Receivers/transmitters use either traditional `AF_PACKET` (BPF filter\n-steering) or modern `AF_XDP` (eBPF/XDP steering) sockets, selected per class by config.\n-\n-- **`src/reference.c` / `src/mirror.c`** — entry points. Parse config, spawn threads per enabled\n-  class, link them into the send chain, run until Ctrl-C.\n-- **`src/config.c/.h`** — YAML parsing into `struct app_config` + per-class\n-  `struct traffic_class_config`. The config is the source of truth for which classes run, cycle\n-  time, frame counts/sizes, VID/PCP, socket type, and security.\n-- **`src/thread.h`** — the central `struct thread_context`: socket fd, XDP socket, ring buffer,\n-  Tx/Rx security contexts, traffic-class config, and `next` pointer to the following class. The\n-  `enum pn_thread_type` (TSN_HIGH, TSN_LOW, RTC, RTA, DCP, LLDP, UDP_HIGH, UDP_LOW) defines class\n-  ordering and priority.\n-- **`src/*_thread.c`** — one file per traffic class (`tsn`, `rtc`, `rta`, `dcp`, `lldp`, `udp`,\n-  `layer2`). Each provides Rx, Tx, and (for generated classes) a Tx-generation thread.\n-- **`src/xdp.c` / `src/xdp_metadata.c`** — AF_XDP socket setup and the zero-copy send/receive path;\n-  Rx/Tx metadata (timestamps, launch time).\n-- **`src/security.c`** — per-frame AES-256-GCM authentication/encryption via OpenSSL 3.x.\n-- **`src/packet.c` / `src/net.c` / `src/profinet.c`** — frame construction/parsing, low-level\n-  socket helpers, PROFINET frame layout and frame-id handling.\n-- **`src/stat.c` / `src/hist.c`** — per-frame-type round-trip statistics and latency histograms.\n-- **`src/ring_buffer.c`** — lock-free-ish ring buffer carrying Rx frames to be mirrored.\n-- **`src/log.c` / `src/log_json.c` / `src/log_mqtt.c`** — text, JSON, and optional MQTT metric output.\n-- **`src/workload.c`** — dynamically loaded `.so` workload plugins simulating RT CPU load (see\n-  `tests/workloads/`, e.g. `jacobi_2d`, `pointer_chasing`). `mirror` is linked with\n-  `--export-dynamic` so plugins can resolve symbols.\n-- **`src/tx_time.c`** — ETF / Tx launch-time (Qbv end-station) support.\n-- **`src/napictl.c`** — standalone helper (built only with libnl3 + kernel v6.13+) for NAPI config.\n-\n-**Thread chaining is the key mechanism:** the traffic classes form a linked list via\n-`thread_context.next` (`link_pn_threads()`). When the first class finishes its Tx cycle it signals\n-the next via `pthread_cond_signal` on `data_cond_var`, so frames leave the wire in deterministic\n-strict-priority order within each cycle. `is_first`/`is_last` mark the chain ends.\n-\n-## Code style\n-\n-Linux kernel coding style. Run `clang-format` (config in `.clang-format`) on C files when unsure;\n-`.clang-tidy` is also present. Perl scripts in `scripts/` use the same style via `perltidy` (config\n-in `.perltidyrc`, since clang-format does not support Perl); run `perltidy -b <file>.pl`. Use\n-**reverse xmas tree** (RCS) local-variable ordering — longest declarations first:\n-\n-```c\n-unsigned long long big_variable;\n-unsigned int medium;\n-int small;\n-```\n-\n-Commit messages follow the Linux kernel convention: imperative subject, body explaining _why_, and a\n-`Signed-off-by:` trailer. Contributions are BSD-2-Clause (eBPF programs are dual BSD/GPL). The repo\n-is REUSE-compliant — every file needs an SPDX header (see `REUSE.toml`).\n-\n-`.pre-commit-config.yaml` runs commitizen (commit-msg), prettier, black/isort/flake8 (Python\n-scripts), and shfmt (shell). CI workflows live in `.github/workflows/` (`build.yml`, `qa.yml`,\n-`sphinx.yml`).\n-\n-## Documentation\n-\n-Source docs are reStructuredText under `Documentation/` (Sphinx; published at\n-https://linutronix.github.io/RTC-Testbench). `build.rst`, `test.rst`, `configuration.rst`,\n-`security.rst`, `workload.rst`, and `mqtt.rst` are the most useful references. Update them when\n-behavior or config options change.\ndiff --git a/CMakeLists.txt b/CMakeLists.txt\nindex cf01c77..f782304 100644\n--- a/CMakeLists.txt\n+++ b/CMakeLists.txt\n@@ -1,18 +1,17 @@\n # SPDX-License-Identifier: BSD-2-Clause\n #\n-# Copyright (C) 2020-2026 Linutronix GmbH\n+# Copyright (C) 2020-2025 Linutronix GmbH\n # Author Kurt Kanzenbach <kurt@linutronix.de>\n #\n \n cmake_minimum_required(VERSION 3.10)\n project(rtc-testbench)\n \n-set(VERSION \"5.5\")\n+set(VERSION \"5.4\")\n \n include(CheckIncludeFiles)\n include(CheckFunctionExists)\n include(CheckSymbolExists)\n-include(CheckCSourceCompiles)\n include(GNUInstallDirs)\n \n find_package(PkgConfig REQUIRED)\n@@ -24,8 +23,6 @@ pkg_search_module(YAML yaml-0.1 REQUIRED)\n pkg_search_module(LIBBPF libbpf REQUIRED)\n pkg_search_module(LIBXDP libxdp REQUIRED)\n pkg_search_module(LIBCRYPTO libcrypto>=3.0 REQUIRED)\n-pkg_search_module(LIBNL3 libnl-3.0)\n-pkg_search_module(LIBNL_GENL3 libnl-genl-3.0)\n \n check_symbol_exists(SO_BUSY_POLL \"sys/socket.h\" HAVE_SO_BUSY_POLL)\n check_symbol_exists(SO_PREFER_BUSY_POLL \"sys/socket.h\" HAVE_SO_PREFER_BUSY_POLL)\n@@ -33,28 +30,6 @@ check_symbol_exists(SO_BUSY_POLL_BUDGET \"sys/socket.h\" HAVE_SO_BUSY_POLL_BUDGET)\n check_symbol_exists(XDP_TXMD_FLAGS_LAUNCH_TIME \"linux/if_xdp.h\" HAVE_XDP_FLAGS_TX_TIME)\n check_symbol_exists(XDP_TXMD_FLAGS_TIMESTAMP \"linux/if_xdp.h\" HAVE_XDP_FLAGS_TX_HWTSTAMP)\n \n-#\n-# NETDEV_CMD_QUEUE_GET and NETDEV_CMD_NAPI_SET are enums. So check_symbol_exists() does not work\n-# here. Use test programs instead.\n-#\n-check_c_source_compiles(\"\n-  #include <linux/netdev.h>\n-\n-  int main() {\n-    int i = NETDEV_CMD_QUEUE_GET;\n-    return 0;\n-  }\"\n-  HAVE_CMD_QUEUE_GET)\n-\n-check_c_source_compiles(\"\n-  #include <linux/netdev.h>\n-\n-  int main() {\n-    int i = NETDEV_CMD_NAPI_SET;\n-    return 0;\n-  }\"\n-  HAVE_CMD_NAPI_SET)\n-\n add_executable(reference\n   src/reference.c\n   src/config.c\n@@ -79,9 +54,7 @@ add_executable(reference\n   src/packet.c\n   src/hist.c\n   src/workload.c\n-  src/print.c\n-  src/profinet.c\n-  src/tb.c)\n+  src/print.c)\n add_executable(mirror\n   src/mirror.c\n   src/config.c\n@@ -106,11 +79,9 @@ add_executable(mirror\n   src/packet.c\n   src/hist.c\n   src/workload.c\n-  src/print.c\n-  src/profinet.c\n-  src/tb.c)\n+  src/print.c)\n \n-option(WITH_MQTT \"Enable/Disable logging via MQTT\" OFF)\n+option(WITH_MQTT, \"Enable/Disable logging via MQTT\" OFF)\n if (WITH_MQTT)\n   pkg_search_module(MOSQUITTO libmosquitto REQUIRED)\n   target_link_libraries(reference ${MOSQUITTO_LIBRARIES})\n@@ -120,7 +91,7 @@ if (WITH_MQTT)\n endif()\n \n option(RX_TIMESTAMP \"Enable RX timestamp support (requires libbpf >= 1.2)\" OFF)\n-option(TX_TIMESTAMP \"Enable TX timestamp support (requires libxdp >= 1.5.2)\" OFF)\n+option(TX_TIMESTAMP \"Enable TX timestamp support (requires libxdp >= 1.4.1)\" OFF)\n \n if(RX_TIMESTAMP)\n   pkg_check_modules(LIBBPF_REQUIRED_FOR_RX_TIMESTAMP libbpf>=1.2)\n@@ -130,18 +101,18 @@ if(RX_TIMESTAMP)\n endif()\n \n if(TX_TIMESTAMP)\n-  if(NOT ((${LIBXDP_VERSION} GREATER_EQUAL 1.5.2) AND (${HAVE_XDP_FLAGS_TX_HWTSTAMP})))\n-    message(FATAL_ERROR \"TX_TIMESTAMP requires libxdp >= 1.5.2 and kernel support for XDP_TXMD_FLAGS_TIMESTAMP\")\n+  if(NOT ((${LIBXDP_VERSION} GREATER_EQUAL 1.4.1) AND (${HAVE_XDP_FLAGS_TX_HWTSTAMP})))\n+    message(FATAL_ERROR \"TX_TIMESTAMP requires libxdp >= 1.4.1 and kernel support for XDP_TXMD_FLAGS_TIMESTAMP\")\n   endif()\n endif()\n \n-target_compile_options(reference PRIVATE -Wall -Wextra -Wno-sign-compare -Wshadow -std=gnu99 -Wvla)\n-target_compile_options(mirror PRIVATE -Wall -Wextra -Wno-sign-compare -Wshadow -std=gnu99 -Wvla)\n+target_compile_options(reference PRIVATE -Wall -Wshadow -std=gnu99 -Wvla)\n+target_compile_options(mirror PRIVATE -Wall -Wshadow -std=gnu99 -Wvla)\n \n target_compile_definitions(reference PRIVATE -D_GNU_SOURCE)\n target_compile_definitions(mirror PRIVATE -D_GNU_SOURCE)\n \n-option(WITH_ASAN \"Build with AddressSanitizer for debugging\" OFF)\n+option(WITH_ASAN, \"Build with AddressSanitizer for debugging\" OFF)\n if (WITH_ASAN)\n   target_compile_options(reference PRIVATE -fsanitize=address)\n   target_compile_options(mirror PRIVATE -fsanitize=address)\n@@ -150,27 +121,6 @@ if (WITH_ASAN)\n   target_link_options(mirror PRIVATE -fsanitize=address)\n endif()\n \n-if ((${LIBNL3_FOUND}) AND (${LIBNL_GENL3_FOUND}) AND\n-    (${HAVE_CMD_QUEUE_GET}) AND (${HAVE_CMD_NAPI_SET}))\n-  message(STATUS \"Found libnl3: Building napictl configuration tool\")\n-\n-  add_executable(napictl src/napictl.c)\n-  target_compile_options(napictl PRIVATE -Wall -Wextra -Wshadow -std=gnu99 -Wvla)\n-  target_compile_definitions(napictl PRIVATE -D_GNU_SOURCE)\n-\n-  target_link_libraries(napictl ${LIBNL3_LIBRARIES})\n-  target_link_libraries(napictl ${LIBNL_GENL3_LIBRARIES})\n-  target_include_directories(napictl PUBLIC ${LIBNL3_INCLUDE_DIRS})\n-  target_include_directories(napictl PUBLIC ${LIBNL_GENL3_INCLUDE_DIRS})\n-\n-  if (WITH_ASAN)\n-    target_compile_options(napictl PRIVATE -fsanitize=address)\n-    target_link_options(napictl PRIVATE -fsanitize=address)\n-  endif()\n-else()\n-  message(STATUS \"Building napictl configuration tool requires kernel v6.13+\")\n-endif()\n-\n # Export symbols for dynamically loaded workloads\n target_link_options(mirror PRIVATE -Wl,--export-dynamic)\n \n@@ -208,9 +158,9 @@ if ((${LIBXDP_VERSION} GREATER_EQUAL 1.5.0) AND (${HAVE_XDP_FLAGS_TX_TIME}))\n endif()\n \n #\n-# Check for XDP and Tx HW Timestamp support. It requires: libxdp >= 1.5.2 and Linux v6.8.\n+# Check for XDP and Tx HW Timestamp support. It requires: libxdp >= 1.4.1 and Linux v6.8.\n #\n-if ((${LIBXDP_VERSION} GREATER_EQUAL 1.5.2) AND (${HAVE_XDP_FLAGS_TX_HWTSTAMP}))\n+if ((${LIBXDP_VERSION} GREATER_EQUAL 1.4.1) AND (${HAVE_XDP_FLAGS_TX_HWTSTAMP}))\n   message(STATUS \"XDP and Tx HW Timestamp support available using libxdp version: ${LIBXDP_VERSION}\")\n endif()\n \n@@ -241,28 +191,12 @@ function(add_xdp_prog name)\n   install(FILES ${CMAKE_BINARY_DIR}/${name}.o DESTINATION ${CMAKE_INSTALL_LIBDIR}/rtc-testbench/ebpf)\n endfunction()\n \n-function(add_xdp_prog_tb name vid eth_type priority check_profinet)\n-  set(defs -DEBPF_VID=${vid} -DEBPF_ETH_TYPE=${eth_type} -DEBPF_PRIORITY=${priority})\n-  if(check_profinet)\n-    list(APPEND defs -DEBPF_CHECK_FRAMEID=1)\n-  endif()\n-\n-  add_custom_target(${name} ALL\n-    COMMAND clang ${CLANG_FLAGS} ${defs} -target bpf -c -g -o ${CMAKE_BINARY_DIR}/${name}.o xdp_kern_tb.c\n-    DEPENDS reference\n-    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n-    SOURCES src/xdp_kern_tb.c\n-  )\n-\n-  install(FILES ${CMAKE_BINARY_DIR}/${name}.o DESTINATION ${CMAKE_INSTALL_LIBDIR}/rtc-testbench/ebpf)\n-endfunction()\n-\n-add_xdp_prog_tb(xdp_kern_profinet_vid100 100 0x8892 10 1)\n-add_xdp_prog_tb(xdp_kern_opcua_vid100    100 0xb62c 20 0)\n-add_xdp_prog_tb(xdp_kern_profinet_vid200 200 0x8892 10 1)\n-add_xdp_prog_tb(xdp_kern_opcua_vid200    200 0xb62c 20 0)\n-add_xdp_prog_tb(xdp_kern_opcua_vid300    300 0xb62c 20 0)\n-add_xdp_prog_tb(xdp_kern_avtp_vid400     400 0x22f0 20 0)\n+add_xdp_prog(xdp_kern_profinet_vid100)\n+add_xdp_prog(xdp_kern_opcua_vid100)\n+add_xdp_prog(xdp_kern_profinet_vid200)\n+add_xdp_prog(xdp_kern_opcua_vid200)\n+add_xdp_prog(xdp_kern_opcua_vid300)\n+add_xdp_prog(xdp_kern_avtp_vid400)\n add_xdp_prog(xdp_kern_profinet_veth_dispatch)\n \n install(TARGETS reference DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT binaries)\n@@ -271,10 +205,3 @@ install(DIRECTORY ${CMAKE_SOURCE_DIR}/tests/ DESTINATION ${CMAKE_INSTALL_DATAROO\n \n # Add workloads subdirectory for automatic compilation\n add_subdirectory(tests/workloads)\n-\n-# Add unit tests\n-option(WITH_TESTS \"Build unit tests (requires cmocka)\" OFF)\n-if (WITH_TESTS)\n-  enable_testing()\n-  add_subdirectory(tests/unit)\n-endif()\ndiff --git a/CONTRIBUTING.md b/CONTRIBUTING.md\nindex 9401a0b..e1b342e 100644\n--- a/CONTRIBUTING.md\n+++ b/CONTRIBUTING.md\n@@ -47,9 +47,7 @@ https://www.kernel.org/doc/html/latest/process/submitting-patches.html#sign-your\n ## Code Style\n \n The coding style is the Linux kernel one. If unsure about it, run\n-`clang-format` on the C files. The Perl scripts in `scripts/` follow the same\n-style via `perltidy` (config in `.perltidyrc`); run `perltidy -b <file>.pl` to\n-format them.\n+`clang-format` on the C files.\n \n In addition, the reverse xmas tree variable ordering is used:\n \ndiff --git a/Documentation/build.rst b/Documentation/build.rst\nindex 2428bcb..bc34eb3 100644\n--- a/Documentation/build.rst\n+++ b/Documentation/build.rst\n@@ -1,13 +1,11 @@\n .. SPDX-License-Identifier: BSD-2-Clause\n ..\n-.. Copyright (C) 2022-2026 Linutronix GmbH\n+.. Copyright (C) 2022-2025 Linutronix GmbH\n .. Author Kurt Kanzenbach <kurt@linutronix.de>\n ..\n .. Testbench documentation build file.\n ..\n \n-.. _Build:\n-\n Build\n =====\n \n@@ -17,7 +15,7 @@ The buildsystem is cmake. The applications can be build and installed by:\n \n    mkdir build\n    cd build\n-   cmake -DCMAKE_BUILD_TYPE=Release -DWITH_MQTT=TRUE -DRX_TIMESTAMP=TRUE -DTX_TIMESTAMP=TRUE ..\n+   cmake -DCMAKE_BUILD_TYPE=Release -DWITH_MQTT=TRUE ..\n    make -j`nproc`\n    sudo make install\n \n@@ -40,6 +38,8 @@ For Debian based systems do:\n       libbpf-dev libyaml-dev libc6-dev rt-tests ethtool iproute2 \\\n       iperf3 linuxptp libxdp-dev libssl-dev libmosquitto-dev git bc\n \n+.. Note:: For Debian Bullseye ``libxdp`` is packaged in backports.\n+\n For RHEL based systems do:\n \n .. code:: bash\n@@ -53,17 +53,20 @@ For RHEL based systems do:\n           a matter of running ``crb enable`` on the subscription-manager registered system with the\n           ``EPEL`` repository enabled.\n \n-Furthermore, the Linux RealTime Communication ``Testbench`` requires a real-time (PREEMPT_RT) Linux\n-kernel. A recent LTS version is recommended. Depending on the test case specific networking options\n-should be set. The following script ``scripts/check_kconfig.pl`` can be used to verify a given Linux\n-kernel configuration ``.config`` file.\n-\n-Example: No errors should be printed. Warnings can be evaluated on a case by case basis.\n-\n-.. code:: bash\n-\n-   kurt@jax linux % ~/work/RTC-Testbench/scripts/check_kconfig.pl .config\n-   Checking config file '.config'...\n+Furthermore, the Linux RealTime Communication ``Testbench`` requires Linux kernel version >= v5.12.\n+PREEMPT_RT is recommended. In addition, the following configuration options have to be set:\n+\n+- CONFIG_PTP_1588_CLOCK\n+- CONFIG_BPF\n+- CONFIG_BPF_SYSCALL\n+- CONFIG_DEBUG_INFO\n+- CONFIG_DEBUG_INFO_BTF\n+- CONFIG_XDP_SOCKETS\n+- CONFIG_NET_SCH_MQPRIO\n+- CONFIG_NET_SCH_TAPRIO\n+- CONFIG_NET_SCH_ETF\n+- CONFIG_NET_SCH_INGRESS\n+- CONFIG_NET_CLS_*\n \n Additional Options\n ^^^^^^^^^^^^^^^^^^\n@@ -86,9 +89,3 @@ and options.\n \n    * - Enable TX Hardware Timestamping\n      - TX_TIMESTAMP\n-\n-   * - Compile with AddressSanitizer for debugging\n-     - WITH_ASAN\n-\n-   * - Enable/Build Unit tests (requires ``cmocka`` library)\n-     - WITH_TESTS\ndiff --git a/Documentation/conf.py b/Documentation/conf.py\nindex e9fd4a5..eb7f79e 100644\n--- a/Documentation/conf.py\n+++ b/Documentation/conf.py\n@@ -18,11 +18,11 @@\n # -- Project information -----------------------------------------------------\n \n project = \"Linux RealTime Communication Testbench\"\n-copyright = \"2022-2026, Linutronix GmbH\"\n+copyright = \"2022-2025, Linutronix GmbH\"\n author = \"Kurt Kanzenbach\"\n \n # The full version, including alpha/beta/rc tags\n-release = \"v5.5\"\n+release = \"v5.4\"\n \n \n # -- General configuration ---------------------------------------------------\ndiff --git a/Documentation/configuration.rst b/Documentation/configuration.rst\nindex c99ceb9..16cc434 100644\n--- a/Documentation/configuration.rst\n+++ b/Documentation/configuration.rst\n@@ -1,6 +1,6 @@\n .. SPDX-License-Identifier: BSD-2-Clause\n ..\n-.. Copyright (C) 2022-2026 Linutronix GmbH\n+.. Copyright (C) 2022-2025 Linutronix GmbH\n .. Author Kurt Kanzenbach <kurt@linutronix.de>\n ..\n .. Testbench documentation configuration file.\n@@ -31,7 +31,7 @@ Possible parameters include:\n      - Description\n \n    * - ApplicationClockId (String)\n-     - ``CLOCK_TAI`` or ``CLOCK_MONOTONIC`` or ``CLOCK_REALTIME`` or ``CLOCK_AUX[0-7]``\n+     - ``CLOCK_TAI`` or ``CLOCK_MONOTONIC``\n \n    * - ApplicationBaseStartTimeNS (Integer)\n      - Absolute start time of simulation (or nothing for default value now + 30s)\n@@ -67,7 +67,8 @@ Possible parameters include:\n      - Drive XDP socket in busy poll mode\n \n    * - <Class>TxTimeEnabled (Boolean)\n-     - Use Tx Launch Time for all transmitted frames (requires ETF Qdisc)\n+     - Use Tx Launch Time for all transmitted frames (requires ETF Qdisc and is not compatible with\n+       XDP)\n \n    * - <Class>TxTimeOffsetNS (Integer)\n      - Optional Tx Launch Time offset relative to Qbv schedule and cycle time\ndiff --git a/Documentation/index.rst b/Documentation/index.rst\nindex 1d23b6e..172bcd3 100644\n--- a/Documentation/index.rst\n+++ b/Documentation/index.rst\n@@ -43,11 +43,6 @@ Welcome to Linux RealTime Communication Testbench's documentation!\n \n    configuration.rst\n \n-.. toctree::\n-   :maxdepth: 2\n-\n-   napictl.rst\n-\n .. toctree::\n    :maxdepth: 2\n \ndiff --git a/Documentation/introduction.rst b/Documentation/introduction.rst\nindex 10db327..ec10990 100644\n--- a/Documentation/introduction.rst\n+++ b/Documentation/introduction.rst\n@@ -52,7 +52,6 @@ Version history:\n - v5.2: February 2025, Histogram and bugfixes\n - v5.3: May 2025, AF_XDP Tx Launch Time and bugfixes\n - v5.4: December 2025, Workload simulation and statistics\n-- v5.5: July 2026, Workload enhancements, auxiliary clock support and napictl\n \n Motivation\n ^^^^^^^^^^\ndiff --git a/Documentation/napictl.rst b/Documentation/napictl.rst\ndeleted file mode 100644\nindex 5817302..0000000\n--- a/Documentation/napictl.rst\n+++ /dev/null\n@@ -1,150 +0,0 @@\n-.. SPDX-License-Identifier: BSD-2-Clause\n-..\n-.. Copyright (C) 2025-2026 Linutronix GmbH\n-.. Author Kurt Kanzenbach <kurt@linutronix.de>\n-..\n-.. Testbench documentation napictl file.\n-..\n-\n-.. _napictl:\n-\n-NAPI Configuration (napictl)\n-============================\n-\n-Introduction\n-------------\n-\n-``napictl`` is a small standalone helper that configures the NAPI parameters\n-``defer-hard-irqs`` and ``gro-flush-timeout`` for an individual queue of a network\n-interface. It is primarily used to enable kernel side busy polling for the queue that\n-carries the real time traffic, which reduces the jitter introduced by hardware and software\n-interrupts. See the :ref:`Busypolling` test for the corresponding use case.\n-\n-Background\n-----------\n-\n-The Linux kernel exposes two NAPI parameters relevant for busy polling:\n-\n-- ``defer-hard-irqs``: How often the NAPI processing is deferred, i.e. how many times the\n-  kernel busy polls a queue before re-arming the hardware interrupt.\n-- ``gro-flush-timeout``: Timeout (in nanoseconds) after which the kernel takes over the NAPI\n-  processing again. For cyclic real time traffic it has to be larger than the application\n-  cycle time, otherwise the kernel falls back to interrupt driven processing within a cycle.\n-\n-Both parameters can be set globally for a whole interface via ``sysfs``::\n-\n-   /sys/class/net/<interface>/napi_defer_hard_irqs\n-   /sys/class/net/<interface>/gro_flush_timeout\n-\n-However, in a converged TSN setup only the queue(s) carrying the real time streams should be\n-busy polled, while the remaining queues stay interrupt driven to save CPU cycles. The ``sysfs``\n-interface cannot express this per-queue granularity. ``napictl`` therefore uses the netlink\n-``netdev`` generic netlink API to resolve the NAPI instance(s) backing a given queue and to set\n-both parameters only for that queue.\n-\n-Requirements\n-------------\n-\n-``napictl`` is only built when its build and runtime requirements are met:\n-\n-- ``libnl-3`` and ``libnl-genl-3`` development packages at build time.\n-- Linux kernel v6.13+ headers providing the ``netdev`` generic netlink commands\n-  ``NETDEV_CMD_QUEUE_GET`` and ``NETDEV_CMD_NAPI_SET``.\n-- A network driver that supports per-queue NAPI configuration. This currently works for the\n-  Intel ``igc`` (i225/i226) and ``igb`` drivers.\n-\n-If the requirements are not satisfied, ``cmake`` skips the target and prints::\n-\n-   Building napictl configuration tool requires kernel v6.13+\n-\n-When the requirements are met, ``cmake`` prints ``Found libnl3: Building napictl configuration\n-tool`` and the binary is produced as ``build/napictl``. See :ref:`Build` for the package names\n-on Debian and RHEL based systems.\n-\n-Command Line Usage\n-------------------\n-\n-.. code:: bash\n-\n-   host: ./napictl -h\n-   usage: napictl [options]\n-     options:\n-       -i, --interface:         Network interface to configure\n-       -q, --queue:             Queue of network interface to configure\n-       -d, --defer-hard-irqs:   Set defer-hard-irqs\n-       -g, --gro-flush-timeout: Set gro-flush-timeout\n-       -h, --help:              Print this help text\n-       -v, --verbose:           Print verbose messages\n-       -V, --version:           Print version\n-\n-.. list-table:: Options\n-   :widths: 30 70\n-   :header-rows: 1\n-\n-   * - Option\n-     - Description\n-\n-   * - ``-i``, ``--interface``\n-     - Network interface to configure (mandatory).\n-\n-   * - ``-q``, ``--queue``\n-     - Queue index to configure. Defaults to ``0``.\n-\n-   * - ``-d``, ``--defer-hard-irqs``\n-     - Value for ``defer-hard-irqs``. Defaults to ``0``.\n-\n-   * - ``-g``, ``--gro-flush-timeout``\n-     - Value for ``gro-flush-timeout`` in nanoseconds. Defaults to ``0``.\n-\n-   * - ``-v``, ``--verbose``\n-     - Print the resolved NAPI ids and the applied settings.\n-\n-   * - ``-V``, ``--version``\n-     - Print the version and exit.\n-\n-   * - ``-h``, ``--help``\n-     - Print the usage and exit.\n-\n-.. Note:: ``napictl`` modifies network interface settings and therefore requires\n-          ``CAP_NET_ADMIN`` (e.g. run it as ``root``).\n-\n-How It Works\n-------------\n-\n-For the requested queue ``napictl``:\n-\n-1. Resolves the interface index from the interface name.\n-2. Issues a ``NETDEV_CMD_QUEUE_GET`` dump and matches the queue index to obtain the NAPI ids of\n-   its receive and transmit NAPI instances.\n-3. Applies ``defer-hard-irqs`` and ``gro-flush-timeout`` to both the Rx and Tx NAPI instances via\n-   ``NETDEV_CMD_NAPI_SET``.\n-\n-Passing ``-v`` prints the resolved NAPI ids and the values that get applied, which is helpful for\n-debugging the queue to NAPI mapping.\n-\n-Examples\n---------\n-\n-Configure ``defer-hard-irqs`` and a ``gro-flush-timeout`` of 2 ms (2000000 ns) for queue 0 of\n-``enp3s0``:\n-\n-.. code:: bash\n-\n-   napictl -i enp3s0 -q 0 -d 10 -g 2000000\n-\n-Reset the parameters for queue 0 back to their defaults (interrupt driven processing):\n-\n-.. code:: bash\n-\n-   napictl -i enp3s0 -q 0 -d 0 -g 0\n-\n-The test scripts wrap ``napictl`` in the ``napi_defer_hard_irqs_queue()`` helper from\n-``tests/lib/common.sh``, which derives ``gro-flush-timeout`` from the cycle time and applies the\n-settings per queue:\n-\n-.. code:: bash\n-\n-   # napi_defer_hard_irqs_queue($napictl, $interface, $cycle_time_ns, $queue)\n-   napi_defer_hard_irqs_queue \"../../build/napictl\" \"enp3s0\" \"1000000\" 0\n-\n-See ``tests/busypolling_napiconf/`` and ``tests/er26/`` for complete examples.\ndiff --git a/Documentation/processing_latency.rst b/Documentation/processing_latency.rst\nindex 45c1e22..f4168c4 100644\n--- a/Documentation/processing_latency.rst\n+++ b/Documentation/processing_latency.rst\n@@ -162,7 +162,7 @@ The following table summarizes all dependencies:\n      - Any version\n    * - **libxdp**\n      - Any version\n-     - >= 1.5.2\n+     - >= 1.4.1\n    * - **NIC Driver Feature**\n      - bpf_xdp_metadata_rx_timestamp()\n      - XDP_TXMD_FLAGS_TIMESTAMP\n@@ -202,15 +202,13 @@ per cycle):\n .. code-block:: text\n \n    # Processing Latency Metrics\n-   TsnHighProcFirstMin=982 [us]\n+   TsnHighProcFirstMin=999 [us]\n    TsnHighProcFirstMax=1001 [us]\n-   TsnHighProcFirstAvg=998.476523 [us]\n-   TsnHighProcFirstOutliers=1\n+   TsnHighProcFirstAvg=999.198684 [us]\n \n-   TsnHighProcBatchMin=983 [us]\n+   TsnHighProcBatchMin=1002 [us]\n    TsnHighProcBatchMax=1003 [us]\n-   TsnHighProcBatchAvg=1000.237762 [us]\n-   TsnHighProcBatchOutliers=739\n+   TsnHighProcBatchAvg=1002.000087 [us]\n \n Additional Monitoring Points\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^\ndiff --git a/Documentation/qa.rst b/Documentation/qa.rst\nindex 6f13d6a..07c2501 100644\n--- a/Documentation/qa.rst\n+++ b/Documentation/qa.rst\n@@ -1,6 +1,6 @@\n .. SPDX-License-Identifier: BSD-2-Clause\n ..\n-.. Copyright (C) 2022-2026 Linutronix GmbH\n+.. Copyright (C) 2022-2024 Linutronix GmbH\n .. Author Kurt Kanzenbach <kurt@linutronix.de>\n ..\n .. Testbench documentation qa file.\n@@ -64,17 +64,3 @@ fixed inplace) by running ``clang-tidy``:\n    % cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ..\n    % make -j`nproc`\n    % run-clang-tidy\n-\n-Unit Tests\n-----------\n-\n-Some code parts like the ring buffer have dedicated unit tests. These tests require ``cmocka``\n-library. The tests can be invoked like this:\n-\n-.. code:: bash\n-\n-   % mkdir build\n-   % cd build\n-   % cmake -DWITH_TESTS=ON ..\n-   % make -j`nproc`\n-   % make test\ndiff --git a/Documentation/statistics.rst b/Documentation/statistics.rst\nindex 433d4e4..a159917 100644\n--- a/Documentation/statistics.rst\n+++ b/Documentation/statistics.rst\n@@ -56,18 +56,10 @@ The following table shows all gathered statistics. All statistics are collected\n      - Latency from the first RX hardware timestamp to the last TX hardware timestamp (batch\n        processing latency per cycle). See :ref:`Processing Latency <ProcessingLatency>` for detailed information.\n \n-   * - ProcBatchOutliers\n-     - Is incremented if a ProcBatch latency is greater than cycle time. This usually indicates some\n-       real time issue (e.g., kernel, driver, hardware, ...). Should be zero.\n-\n    * - ProcFirst[Min,Max,Av] [us]\n      - Latency from the first RX hardware timestamp to the first TX hardware timestamp (first-frame\n        processing latency per cycle). See :ref:`Processing Latency <ProcessingLatency>` for detailed information.\n \n-   * - ProcFirstOutliers\n-     - Is incremented if a ProcFirst latency is greater than cycle time. This usually indicates some\n-       real time issue (e.g., kernel, driver, hardware, ...). Should be zero.\n-\n    * - RoundTrip[Min,Max,Av] [us]\n      - Round trip delay measures the time from reference to mirror and back. The delay is calculated\n        based on software timestamps. Also the mirror sends back the frame not immediately, but\ndiff --git a/Documentation/test.rst b/Documentation/test.rst\nindex 5663cd1..3214666 100644\n--- a/Documentation/test.rst\n+++ b/Documentation/test.rst\n@@ -51,11 +51,11 @@ This step has to be done only once.\n    :caption: Configure traffic flow for network interface on both machines\n \n    host1: cd tests/profinet\n-   host1: ./flow.sh enp3s0\n+   host1: ./flow_cml.sh enp3s0\n    host1: journalctl -f               # Verify that PTP is running and stable\n \n    host2: cd tests/profinet\n-   host2: ./flow.sh enp3s0\n+   host2: ./flow_cml.sh enp3s0\n    host2: journalctl -f               # Verify that PTP is running and stable\n \n This step has to be done only once.\n@@ -67,7 +67,7 @@ This step has to be done only once.\n \n    host2: cd tests/profinet\n    host2: cp ../../build/xdp_kern_*.o .\n-   host2: ../../build/mirror -c mirror.yaml\n+   host2: ../../build/mirror -c mirror_vid100_cml.yaml\n    host2: tail -f /var/log/mirror_vid100.log           # Verify that mirror application runs\n \n 4. Reference\n@@ -77,7 +77,7 @@ This step has to be done only once.\n \n    host1: cd tests/profinet\n    host1: cp ../../build/xdp_kern_*.o .\n-   host1: ../../build/reference -c reference.yaml\n+   host1: ../../build/reference -c reference_vid100_cml.yaml\n    host1: tail -f /var/log/reference_vid100.log              # Check statistics\n \n The ``reference`` and ``mirror`` applications can be stopped with Ctrl-C and started again e.g. for\n@@ -453,8 +453,6 @@ All required configuration files and scripts are located in\n \n Hardware: Intel CPU with Intel i225/i226\n \n-.. _Busypolling:\n-\n Busypolling\n ^^^^^^^^^^^\n \n@@ -464,11 +462,6 @@ is designed to test busy polling with AF_XDP sockets.\n \n .. Note:: Busy polling on ``PREEMPT_RT`` only works with Linux kernel >= v6.5.\n \n-.. Note:: The per-queue NAPI parameters (``defer-hard-irqs`` and ``gro-flush-timeout``) required for\n-          busy polling can be configured with the ``napictl`` helper. See\n-          :ref:`NAPI Configuration (napictl) <napictl>`. The ``tests/busypolling_napiconf/`` and\n-          ``tests/er26/`` scenarios use it.\n-\n .. list-table:: Busypolling Test\n    :widths: 28 28 12 12 12 12\n    :header-rows: 1\n@@ -492,45 +485,6 @@ All required configuration files and scripts are located in\n \n Hardware: Intel CPU with Intel i225/i226\n \n-Working Clock\n-^^^^^^^^^^^^^\n-\n-IEEE 60802 (the industrial TSN profile for automation) defines at least two time domains:\n-\n-- **Global time**: Is a global time domain e.g., based on UTC. It is used to correlate events and\n-  for logging and diagnosis. Can be distributed by NTP or gPTP.\n-- **Working clock**: This is a high precision time domain used for scheduling and packet\n-  transmission inside a TSN network. It is not necessarily based on UTC. It can be based on an\n-  arbitrary point in time. It is distributed by gPTP.\n-\n-In this test case, the following Linux clocks are involved:\n-\n-- ``CLOCK_REALTIME`` and ``CLOCK_TAI``: Is set by NTP or gPTP.\n-- ``CLOCK_AUX0``: Is set by ``phc2sys`` from network gPTP time.\n-\n-Both clocks are completely independent from each other. The Testbench applications run on\n-``CLOCK_AUX0``.\n-\n-.. Note:: ``phc2sys`` requires additional patches to use ``CLOCK_AUX``. The patches are not yet\n-\t  released, but in the master branch of ``linuxptp``.\n-\n-.. Note:: The Linux kernel does not support ``clock_nanosleep(2)`` for ``CLOCK_AUX`` yet. To run\n-          this test case successfully we have to wait for Linux >= v7.2.\n-\n-All required configuration files and scripts are located in ``tests/working_clock/``,\n-``tests/working_clock_2/`` and ``tests/working_clock_3/``.\n-\n-Difference between these three test cases:\n-\n-- **working_clock**: ``CLOCK_REALTIME`` set by NTP, e.g. chrony.\n-- **working_clock_2**: ``CLOCK_REALTIME`` set by dedicated gPTP domain. Test utilizes Linux PTP\n-  vClocks for gPTP multi domain support.\n-- **working_clock_3**: ``CLOCK_REALTIME`` set by dedicated gPTP domain. Test utilizes Linux PTP\n-  vClocks for gPTP multi domain support. However, the WC is not derived from vClock, but rather uses\n-  a separate physical PHC. This allows to combine multi domain with EST.\n-\n-Hardware: Intel CPU with Intel i225/i226.\n-\n Tested Hardware\n ---------------\n \ndiff --git a/Documentation/workload.rst b/Documentation/workload.rst\nindex 1e5a1e6..68d04ac 100644\n--- a/Documentation/workload.rst\n+++ b/Documentation/workload.rst\n@@ -1,7 +1,7 @@\n .. SPDX-License-Identifier: BSD-2-Clause\n ..\n-.. Copyright (C) 2025-2026 Linutronix GmbH\n-.. Copyright (C) 2025 Intel Corporation\n+.. Copyright (C) 2025 Linutronix GmbH\n+.. Author Kurt Kanzenbach <kurt@linutronix.de>\n ..\n .. Testbench documentation workload file.\n ..\n@@ -19,147 +19,13 @@ between Rx and Tx. This allows to simulate the compute part in addition to the n\n Workload Integration\n --------------------\n \n-This section describes the requirements for implementing a workload that can be dynamically loaded\n-into the RTC-Testbench framework.\n+The workload integration guide is documented on Github:\n \n-Requirements\n-^^^^^^^^^^^^\n+`Workloads <https://github.com/Linutronix/RTC-Testbench/tree/main/tests/workloads>`_\n \n-To be compatible with RTC-Testbench, a workload must:\n+Available Workloads\n+-------------------\n \n-1. Be compiled as a shared object with position independent code (using the ``-fPIC`` GCC compiler\n-   flag).\n-2. Implement a runtime function to be called each cycle after completion of the network RX routine.\n-3. (Optional) Implement a setup function to perform initialization tasks, such as memory allocation,\n-   to avoid doing them in the time critical path.\n+There is one example workload provided: ``pointer_chasing``:\n \n-Specifying a workload\n-^^^^^^^^^^^^^^^^^^^^^\n-\n-The following options are used to configure a RX workload:\n-\n-.. list-table:: Traffic class configuration options\n-   :widths: 50 100\n-   :header-rows: 1\n-\n-   * - Option\n-     - Description\n-\n-   * - <Class>RxWorkloadEnabled (Boolean)\n-     - Enable/disable workload execution for traffic class\n-\n-   * - <Class>RxWorkloadFile (String)\n-     - Path to the shared library containing the workload\n-\n-   * - <Class>RxWorkloadSetupFunction (String)\n-     - Name of the setup function to call during initialization\n-\n-   * - <Class>RxWorkloadSetupArguments (String)\n-     - Arguments passed to the setup function (space-separated string)\n-\n-   * - <Class>RxWorkloadTeardownFunction (String)\n-     - Name of the teardown function to call during close\n-\n-   * - <Class>RxWorkloadFunction (String)\n-     - Name of the runtime function called each cycle\n-\n-   * - <Class>RxWorkloadArguments (String)\n-     - Arguments passed to the runtime function\n-\n-   * - <Class>RxWorkloadPrewarm (Boolean)\n-     - Execute workload immediately when threads spawn (true) or wait for network traffic (false)\n-\n-   * - <Class>RxWorkloadSkipCount (Integer)\n-     - Skip min/max statistics and outlier updates for the first N workload iterations\n-\n-   * - <Class>RxWorkloadThreadCpu (Integer)\n-     - Comma separated list of CPU core numbers to pin the workload threads to\n-\n-   * - <Class>RxWorkloadThreadPriority (Integer)\n-     - Real-time thread priority (1-99, higher values = higher priority)\n-\n-The RX workload feature is implemented for ``TsnHigh``, ``Rtc`` and ``GenericL2`` traffic classes.\n-\n-Pointer Chasing Workload\n-------------------------\n-\n-Overview\n-^^^^^^^^\n-\n-The pointer chasing workload is designed to stress the CPU's memory hierarchy by following a chain\n-of pointers through memory in a pseudo-random pattern.\n-\n-Core Concept\n-^^^^^^^^^^^^\n-\n-The workload creates a linked list where nodes are distributed randomly throughout a large memory\n-buffer. When executed, the CPU must follow pointer chains, causing cache misses and memory stalls.\n-The buffer and span sizes can be customized to target specific hierarchies in the cache subsystem,\n-or sized sufficiently large for main memory.\n-\n-Key Components\n-^^^^^^^^^^^^^^\n-\n-1. **Data Structure (ptr_node)**\n-\n-   - 64-byte aligned union containing a pointer to the next node and a value\n-\n-2. **Setup Phase (ptr_chase_setup)**\n-\n-   - Parses buffer size and span size from command line arguments\n-   - Allocates memory and creates a randomized linked list\n-   - Buffer size: Total memory allocated\n-   - Span size: Used in conjunction with ``CACHE_LINE_SIZE`` to determine how many linked list nodes\n-     are traversed\n-\n-3. **Linked List Generation (create_linked_list)**\n-\n-   - Creates a pseudo-random chain of pointers within the specified span\n-   - Uses a seeded random number generator for reproducible results\n-   - Ensures each node is visited exactly once\n-\n-4. **Execution (run_ptr_chasing)**\n-   - Follows the pointer chain using optimized assembly code\n-   - Assembly loop continues until reaching a NULL pointer\n-\n-Assembly Implementation\n-^^^^^^^^^^^^^^^^^^^^^^^\n-\n-The core loop is implemented in assembly for precise control:\n-\n-.. code:: NASM\n-\n-   __chasing_code_loop:\n-       mov (%rax), %rax        ; Load next pointer\n-       test %rax, %rax         ; Check if NULL\n-       jne __chasing_code_loop ; Continue if not NULL\n-       ret                     ; Return when done\n-\n-ptr_chase_setup parameters\n-^^^^^^^^^^^^^^^^^^^^^^^^^^\n-\n-The ``ptr_chase_setup`` function expects two arguments:\n-\n-- **Buffer Size**: Total memory allocated (in hexadecimal)\n-- **Span Size**: Used in conjunction with ``CACHE_LINE_SIZE`` to determine how many linked list\n-  nodes are traversed. As span size approaches buffer size, it will take longer to create the\n-  LinkedList. Recommended to keep buffer size slightly larger than span size.\n-\n-In the ``tests/busypolling_1ms_rtworkload`` example, the following values are used:\n-\n-- Buffer size: 0x4A4000 (~4.6MB total allocation)\n-- Span size: 0x129000 (~1.2MB used for linked list)\n-\n-Example RTC-Testbench configuration file for pointer chasing\n-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n-\n-See ``tests/busypolling_1ms_rtworkload/mirror.yaml``.\n-\n-Build Instructions\n-^^^^^^^^^^^^^^^^^^\n-\n-From the main project build directory:\n-\n-.. code:: bash\n-\n-   make pointer_chasing                   # Build shared library\n+`Pointer Chasing <https://github.com/Linutronix/RTC-Testbench/tree/main/tests/workloads/pointer_chasing>`_\ndiff --git a/README.md b/README.md\nindex 441c7d7..8c74c21 100644\n--- a/README.md\n+++ b/README.md\n@@ -86,8 +86,10 @@ Linux RealTime Communication `Testbench` features include:\n \n The following list contains ideas for further development:\n \n+- Rework busy polling configuration\n - Add configurations for other hardware platforms (hybrid!) and NIC(s)\n - Add integration with `ClockManager`\n+- Package for Debian\n \n ## Documentation\n \n@@ -103,7 +105,7 @@ Supported by Siemens AG and Intel Corporation\n \n ## Copyright\n \n-Copyright (C) 2020-2026 Linutronix GmbH\n+Copyright (C) 2020-2025 Linutronix GmbH\n \n Copyright (C) 2024 Intel Corporation\n \ndiff --git a/REUSE.toml b/REUSE.toml\ndeleted file mode 100644\nindex 021a367..0000000\n--- a/REUSE.toml\n+++ /dev/null\n@@ -1,28 +0,0 @@\n-version = 1\n-SPDX-PackageName = \"RTC-Testbench\"\n-SPDX-PackageSupplier = \"Kurt Kanzenbach <kurt@linutronix.de>\"\n-SPDX-PackageDownloadLocation = \"https://www.github.com/Linutronix/RTC-Testbench\"\n-\n-[[annotations]]\n-path = [\".clang-format\", \".clang-tidy\", \".perltidyrc\", \".dir-locals.el\", \"CONTRIBUTING.md\", \"Documentation/Makefile\", \"Documentation/conf.py\"]\n-precedence = \"aggregate\"\n-SPDX-FileCopyrightText = \"2020-2024 Linutronix GmbH\"\n-SPDX-License-Identifier = \"BSD-2-Clause\"\n-\n-[[annotations]]\n-path = [\"Documentation/images/**.png\", \"Documentation/make.bat\", \"ISSUE_TEMPLATE.md\", \"README.md\", \"AGENTS.md\"]\n-precedence = \"aggregate\"\n-SPDX-FileCopyrightText = \"2020-2024 Linutronix GmbH\"\n-SPDX-License-Identifier = \"BSD-2-Clause\"\n-\n-[[annotations]]\n-path = [\".github/workflows/sphinx.yml\", \"devtools/conda-envs/docs_env.yaml\", \"requirements.txt\", \".github/labels.yml\", \".github/workflows/label.yml\"]\n-precedence = \"aggregate\"\n-SPDX-FileCopyrightText = \"2024 Pablo Iranzo Gómez <Pablo.Iranzo@gmail.com>\"\n-SPDX-License-Identifier = \"BSD-2-Clause\"\n-\n-[[annotations]]\n-path = \"docker/mqtt-composer/**\"\n-precedence = \"aggregate\"\n-SPDX-FileCopyrightText = \"2024 Intel Corporation\"\n-SPDX-License-Identifier = \"BSD-2-Clause\"\ndiff --git a/debian/changelog b/debian/changelog\nindex 7e90589..228cda2 100644\n--- a/debian/changelog\n+++ b/debian/changelog\n@@ -1,24 +1,3 @@\n-rtc-testbench (5.5-1) unstable; urgency=medium\n-\n-  * New upstream release.\n-  * d/copyright: refresh coverage for upstream 5.5.\n-  * d/source/lintian-overrides: document upstream release signature\n-    availability.\n-  * d/rules:\n-    - Move workload shared objects to the multiarch library directory.\n-    - Remove the unit-test CMakeLists.txt for internal logic, not the RTC\n-      benchmark tests.\n-    - Install the shared ptp.sh helper required by the packaged NIC benchmark\n-      test scripts.\n-  * d/control:\n-    - bump Standards-Version to 4.7.4.\n-    - bump debhelper compat to 14.\n-    - Add bc for shipped flow scripts.\n-    - Add libnl-3-dev, libnl-genl-3-dev, and linux-libc-dev to\n-      Build-Depends for the 5.5 NAPI tooling.\n-\n- -- Gavin Lai (賴建宇) <gavin09@gmail.com>  Sat, 12 Sep 2026 23:07:01 +0800\n-\n rtc-testbench (5.4-1) unstable; urgency=medium\n \n   * New upstream release. (Closes: #1090344)\ndiff --git a/debian/control b/debian/control\nindex 4db644b..cf43a27 100644\n--- a/debian/control\n+++ b/debian/control\n@@ -3,8 +3,7 @@ Section: utils\n Maintainer: Gavin Lai (賴建宇) <gavin09@gmail.com>\n Uploaders: SZ Lin (林上智) <szlin@debian.org>\n Build-Depends:\n- debhelper-compat (= 14),\n- dh-sequence-single-binary,\n+ debhelper-compat (= 13),\n  cmake (>= 3.10),\n  pkgconf,\n  libbpf-dev,\n@@ -12,12 +11,9 @@ Build-Depends:\n  libxdp-dev,\n  libssl-dev (>= 3.0),\n  libmosquitto-dev,\n- libnl-3-dev,\n- libnl-genl-3-dev,\n- linux-libc-dev,\n  clang,\n  llvm\n-Standards-Version: 4.7.4\n+Standards-Version: 4.7.3\n Homepage: https://github.com/Linutronix/RTC-Testbench\n Vcs-Browser: https://salsa.debian.org/debian/rtc-testbench\n Vcs-Git: https://salsa.debian.org/debian/rtc-testbench.git\n@@ -31,8 +27,7 @@ Depends:\n  ethtool,\n  rt-tests,\n  linuxptp,\n- iperf3,\n- bc\n+ iperf3\n Description: Linux RealTime Communication Testbench\n  A real-time traffic validation tool for Ethernet networks with TSN.\n  .\ndiff --git a/debian/copyright b/debian/copyright\nindex 8d0a56b..2827b3d 100644\n--- a/debian/copyright\n+++ b/debian/copyright\n@@ -35,10 +35,7 @@ Files:     .clang-format\n            src/udp_thread.h\n            src/utils.c\n            src/utils.h\n-           .perltidyrc\n-           AGENTS.md\n-Copyright: 2020-2026 Linutronix GmbH\n-           2024 Intel Corporation\n+Copyright: 2020-2024 Linutronix GmbH\n License:   BSD-2-Clause\n \n Files:     .flake8\n@@ -71,40 +68,40 @@ Files:     .github/workflows/build.yml\n            scripts/trace_syscalls.bt\n            src/hist.c\n            src/hist.h\n-           tests/busypolling_125us/flow.sh\n-           tests/busypolling_125us/mirror.yaml\n-           tests/busypolling_125us/reference.yaml\n-           tests/busypolling_250us/flow.sh\n-           tests/busypolling_250us/mirror.yaml\n-           tests/busypolling_250us/reference.yaml\n-           tests/busypolling_8ms/flow.sh\n+           tests/busypolling_125us/flow_cml.sh\n+           tests/busypolling_125us/mirror_vid100_cml.yaml\n+           tests/busypolling_125us/reference_vid100_cml.yaml\n+           tests/busypolling_250us/flow_cml.sh\n+           tests/busypolling_250us/mirror_vid100_cml.yaml\n+           tests/busypolling_250us/reference_vid100_cml.yaml\n+           tests/busypolling_8ms/flow_cml.sh\n            tests/busypolling_8ms/mirror.sh\n-           tests/busypolling_8ms/mirror.yaml\n+           tests/busypolling_8ms/mirror_vid100_cml.yaml\n            tests/busypolling_8ms/ref.sh\n-           tests/busypolling_8ms/reference.yaml\n-           tests/busypolling_i210/flow.sh\n+           tests/busypolling_8ms/reference_vid100_cml.yaml\n+           tests/busypolling_i210/flow_apl.sh\n            tests/busypolling_i210/mirror.sh\n-           tests/busypolling_i210/mirror.yaml\n+           tests/busypolling_i210/mirror_vid100_apl.yaml\n            tests/busypolling_i210/ref.sh\n-           tests/busypolling_i210/reference.yaml\n-           tests/busypolling_sp/flow.sh\n+           tests/busypolling_i210/reference_vid100_apl.yaml\n+           tests/busypolling_sp/flow_cml.sh\n            tests/imx93/profinet_rt/flow.sh\n            tests/imx93/profinet_rt/mirror.sh\n            tests/imx93/profinet_rt/mirror_vid100.yaml\n            tests/imx93/profinet_rt/ref.sh\n            tests/imx93/profinet_rt/reference_vid100.yaml\n-           tests/profinet_packet/mirror.yaml\n-           tests/profinet_packet/reference.yaml\n-           tests/profinet_rt/flow_noqbv_polling.sh\n+           tests/profinet_packet/mirror_vid100_cml.yaml\n+           tests/profinet_packet/reference_vid100_cml.yaml\n+           tests/profinet_rt/flow_cml_noqbv_polling.sh\n            tools/ci/build.sh\n            tools/ci/qa.sh\n-           tests/busypolling_1ms_rtworkload/reference.yaml\n-           tests/busypolling_1ms_rtworkload_multi/flow.sh\n-           tests/busypolling_1ms_rtworkload_multi/mirror.yaml\n-           tests/busypolling_1ms_rtworkload_multi/reference.yaml\n-           tests/i210/mirror.sh\n-           tests/i210/ref.sh\n-Copyright: 2024-2026 Linutronix GmbH\n+Copyright: 2024 Linutronix GmbH\n+License:   BSD-2-Clause\n+\n+Files:     .reuse/dep5\n+Copyright: 2020-2024 Linutronix GmbH\n+           2024 Pablo Iranzo Gómez <Pablo.Iranzo@gmail.com>\n+           2024 Intel Corporation\n License:   BSD-2-Clause\n \n Files:     CMakeLists.txt\n@@ -122,7 +119,7 @@ Files:     CMakeLists.txt\n            src/reference.c\n            src/rta_thread.c\n            src/rtc_thread.c\n-Copyright: 2020-2026 Linutronix GmbH\n+Copyright: 2020-2025 Linutronix GmbH\n License:   BSD-2-Clause\n \n Files:     Documentation/build.rst\n@@ -137,8 +134,7 @@ Files:     Documentation/build.rst\n            src/layer2_thread.c\n            src/tx_time.c\n            src/tx_time.h\n-Copyright: 2022-2026 Linutronix GmbH\n-           2025 Intel Corporation\n+Copyright: 2022-2025 Linutronix GmbH\n License:   BSD-2-Clause\n \n Files:     Documentation/index.rst\n@@ -164,7 +160,7 @@ Files:     Documentation/index.rst\n            tests/multi_middleware/i226/xdp/reference_opcua_vid200.yaml\n            tests/multi_middleware/i226/xdp/reference_opcua_vid300.yaml\n            tests/multi_middleware/i226/xdp/reference_vid100.yaml\n-Copyright: 2022-2026 Linutronix GmbH\n+Copyright: 2022-2024 Linutronix GmbH\n License:   BSD-2-Clause\n \n Files:     Documentation/mqtt.rst\n@@ -193,23 +189,22 @@ Files:     docker/Dockerfile\n            tests/ew25/mirror_vid100.yaml\n            tests/ew25/ref.sh\n            tests/ew25/reference_vid100.yaml\n-           tests/opcua_txtime/flow.sh\n-           tests/opcua_txtime/mirror.yaml\n-           tests/opcua_txtime/reference.yaml\n-           tests/profinet_txtime/flow.sh\n-           tests/profinet_txtime/mirror.yaml\n-           tests/profinet_txtime/reference.yaml\n-           tests/profinet_veth/flow.sh\n-           tests/profinet_veth/mirror_vid100.yaml\n-           tests/profinet_veth/mirror_vid200.yaml\n-           tests/profinet_veth/reference_vid100.yaml\n-           tests/profinet_veth/reference_vid200.yaml\n-           tests/profinet_veth_xdp/flow.sh\n-           tests/profinet_veth_xdp/mirror_vid100.yaml\n-           tests/profinet_veth_xdp/mirror_vid200.yaml\n-           tests/profinet_veth_xdp/reference_vid100.yaml\n-           tests/profinet_veth_xdp/reference_vid200.yaml\n-           tests/busypolling_napiconf/flow.sh\n+           tests/opcua_txtime/flow_cml.sh\n+           tests/opcua_txtime/mirror_opcua_vid200_cml.yaml\n+           tests/opcua_txtime/reference_opcua_vid200_cml.yaml\n+           tests/profinet_txtime/flow_cml.sh\n+           tests/profinet_txtime/mirror_vid100_cml.yaml\n+           tests/profinet_txtime/reference_vid100_cml.yaml\n+           tests/profinet_veth/flow_cml.sh\n+           tests/profinet_veth/mirror_vid100_cml.yaml\n+           tests/profinet_veth/mirror_vid200_cml.yaml\n+           tests/profinet_veth/reference_vid100_cml.yaml\n+           tests/profinet_veth/reference_vid200_cml.yaml\n+           tests/profinet_veth_xdp/flow_cml.sh\n+           tests/profinet_veth_xdp/mirror_vid100_cml.yaml\n+           tests/profinet_veth_xdp/mirror_vid200_cml.yaml\n+           tests/profinet_veth_xdp/reference_vid100_cml.yaml\n+           tests/profinet_veth_xdp/reference_vid200_cml.yaml\n Copyright: 2023-2025 Linutronix GmbH\n License:   BSD-2-Clause\n \n@@ -219,7 +214,7 @@ Files:     elbe/x86_64.xml\n            src/tsn_thread.c\n            src/xdp.c\n            src/xdp.h\n-Copyright: 2021-2026 Linutronix GmbH\n+Copyright: 2021-2025 Linutronix GmbH\n License:   BSD-2-Clause\n \n Files:     red?hat/rtc-testbench.spec\n@@ -234,20 +229,21 @@ License:   BSD-2-Clause\n Files:     src/log_mqtt.c\n            src/log_mqtt.h\n Copyright: 2024 Intel Corporation\n-           2025-2026 Linutronix GmbH\n+           2025 Linutronix GmbH\n License:   BSD-2-Clause\n \n Files:     src/workload.c\n            src/workload.h\n            tests/workloads/CMakeLists.txt\n+           tests/workloads/README.md\n+           tests/workloads/pointer_chasing/README.md\n            tests/workloads/pointer_chasing/pointer_chasing.c\n            tests/workloads/pointer_chasing/pointer_chasing.h\n Copyright: 2025 Intel Corporation\n-           2025-2026 Linutronix GmbH\n License:   BSD-2-Clause\n \n Files:     src/net.h\n-Copyright: 2020-2025 Linutronix GmbH\n+Copyright: 2020-2022 Linutronix GmbH\n License:   BSD-2-Clause\n \n Files:     src/net_def.h\n@@ -255,7 +251,7 @@ Copyright: 2022-2023 Linutronix GmbH\n License:   BSD-2-Clause\n \n Files:     src/ring_buffer.h\n-Copyright: 2020-2026 Linutronix GmbH\n+Copyright: 2020 Linutronix GmbH\n License:   BSD-2-Clause\n \n Files:     tests/lib/common.sh\n@@ -267,24 +263,24 @@ License:   BSD-2-Clause\n \n Files:     src/security.c\n            src/security.h\n-           tests/busypolling/flow.sh\n+           tests/busypolling/flow_cml.sh\n            tests/busypolling/mirror.sh\n-           tests/busypolling/mirror.yaml\n+           tests/busypolling/mirror_vid100_cml.yaml\n            tests/busypolling/ref.sh\n-           tests/busypolling/reference.yaml\n+           tests/busypolling/reference_vid100_cml.yaml\n            tests/busypolling_125us/mirror.sh\n            tests/busypolling_125us/ref.sh\n            tests/busypolling_250us/mirror.sh\n            tests/busypolling_250us/ref.sh\n-           tests/busypolling_1ms_rtworkload/flow.sh\n+           tests/busypolling_1ms_rtworkload/flow_cml.sh\n            tests/busypolling_1ms_rtworkload/mirror.sh\n-           tests/busypolling_1ms_rtworkload/mirror.yaml\n+           tests/busypolling_1ms_rtworkload/mirror_vid100_cml.yaml\n            tests/busypolling_1ms_rtworkload/ref.sh\n-           tests/intel_kpi/i226_xdp_1ms_32x128b_57k/reference.yaml\n+           tests/busypolling_1ms_rtworkload/reference_vid100_cml.yaml\n            tests/busypolling_sp/mirror.sh\n-           tests/busypolling_sp/mirror.yaml\n+           tests/busypolling_sp/mirror_vid100_cml.yaml\n            tests/busypolling_sp/ref.sh\n-           tests/busypolling_sp/reference.yaml\n+           tests/busypolling_sp/reference_vid100_cml.yaml\n            tests/ew23/flow.sh\n            tests/ew23/mirror.sh\n            tests/ew23/mirror_vid100.yaml\n@@ -292,54 +288,54 @@ Files:     src/security.c\n            tests/ew23/ref.sh\n            tests/ew23/reference_vid100.yaml\n            tests/ew23/reference_vid100_xdp.yaml\n-           tests/netload/flow.sh\n+           tests/netload/flow_cml.sh\n            tests/netload/mirror.sh\n-           tests/netload/mirror.yaml\n+           tests/netload/mirror_vid100_cml.yaml\n            tests/netload/ref.sh\n-           tests/netload/reference.yaml\n-           tests/opcua/flow.sh\n+           tests/netload/reference_vid100_cml.yaml\n+           tests/opcua/flow_cml.sh\n            tests/opcua/mirror.sh\n-           tests/opcua/mirror.yaml\n+           tests/opcua/mirror_opcua_vid200_cml.yaml\n            tests/opcua/ref.sh\n-           tests/opcua/reference.yaml\n-           tests/opcua_packet/flow.sh\n+           tests/opcua/reference_opcua_vid200_cml.yaml\n+           tests/opcua_packet/flow_cml.sh\n            tests/opcua_packet/mirror.sh\n            tests/opcua_packet/ref.sh\n-           tests/opcua_fpe/flow.sh\n+           tests/opcua_fpe/flow_cml.sh\n            tests/opcua_fpe/mirror.sh\n-           tests/opcua_fpe/mirror.yaml\n+           tests/opcua_fpe/mirror_opcua_vid200_cml.yaml\n            tests/opcua_fpe/ref.sh\n-           tests/opcua_fpe/reference.yaml\n+           tests/opcua_fpe/reference_opcua_vid200_cml.yaml\n            tests/opcua_txtime/mirror.sh\n            tests/opcua_txtime/ref.sh\n-           tests/profinet/flow.sh\n+           tests/profinet/flow_cml.sh\n            tests/profinet/mirror.sh\n-           tests/profinet/mirror.yaml\n+           tests/profinet/mirror_vid100_cml.yaml\n            tests/profinet/ref.sh\n-           tests/profinet/reference.yaml\n-           tests/profinet_auth/flow.sh\n+           tests/profinet/reference_vid100_cml.yaml\n+           tests/profinet_auth/flow_cml.sh\n            tests/profinet_auth/mirror.sh\n-           tests/profinet_auth/mirror.yaml\n+           tests/profinet_auth/mirror_vid100_cml.yaml\n            tests/profinet_auth/ref.sh\n-           tests/profinet_auth/reference.yaml\n-           tests/profinet_crypt/flow.sh\n+           tests/profinet_auth/reference_vid100_cml.yaml\n+           tests/profinet_crypt/flow_cml.sh\n            tests/profinet_crypt/mirror.sh\n-           tests/profinet_crypt/mirror.yaml\n+           tests/profinet_crypt/mirror_vid100_cml.yaml\n            tests/profinet_crypt/ref.sh\n-           tests/profinet_crypt/reference.yaml\n-           tests/profinet_packet/flow.sh\n+           tests/profinet_crypt/reference_vid100_cml.yaml\n+           tests/profinet_packet/flow_cml.sh\n            tests/profinet_packet/mirror.sh\n            tests/profinet_packet/ref.sh\n-           tests/profinet_rt/flow.sh\n-           tests/profinet_rt/flow_noqbv.sh\n-           tests/profinet_rt/flow_polling.sh\n-           tests/profinet_rt/irq.sh\n+           tests/profinet_rt/flow_cml.sh\n+           tests/profinet_rt/flow_cml_noqbv.sh\n+           tests/profinet_rt/flow_cml_polling.sh\n+           tests/profinet_rt/irq_cml.sh\n            tests/profinet_rt/mirror.sh\n-           tests/profinet_rt/mirror.yaml\n-           tests/profinet_rt/mirror_polling.yaml\n+           tests/profinet_rt/mirror_vid100_cml.yaml\n+           tests/profinet_rt/mirror_vid100_cml_polling.yaml\n            tests/profinet_rt/ref.sh\n-           tests/profinet_rt/reference.yaml\n-           tests/profinet_rt/reference_polling.yaml\n+           tests/profinet_rt/reference_vid100_cml.yaml\n+           tests/profinet_rt/reference_vid100_cml_polling.yaml\n            tests/profinet_sriov/flow.sh\n            tests/profinet_sriov/mirror.sh\n            tests/profinet_sriov/mirror_vid100.yaml\n@@ -357,16 +353,16 @@ Files:     src/security.c\n            tests/profinet_veth/ref.sh\n            tests/profinet_veth_xdp/mirror.sh\n            tests/profinet_veth_xdp/ref.sh\n-           tests/profinet_zc/flow.sh\n+           tests/profinet_zc/flow_cml.sh\n            tests/profinet_zc/mirror.sh\n-           tests/profinet_zc/mirror.yaml\n+           tests/profinet_zc/mirror_vid100_cml.yaml\n            tests/profinet_zc/ref.sh\n-           tests/profinet_zc/reference.yaml\n-Copyright: 2023-2026 Linutronix GmbH\n+           tests/profinet_zc/reference_vid100_cml.yaml\n+Copyright: 2023-2025 Linutronix GmbH\n License:   BSD-2-Clause\n \n Files:     src/thread.c\n-Copyright: 2020-2026 Linutronix GmbH\n+Copyright: 2020,2022 Linutronix GmbH\n License:   BSD-2-Clause\n \n Files:     src/tsn_thread.h\n@@ -375,12 +371,20 @@ Files:     src/tsn_thread.h\n Copyright: 2021-2024 Linutronix GmbH\n License:   BSD-2-Clause\n \n+Files:     src/xdp_kern_avtp_vid400.c\n+           src/xdp_kern_opcua_vid100.c\n+           src/xdp_kern_opcua_vid200.c\n+           src/xdp_kern_opcua_vid300.c\n+Copyright: 2022 Linutronix GmbH\n+License:   GPL-2.0-only OR BSD-2-Clause\n+\n Files:     src/xdp_kern_profinet_veth_dispatch.c\n Copyright: 2021-2025 Linutronix GmbH\n License:   GPL-2.0-only OR BSD-2-Clause\n \n-Files:     src/xdp_kern_tb.c\n-Copyright: 2021-2026 Linutronix GmbH\n+Files:     src/xdp_kern_profinet_vid100.c\n+           src/xdp_kern_profinet_vid200.c\n+Copyright: 2021,2022 Linutronix GmbH\n License:   GPL-2.0-only OR BSD-2-Clause\n \n Files:     src/xdp_metadata.c\n@@ -388,41 +392,41 @@ Files:     src/xdp_metadata.c\n Copyright: Intel Corporation\n License:   GPL-2.0-only OR BSD-2-Clause\n \n-Files:     tests/multi_middleware/stmmac/packet/flow.sh\n-           tests/multi_middleware/stmmac/xdp/flow.sh\n+Files:     tests/multi_middleware/stmmac/packet/flow_ehl.sh\n+           tests/multi_middleware/stmmac/xdp/flow_ehl.sh\n Copyright: 2021 Linutronix GmbH\n License:   BSD-2-Clause\n \n Files:     tests/multi_middleware/stmmac/packet/mirror.sh\n-           tests/multi_middleware/stmmac/packet/mirror_avtp_vid400.yaml\n-           tests/multi_middleware/stmmac/packet/mirror_opcua_vid200.yaml\n-           tests/multi_middleware/stmmac/packet/mirror_opcua_vid300.yaml\n+           tests/multi_middleware/stmmac/packet/mirror_avtp_vid400_ehl.yaml\n+           tests/multi_middleware/stmmac/packet/mirror_opcua_vid200_ehl.yaml\n+           tests/multi_middleware/stmmac/packet/mirror_opcua_vid300_ehl.yaml\n            tests/multi_middleware/stmmac/packet/ref.sh\n-           tests/multi_middleware/stmmac/packet/reference_avtp_vid400.yaml\n-           tests/multi_middleware/stmmac/packet/reference_opcua_vid200.yaml\n-           tests/multi_middleware/stmmac/packet/reference_opcua_vid300.yaml\n+           tests/multi_middleware/stmmac/packet/reference_avtp_vid400_ehl.yaml\n+           tests/multi_middleware/stmmac/packet/reference_opcua_vid200_ehl.yaml\n+           tests/multi_middleware/stmmac/packet/reference_opcua_vid300_ehl.yaml\n            tests/multi_middleware/stmmac/xdp/mirror.sh\n-           tests/multi_middleware/stmmac/xdp/mirror_avtp_vid400.yaml\n-           tests/multi_middleware/stmmac/xdp/mirror_opcua_vid200.yaml\n-           tests/multi_middleware/stmmac/xdp/mirror_opcua_vid300.yaml\n+           tests/multi_middleware/stmmac/xdp/mirror_avtp_vid400_ehl.yaml\n+           tests/multi_middleware/stmmac/xdp/mirror_opcua_vid200_ehl.yaml\n+           tests/multi_middleware/stmmac/xdp/mirror_opcua_vid300_ehl.yaml\n            tests/multi_middleware/stmmac/xdp/ref.sh\n-           tests/multi_middleware/stmmac/xdp/reference_avtp_vid400.yaml\n-           tests/multi_middleware/stmmac/xdp/reference_opcua_vid200.yaml\n-           tests/multi_middleware/stmmac/xdp/reference_opcua_vid300.yaml\n+           tests/multi_middleware/stmmac/xdp/reference_avtp_vid400_ehl.yaml\n+           tests/multi_middleware/stmmac/xdp/reference_opcua_vid200_ehl.yaml\n+           tests/multi_middleware/stmmac/xdp/reference_opcua_vid300_ehl.yaml\n            tests/ptm/mirror.sh\n            tests/ptm/ref.sh\n Copyright: 2022 Linutronix GmbH\n License:   BSD-2-Clause\n \n-Files:     tests/multi_middleware/stmmac/packet/mirror_vid100.yaml\n-           tests/multi_middleware/stmmac/packet/reference_vid100.yaml\n-           tests/multi_middleware/stmmac/xdp/mirror_vid100.yaml\n-           tests/multi_middleware/stmmac/xdp/reference_vid100.yaml\n+Files:     tests/multi_middleware/stmmac/packet/mirror_vid100_ehl.yaml\n+           tests/multi_middleware/stmmac/packet/reference_vid100_ehl.yaml\n+           tests/multi_middleware/stmmac/xdp/mirror_vid100_ehl.yaml\n+           tests/multi_middleware/stmmac/xdp/reference_vid100_ehl.yaml\n Copyright: Linutronix GmbH\n License:   BSD-2-Clause\n \n-Files:     tests/opcua_packet/mirror.yaml\n-           tests/opcua_packet/reference.yaml\n+Files:     tests/opcua_packet/mirror_opcua_vid200_cml.yaml\n+           tests/opcua_packet/reference_opcua_vid200_cml.yaml\n Copyright: 2023-2024 Linutronix GmbH\n License:   BSD-2-Clause\n \n@@ -430,90 +434,24 @@ Files:     tests/profinet_veth/run.sh\n            tests/profinet_veth/veth.sh\n            tests/profinet_veth_xdp/run.sh\n            tests/profinet_veth_xdp/veth.sh\n-           src/napictl.c\n Copyright: 2025 Linutronix GmbH\n License:   BSD-2-Clause\n \n-Files:     tests/ptm/flow.sh\n-           tests/ptm/mirror.yaml\n-           tests/ptm/reference.yaml\n+Files:     tests/ptm/flow_cml.sh\n+           tests/ptm/mirror_vid100_cml.yaml\n+           tests/ptm/reference_vid100_cml.yaml\n Copyright: 2021,2022 Linutronix GmbH\n License:   BSD-2-Clause\n \n-Files:     src/profinet.c\n-           src/profinet.h\n-Copyright: 2020-2026 Linutronix GmbH\n-License:   BSD-2-Clause\n-\n-Files:     tests/working_clock/ptp.sh\n-           tests/working_clock_2/ptp.sh\n-           tests/working_clock_3/ptp.sh\n-Copyright: 2021-2026 Linutronix GmbH\n-License:   BSD-2-Clause\n-\n-Files:     tests/busypolling_1ms_rtworkload_multi/mirror.sh\n-           tests/busypolling_1ms_rtworkload_multi/ref.sh\n-           tests/busypolling_napiconf/mirror.sh\n-           tests/busypolling_napiconf/mirror.yaml\n-           tests/busypolling_napiconf/ref.sh\n-           tests/busypolling_napiconf/reference.yaml\n-           tests/er26/mirror.sh\n-           tests/er26/ref.sh\n-           tests/working_clock/flow.sh\n-           tests/working_clock/mirror.sh\n-           tests/working_clock/mirror.yaml\n-           tests/working_clock/ref.sh\n-           tests/working_clock/reference.yaml\n-           tests/working_clock_2/flow.sh\n-           tests/working_clock_2/mirror.sh\n-           tests/working_clock_2/mirror.yaml\n-           tests/working_clock_2/ref.sh\n-           tests/working_clock_2/reference.yaml\n-Copyright: 2023 Linutronix GmbH\n-License:   BSD-2-Clause\n-\n-Files:     Documentation/napictl.rst\n-Copyright: 2025-2026 Linutronix GmbH\n-License:   BSD-2-Clause\n-\n-Files:     scripts/check_kconfig.pl\n-           scripts/ptp_log.pl\n-           src/tb.c\n-           src/tb.h\n-           tests/er26/flow.sh\n-           tests/er26/mirror.yaml\n-           tests/er26/reference.yaml\n-           tests/i210/flow.sh\n-           tests/i210/mirror.yaml\n-           tests/i210/reference.yaml\n-           tests/intel_kpi/i226_xdp_125us_1x64b_11k_proclat/flow_mirror.sh\n-           tests/intel_kpi/i226_xdp_125us_1x64b_11k_proclat/flow_ref.sh\n-           tests/intel_kpi/i226_xdp_125us_1x64b_11k_proclat/mirror.sh\n-           tests/intel_kpi/i226_xdp_125us_1x64b_11k_proclat/mirror.yaml\n-           tests/intel_kpi/i226_xdp_125us_1x64b_11k_proclat/ref.sh\n-           tests/intel_kpi/i226_xdp_125us_1x64b_11k_proclat/reference.yaml\n-           tests/intel_kpi/i226_xdp_1ms_1x64b_57k_proclat/flow_mirror.sh\n-           tests/intel_kpi/i226_xdp_1ms_1x64b_57k_proclat/flow_ref.sh\n-           tests/intel_kpi/i226_xdp_1ms_1x64b_57k_proclat/mirror.sh\n-           tests/intel_kpi/i226_xdp_1ms_1x64b_57k_proclat/mirror.yaml\n-           tests/intel_kpi/i226_xdp_1ms_1x64b_57k_proclat/ref.sh\n-           tests/intel_kpi/i226_xdp_1ms_1x64b_57k_proclat/reference.yaml\n-           tests/intel_kpi/i226_xdp_1ms_32x128b_57k/flow.sh\n-           tests/intel_kpi/i226_xdp_1ms_32x128b_57k/mirror.sh\n-           tests/intel_kpi/i226_xdp_1ms_32x128b_57k/mirror.yaml\n-           tests/intel_kpi/i226_xdp_1ms_32x128b_57k/ref.sh\n-           tests/unit/CMakeLists.txt\n-           tests/unit/ring_buffer_test.c\n-           tests/working_clock_3/flow.sh\n-           tests/working_clock_3/mirror.sh\n-           tests/working_clock_3/mirror.yaml\n-           tests/working_clock_3/ref.sh\n-           tests/working_clock_3/reference.yaml\n-           tests/workloads/jacobi_2d/jacobi_2d.c\n-           tests/workloads/jacobi_2d/jacobi_2d.h\n-Copyright: 2026 Linutronix GmbH\n+Files:     LICENSES/BSD-2-Clause.txt\n+Copyright: 2020-2025 Linutronix GmbH\n+           2024 Intel Corporation\n License:   BSD-2-Clause\n \n+Files:     LICENSES/GPL-2.0-only.txt\n+Copyright: 1989-1991 Free Software Foundation, Inc.\n+License:   GPL-2.0-only\n+\n Files:     debian/*\n Copyright: 2025-2026 Gavin Lai (賴建宇) <gavin09@gmail.com>\n License:   BSD-2-Clause\ndiff --git a/debian/rules b/debian/rules\nindex d1ab654..e397e28 100755\n--- a/debian/rules\n+++ b/debian/rules\n@@ -24,7 +24,6 @@ override_dh_install:\n \tif [ -d debian/rtc-testbench/usr/share/testbench ]; then \\\n \t\tmv debian/rtc-testbench/usr/share/testbench debian/rtc-testbench/usr/share/rtc-testbench; \\\n \tfi\n-\tinstall -D -m 0755 scripts/ptp.sh debian/rtc-testbench/usr/share/rtc-testbench/scripts/ptp.sh\n \tif [ -d debian/rtc-testbench/usr/share/rtc-testbench/ebpf ]; then \\\n \t\tmkdir -p debian/rtc-testbench/usr/lib/$(DEB_HOST_MULTIARCH)/rtc-testbench; \\\n \t\tmv debian/rtc-testbench/usr/share/rtc-testbench/ebpf debian/rtc-testbench/usr/lib/$(DEB_HOST_MULTIARCH)/rtc-testbench/; \\\n@@ -36,21 +35,10 @@ override_dh_install:\n \t\tln -sf /usr/lib/$(DEB_HOST_MULTIARCH)/rtc-testbench/tests/workloads/pointer_chasing/pointer_chasing.so \\\n \t\t   debian/rtc-testbench/usr/share/rtc-testbench/tests/workloads/pointer_chasing/pointer_chasing.so; \\\n \tfi\n-\tif [ -f debian/rtc-testbench/usr/share/rtc-testbench/tests/workloads/jacobi_2d/jacobi_2d.so ]; then \\\n-\t\tmkdir -p debian/rtc-testbench/usr/lib/$(DEB_HOST_MULTIARCH)/rtc-testbench/tests/workloads/jacobi_2d; \\\n-\t\tmv debian/rtc-testbench/usr/share/rtc-testbench/tests/workloads/jacobi_2d/jacobi_2d.so \\\n-\t\t   debian/rtc-testbench/usr/lib/$(DEB_HOST_MULTIARCH)/rtc-testbench/tests/workloads/jacobi_2d/; \\\n-\t\tln -sf /usr/lib/$(DEB_HOST_MULTIARCH)/rtc-testbench/tests/workloads/jacobi_2d/jacobi_2d.so \\\n-\t\t   debian/rtc-testbench/usr/share/rtc-testbench/tests/workloads/jacobi_2d/jacobi_2d.so; \\\n-\tfi\n \tif [ -d debian/rtc-testbench/usr/share/rtc-testbench/tests/workloads ]; then \\\n \t\tfind debian/rtc-testbench/usr/share/rtc-testbench/tests/workloads -type f \\\n \t\t\t\\( -name \"CMakeLists.txt\" -o -name \"README.md\" \\) -delete; \\\n \tfi\n-\tif [ -d debian/rtc-testbench/usr/share/rtc-testbench/tests/unit ]; then \\\n-\t\tfind debian/rtc-testbench/usr/share/rtc-testbench/tests/unit -type f \\\n-\t\t\t-name \"CMakeLists.txt\" -delete; \\\n-\tfi\n \tif [ -d debian/rtc-testbench/usr/share/rtc-testbench/tests ]; then \\\n \t\tfind debian/rtc-testbench/usr/share/rtc-testbench/tests -name \"*.sh\" -exec \\\n \t\t\tperl -pi -e 's#\\\\.{2}/(?:\\\\.{2}/)+build/reference\\\\b#rtc-testbench-reference#g; s#\\\\.{2}/(?:\\\\.{2}/)+build/mirror\\\\b#rtc-testbench-mirror#g' {} +; \\\ndiff --git a/debian/source/lintian-overrides b/debian/source/lintian-overrides\ndeleted file mode 100644\nindex fe8f14d..0000000\n--- a/debian/source/lintian-overrides\n+++ /dev/null\n@@ -1,8 +0,0 @@\n-# REUSE Specification 3.3 excludes REUSE.toml and license files under\n-# LICENSES/ from the files that require copyright and licensing information.\n-rtc-testbench source: file-without-copyright-information LICENSES/BSD-2-Clause.txt [debian/copyright]\n-rtc-testbench source: file-without-copyright-information LICENSES/GPL-2.0-only.txt [debian/copyright]\n-rtc-testbench source: file-without-copyright-information REUSE.toml [debian/copyright]\n-\n-# Upstream does not publish OpenPGP signatures for its GitHub releases.\n-rtc-testbench source: debian-watch-does-not-check-openpgp-signature\ndiff --git a/docker/mqtt-composer/telegraf.conf b/docker/mqtt-composer/telegraf.conf\nindex edfb899..722e8ff 100644\n--- a/docker/mqtt-composer/telegraf.conf\n+++ b/docker/mqtt-composer/telegraf.conf\n@@ -12,25 +12,13 @@\n   data_format = \"json_v2\"\n    topics=[\"testbench\"]\n    [[inputs.mqtt_consumer.json_v2]]\n-       measurement_name_path = \"testbench.MeasurementName\"\n-       timestamp_path = \"testbench.Timestamp\"\n+       measurement_name_path = \"reference.MeasurementName\"\n+       timestamp_path = \"reference.Timestamp\"\n        timestamp_format = \"unix_ns\"\n        [[inputs.mqtt_consumer.json_v2.object]]\n-           path = \"testbench.stats\"\n+           path = \"reference.stats\"\n            excluded_keys = [\"Timestamp\"]\n \n-[[inputs.mqtt_consumer]]\n-  servers = [\"tcp://mosquitto:1883\"]\n-  topics  = [\"ptp\"]\n-  data_format = \"json_v2\"\n-  [[inputs.mqtt_consumer.json_v2]]\n-    measurement_name_path = \"ptp.MeasurementName\"\n-    timestamp_path        = \"ptp.Timestamp\"\n-    timestamp_format      = \"unix_ns\"\n-    [[inputs.mqtt_consumer.json_v2.object]]\n-      path          = \"ptp.stats\"\n-      excluded_keys = [\"Timestamp\"]\n-\n [[outputs.influxdb]]\n   urls = [\"http://influxdb:8086\"]\n   database=\"testbench\"\ndiff --git a/elbe/x86_64.xml b/elbe/x86_64.xml\nindex 1d55163..7c6e1bd 100644\n--- a/elbe/x86_64.xml\n+++ b/elbe/x86_64.xml\n@@ -133,8 +133,6 @@ Run 'elbe initvm -v <hdd|sd> submit x86_64.xml' to build it.\n       <pkg>libssl-dev</pkg>\n       <pkg>dwarves</pkg>\n       <pkg>zstd</pkg>\n-      <pkg>xdp-tools</pkg>\n-      <pkg>bpftool</pkg>\n       <!-- Firmware -->\n       <pkg>firmware-linux</pkg>\n     </pkg-list>\ndiff --git a/scripts/check_kconfig.pl b/scripts/check_kconfig.pl\ndeleted file mode 100755\nindex 48313b4..0000000\n--- a/scripts/check_kconfig.pl\n+++ /dev/null\n@@ -1,228 +0,0 @@\n-#!/usr/bin/env perl\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# Check Linux kernel configuration for RTC TB.\n-#\n-\n-use strict;\n-use warnings;\n-use version;\n-use utf8;\n-use Data::Dumper;\n-use Getopt::Long;\n-use Term::ANSIColor qw(:constants);\n-\n-# config\n-my (%config, @err, @warn, @musthave, @disabled, $file, $help);\n-\n-# Must have options\n-@musthave = (\n-\n-\t# PREEMPT_RT\n-\t\"CONFIG_PREEMPT_RT\",\n-\t\"CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE\",\n-\t\"CONFIG_HIGH_RES_TIMERS\",\n-\n-\t# Networking\n-\t\"CONFIG_PTP_1588_CLOCK\",\n-\t\"CONFIG_BPF\",\n-\t\"CONFIG_BPF_SYSCALL\",\n-\t\"CONFIG_DEBUG_INFO\",\n-\t\"CONFIG_DEBUG_INFO_BTF\",\n-\t\"CONFIG_XDP_SOCKETS\",\n-\t\"CONFIG_NET_SCH_MQPRIO\",\n-\t\"CONFIG_NET_SCH_TAPRIO\",\n-\t\"CONFIG_NET_SCH_ETF\",\n-\t\"CONFIG_NET_SCH_INGRESS\",\n-\t\"CONFIG_NET_CLS\",\n-\t\"CONFIG_NET_CLS_FLOWER\",\n-\t\"CONFIG_VLAN_8021Q\",\n-\t\"CONFIG_MACVLAN\",\n-\t\"CONFIG_VETH\",\n-\t\"CONFIG_POSIX_AUX_CLOCKS\",\n-\t\"CONFIG_NET_RX_BUSY_POLL\",\n-);\n-\n-# Should be disabled\n-@disabled = (\n-\n-\t# PREEMPT_RT\n-\t\"CONFIG_NO_HZ_IDLE\",\n-\t\"CONFIG_PROVE_LOCKING\",\n-\t\"CONFIG_DRM\",    # For example, i915 may cause latency\n-);\n-\n-sub print_usage_and_die\n-{\n-\tselect STDERR;\n-\n-\tlocal $| = 1;\n-\n-\tprint <<\"EOF\";\n-usage: $0 [options] <config_file>\n-\n-options:\n-    --help, -h: Show this help text\n-EOF\n-\n-\texit -1;\n-}\n-\n-sub get_args\n-{\n-\tGetOptions(\"help\" => \\$help,) || print_usage_and_die();\n-\n-\tprint_usage_and_die() if $help;\n-\t$file = shift @ARGV;\n-\tprint_usage_and_die() unless $file;\n-\n-\treturn;\n-}\n-\n-sub kurt_err\n-{\n-\tmy ($msg) = @_;\n-\tmy (undef, undef, undef, $sub) = caller(1);\n-\tmy (undef, $file, $line, undef) = caller(0);\n-\n-\tprint_red(\"[ERROR in $sub $file:$line]: $msg\\n\");\n-\n-\texit -1;\n-}\n-\n-sub print_red\n-{\n-\tmy ($msg) = @_;\n-\n-\tprint STDERR BOLD RED \"$msg\", RESET;\n-\n-\treturn;\n-}\n-\n-sub print_yellow\n-{\n-\tmy ($msg) = @_;\n-\n-\tprint STDERR BOLD YELLOW \"$msg\", RESET;\n-\n-\treturn;\n-}\n-\n-sub print_green\n-{\n-\tmy ($msg) = @_;\n-\n-\tprint BOLD GREEN \"$msg\", RESET;\n-\n-\treturn;\n-}\n-\n-sub print_bold\n-{\n-\tmy ($msg) = @_;\n-\n-\tprint BOLD \"$msg\", RESET;\n-\n-\treturn;\n-}\n-\n-sub read_config\n-{\n-\tmy ($fh, $line);\n-\n-\topen($fh, \"<\", $file) || kurt_err(\"Failed to open file '$file': $!\");\n-\n-\twhile ($line = <$fh>) {\n-\t\tmy ($conf, $value);\n-\n-\t\tnext if $line =~ /^#/;\n-\t\tnext if $line =~ /^\\s+/;\n-\n-\t\tunless (($conf, $value) = $line =~ /(CONFIG_.*?)=(.*)/) {\n-\t\t\tkurt_err(\"Failed to parse line '$line' in config file '$file'\");\n-\t\t}\n-\n-\t\t$config{$conf} = \"enabled\";\n-\t}\n-\n-\tclose($fh);\n-\n-\treturn;\n-}\n-\n-sub add_err\n-{\n-\tmy ($msg) = @_;\n-\n-\tpush(@err, $msg);\n-\n-\treturn;\n-}\n-\n-sub add_warn\n-{\n-\tmy ($msg) = @_;\n-\n-\tpush(@warn, $msg);\n-\n-\treturn;\n-}\n-\n-sub check_config\n-{\n-\tmy (@debug);\n-\n-\tforeach my $opt (@musthave) {\n-\t\tadd_err(\"Option '$opt' is not set while it should be!\")\n-\t\t\tunless exists($config{$opt});\n-\t}\n-\n-\tforeach my $opt (@disabled) {\n-\t\tadd_err(\"Option '$opt' is set while it should not be!\")\n-\t\t\tif exists($config{$opt});\n-\t}\n-\n-\t@debug = grep { $_ =~ /DEBUG/ } keys %config;\n-\t@debug = grep { $_ !~ /FS/ } @debug;\n-\t@debug = grep { $_ !~ /ARCH_HAS/ } @debug;\n-\t@debug = grep { $_ !~ /SUPPORT/ } @debug;\n-\t@debug = grep { $_ !~ /HAVE/ } @debug;\n-\t@debug = grep { $_ ne \"CONFIG_DEBUG_INFO\" } @debug;\n-\t@debug = grep { $_ ne \"CONFIG_DEBUG_INFO_BTF\" } @debug;\n-\t@debug = grep { $_ ne \"CONFIG_DEBUG_INFO_NONE\" } @debug;\n-\n-\tforeach (@debug) {\n-\t\tadd_warn(\"DEBUG option '$_' enabled\");\n-\t}\n-\n-\treturn;\n-}\n-\n-sub main\n-{\n-\tget_args();\n-\tread_config();\n-\tcheck_config();\n-\n-\tprint_green(\"Checking config file '$file'...\\n\");\n-\n-\tprint_red(\"The following errors have been found:\\n\") if @err > 0;\n-\tfor my $msg (sort @err) {\n-\t\tprint \"\\t$msg\\n\";\n-\t}\n-\n-\tprint_yellow(\"The following warnings have been found:\\n\") if @warn > 0;\n-\tfor my $msg (sort @warn) {\n-\t\tprint \"\\t$msg\\n\";\n-\t}\n-\n-\treturn;\n-}\n-\n-main();\n-\n-exit 0;\ndiff --git a/scripts/plot_histogram.pl b/scripts/plot_histogram.pl\nindex b198cbc..405db13 100755\n--- a/scripts/plot_histogram.pl\n+++ b/scripts/plot_histogram.pl\n@@ -14,99 +14,96 @@ my ($tsn_high, $tsn_low, $rtc, $rta, $dcp, $lldp, $udp_high, $udp_low, $g2);\n \n sub write_data_file\n {\n-\tmy ($file) = @_;\n-\tmy ($fh, $line, @lines, $rtt);\n-\n-\t$tsn_high = $tsn_low = $rtc = $rta = $dcp = $lldp = $udp_high = $udp_low = $g2 = 0;\n-\t$max      = 0;\n-\t$min      = 1e9;\n-\topen($fh, \"<\", $file) or die(\"Failed to open file '$file': $!\");\n-\twhile ($line = <$fh>) {\n-\t\tmy ($c1, $c2, $c3, $c4, $c5, $c6, $c7, $c8, $c9);\n-\n-\t\tpush(@lines, $line) if $line =~ /^\\d+:/;\n-\n-\t\tif (($rtt) = $line =~ /^(\\d+):/) {\n-\t\t\t$max = $rtt if $rtt > $max;\n-\t\t\t$min = $rtt if $rtt < $min;\n-\t\t}\n-\n-\t\tif (($c1, $c2, $c3, $c4, $c5, $c6, $c7, $c8, $c9) =\n-\t\t\t$line =~\n-/^\\d+:\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)/\n-\t\t\t)\n-\t\t{\n-\t\t\t$tsn_high = 1 if $c1 != 0;\n-\t\t\t$tsn_low  = 1 if $c2 != 0;\n-\t\t\t$rtc      = 1 if $c3 != 0;\n-\t\t\t$rta      = 1 if $c4 != 0;\n-\t\t\t$dcp      = 1 if $c5 != 0;\n-\t\t\t$lldp     = 1 if $c6 != 0;\n-\t\t\t$udp_high = 1 if $c7 != 0;\n-\t\t\t$udp_low  = 1 if $c8 != 0;\n-\t\t\t$g2       = 1 if $c9 != 0;\n-\t\t}\n+    my ($file) = @_;\n+    my ($fh, $line, @lines, $rtt);\n+\n+    $tsn_high = $tsn_low = $rtc = $rta = $dcp = $lldp = $udp_high = $udp_low = $g2 = 0;\n+    $max = 0;\n+    $min = 1e9;\n+    open($fh, \"<\", $file) or die(\"Failed to open file '$file': $!\");\n+    while ($line = <$fh>) {\n+\tmy ($c1, $c2, $c3, $c4, $c5, $c6, $c7, $c8, $c9);\n+\n+\tpush(@lines, $line) if $line =~ /^\\d+:/;\n+\n+\tif (($rtt) = $line =~ /^(\\d+):/) {\n+\t    $max = $rtt if $rtt > $max;\n+\t    $min = $rtt if $rtt < $min;\n+\t}\n+\n+\tif (($c1, $c2, $c3, $c4, $c5, $c6, $c7, $c8, $c9) =\n+\t    $line =~ /^\\d+:\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)/) {\n+\t    $tsn_high = 1 if $c1 != 0;\n+\t    $tsn_low = 1 if $c2 != 0;\n+\t    $rtc = 1 if $c3 != 0;\n+\t    $rta = 1 if $c4 != 0;\n+\t    $dcp = 1 if $c5 != 0;\n+\t    $lldp = 1 if $c6 != 0;\n+\t    $udp_high = 1 if $c7 != 0;\n+\t    $udp_low = 1 if $c8 != 0;\n+\t    $g2 = 1 if $c9 != 0;\n \t}\n-\tclose($fh);\n+    }\n+    close($fh);\n \n-\topen($fh, \">\", \"data.txt\") or die;\n-\tprint $fh $_ for @lines;\n-\tclose($fh);\n+    open($fh, \">\", \"data.txt\") or die;\n+    print $fh $_ for @lines;\n+    close($fh);\n }\n \n sub plot\n {\n-\tmy ($file) = @_;\n-\tmy ($fh);\n-\n-\t$file =~ s/\\.txt$//;\n-\t$file .= \".png\";\n-\n-\t$min =~ s/^0+//;\n-\t$max =~ s/^0+//;\n-\n-\t$min -= 100;\n-\t$max += 100;\n-\n-\topen($fh, \">\", \"plot.txt\");\n-\tprint $fh \"set terminal png size 1920,1080 enhanced font 'sans-serif,20'\\n\";\n-\tprint $fh \"set output '$file';\\n\";\n-\tprint $fh \"set title 'Testbench latency plot';\\n\";\n-\tprint $fh \"set xlabel 'Latency [us]';\\n\";\n-\tprint $fh \"set ylabel 'Number of latency samples';\\n\";\n-\tprint $fh \"set ytics right;\\n\";\n-\tprint $fh \"set logscale y;\\n\";\n-\tprint $fh \"set xrange [$min:$max];\\n\";\n-\tprint $fh \"set yrange [0.8:*];\\n\";\n-\tprint $fh \"set grid back;\\n\";\n-\tprint $fh \"plot \";\n-\tprint $fh \"'data.txt' using 1:2 with fsteps lw 3 title 'TsnHigh', \"    if $tsn_high;\n-\tprint $fh \"'data.txt' using 1:3 with fsteps lw 3 title 'TsnLow', \"     if $tsn_low;\n-\tprint $fh \"'data.txt' using 1:4 with fsteps lw 3 title 'Rtc', \"        if $rtc;\n-\tprint $fh \"'data.txt' using 1:5 with fsteps lw 3 title 'Rta', \"        if $rta;\n-\tprint $fh \"'data.txt' using 1:6 with fsteps lw 3 title 'Dcp', \"        if $dcp;\n-\tprint $fh \"'data.txt' using 1:7 with fsteps lw 3 title 'Lldp', \"       if $lldp;\n-\tprint $fh \"'data.txt' using 1:8 with fsteps lw 3 title 'UdpHigh', \"    if $udp_high;\n-\tprint $fh \"'data.txt' using 1:9 with fsteps lw 3 title 'UdpLow', \"     if $udp_low;\n-\tprint $fh \"'data.txt' using 1:10 with fsteps lw 3 title 'GenericL2', \" if $g2;\n-\tprint $fh \";\\n\";\n-\tclose($fh);\n-\n-\t`gnuplot plot.txt`;\n-\tdie(\"Gnuplot failed!\") if $?;\n+    my ($file) = @_;\n+    my ($fh);\n+\n+    $file =~ s/\\.txt$//;\n+    $file .= \".png\";\n+\n+    $min =~ s/^0+//;\n+    $max =~ s/^0+//;\n+\n+    $min -= 100;\n+    $max += 100;\n+\n+    open($fh, \">\", \"plot.txt\");\n+    print $fh \"set terminal png size 1920,1080 enhanced font 'sans-serif,20'\\n\";\n+    print $fh \"set output '$file';\\n\";\n+    print $fh \"set title 'Testbench latency plot';\\n\";\n+    print $fh \"set xlabel 'Latency [us]';\\n\";\n+    print $fh \"set ylabel 'Number of latency samples';\\n\";\n+    print $fh \"set ytics right;\\n\";\n+    print $fh \"set logscale y;\\n\";\n+    print $fh \"set xrange [$min:$max];\\n\";\n+    print $fh \"set yrange [0.8:*];\\n\";\n+    print $fh \"set grid back;\\n\";\n+    print $fh \"plot \";\n+    print $fh \"'data.txt' using 1:2 with fsteps lw 3 title 'TsnHigh', \" if $tsn_high;\n+    print $fh \"'data.txt' using 1:3 with fsteps lw 3 title 'TsnLow', \" if $tsn_low;\n+    print $fh \"'data.txt' using 1:4 with fsteps lw 3 title 'Rtc', \" if $rtc;\n+    print $fh \"'data.txt' using 1:5 with fsteps lw 3 title 'Rta', \" if $rta;\n+    print $fh \"'data.txt' using 1:6 with fsteps lw 3 title 'Dcp', \" if $dcp;\n+    print $fh \"'data.txt' using 1:7 with fsteps lw 3 title 'Lldp', \" if $lldp;\n+    print $fh \"'data.txt' using 1:8 with fsteps lw 3 title 'UdpHigh', \" if $udp_high;\n+    print $fh \"'data.txt' using 1:9 with fsteps lw 3 title 'UdpLow', \" if $udp_low;\n+    print $fh \"'data.txt' using 1:10 with fsteps lw 3 title 'GenericL2', \" if $g2;\n+    print $fh \";\\n\";\n+    close($fh);\n+\n+    `gnuplot plot.txt`;\n+    die(\"Gnuplot failed!\") if $?;\n }\n \n sub cleanup\n {\n-\t`rm -f data.txt`;\n-\t`rm -f plot.txt`;\n+    `rm -f data.txt`;\n+    `rm -f plot.txt`;\n }\n \n sub print_usage\n {\n-\tselect(STDERR);\n-\tprint \"usage: plot_histogram.pl <histogram_file>\\n\";\n-\texit 0;\n+    select(STDERR);\n+    print \"usage: plot_histogram.pl <histogram_file>\\n\";\n+    exit 0;\n }\n \n $hist = $ARGV[0];\ndiff --git a/scripts/prepare_config.pl b/scripts/prepare_config.pl\nindex cb64425..6bd58e2 100755\n--- a/scripts/prepare_config.pl\n+++ b/scripts/prepare_config.pl\n@@ -16,9 +16,9 @@ my (@yaml_files, $help, $mac_src, $mac_dst, $ip_src, $ip_dst, $inf);\n \n sub print_usage\n {\n-\tselect(STDERR);\n+    select(STDERR);\n \n-\tprint <<'EOF';\n+    print <<'EOF';\n usage: prepare_config.pl [options] -- <yaml_files>\n \n options:\n@@ -35,55 +35,51 @@ EOF\n \n sub get_args\n {\n-\tGetOptions(\n-\t\t\"help\"        => \\$help,\n-\t\t\"a|mac_src=s\" => \\$mac_src,\n-\t\t\"b|mac_dst=s\" => \\$mac_dst,\n-\t\t\"c|ip_src=s\"  => \\$ip_src,\n-\t\t\"d|ip_dst=s\"  => \\$ip_dst,\n-\t\t\"i|inf=s\"     => \\$inf\n-\t) || print_usage();\n-\n-\t@yaml_files = @ARGV;\n-\tprint_usage() unless @yaml_files;\n+    GetOptions(\"help\"        => \\$help,\n+\t       \"a|mac_src=s\" => \\$mac_src,\n+\t       \"b|mac_dst=s\" => \\$mac_dst,\n+\t       \"c|ip_src=s\"  => \\$ip_src,\n+\t       \"d|ip_dst=s\"  => \\$ip_dst,\n+\t       \"i|inf=s\"     => \\$inf) || print_usage();\n+\n+    @yaml_files = @ARGV;\n+    print_usage() unless @yaml_files;\n }\n \n sub process_yaml_file\n {\n-\tmy ($file) = @_;\n-\tmy (@lines, $fh, $line, $mirror, $l2_dst, $l3_src, $l3_dst);\n+    my ($file) = @_;\n+    my (@lines, $fh, $line, $mirror, $l2_dst, $l3_src, $l3_dst);\n \n-\t$mirror = $file =~ /mirror/;\n+    $mirror = $file =~ /mirror/;\n \n-\t$l2_dst = $mirror ? $mac_src : $mac_dst;\n-\t$l3_dst = $mirror ? $ip_src  : $ip_dst;\n-\t$l3_src = $mirror ? $ip_dst  : $ip_src;\n+    $l2_dst = $mirror ? $mac_src : $mac_dst;\n+    $l3_dst = $mirror ? $ip_src : $ip_dst;\n+    $l3_src = $mirror ? $ip_dst : $ip_src;\n \n-\topen $fh, '<', $file or die;\n+    open $fh, '<', $file or die;\n \n-\twhile ($line = <$fh>) {\n-\t\t$line =~\n-s/(TsnHigh|TsnLow|Rtc|Rta|Dcp|Lldp|UdpHigh|UpdLow|GenericL2)Interface: \\w+/$1Interface: $inf/g\n-\t\t\tif $inf;\n+    while ($line = <$fh>) {\n+\t$line =~ s/(TsnHigh|TsnLow|Rtc|Rta|Dcp|Lldp|UdpHigh|UpdLow|GenericL2)Interface: \\w+/$1Interface: $inf/g\n+\t    if $inf;\n \n-\t\t$line =~\n-s/(TsnHigh|TsnLow|Rtc|Rta|Dcp|Lldp|GenericL2)Destination: [0-9a-fA-F:]+/$1Destination: $l2_dst/g\n-\t\t\tif $l2_dst;\n+\t$line =~ s/(TsnHigh|TsnLow|Rtc|Rta|Dcp|Lldp|GenericL2)Destination: [0-9a-fA-F:]+/$1Destination: $l2_dst/g\n+\t    if $l2_dst;\n \n-\t\t$line =~ s/(UdpHigh|UpdLow)Destination: \\w+/$1Destination: $l3_dst/g\n-\t\t\tif $l3_dst;\n+\t$line =~ s/(UdpHigh|UpdLow)Destination: \\w+/$1Destination: $l3_dst/g\n+\t    if $l3_dst;\n \n-\t\t$line =~ s/(UdpHigh|UpdLow)Source: \\w+/$1Source: $l3_src/g\n-\t\t\tif $l3_src;\n+\t$line =~ s/(UdpHigh|UpdLow)Source: \\w+/$1Source: $l3_src/g\n+\t    if $l3_src;\n \n-\t\tpush @lines, $line;\n-\t}\n+\tpush @lines, $line;\n+    }\n \n-\tclose $fh;\n+    close $fh;\n \n-\topen $fh, '>', $file or die;\n-\tprint $fh $_ for @lines;\n-\tclose $fh;\n+    open $fh, '>', $file or die;\n+    print $fh $_ for @lines;\n+    close $fh;\n }\n \n get_args;\ndiff --git a/scripts/ptp_log.pl b/scripts/ptp_log.pl\ndeleted file mode 100755\nindex 94ea68d..0000000\n--- a/scripts/ptp_log.pl\n+++ /dev/null\n@@ -1,110 +0,0 @@\n-#!/usr/bin/env perl\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Parses ptp4l/phc2sys logging messages and converts them into JSON objects to be published via\n-# MQTT. The purpose is to get the time sync accuracy into Grafana as well.\n-#\n-# To be used like this: ptp4l ... | ptp_log.pl -s ptp4l -m <measurement_name>\n-#                       phc2sys ... | ptp_log.pl -s phc2sys -m <measurement_name>\n-#\n-\n-use strict;\n-use warnings;\n-use Getopt::Long;\n-use Time::HiRes qw(gettimeofday);\n-use Net::MQTT::Simple;\n-use JSON::PP;\n-\n-# Config\n-my ($help, $source, $broker, $port, $topic, $measurement);\n-\n-# Default settings\n-$topic  = \"ptp\";\n-$broker = \"localhost\";\n-$port   = 1883;\n-\n-sub print_usage_and_die\n-{\n-\tselect(STDERR);\n-\n-\tprint <<'EOF';\n-usage: ptp_log.pl [options]\n-\n-options:\n-    --help, -h        | Show this help text\n-    --source, -s      | Either 'ptp4l' or 'phc2sys'\n-    --broker, -b      | IP of MQTT broker\n-    --port, -p        | Port of MQTT broker\n-    --measurement, -m | Measurement name\n-\n-Run like this: ptp4l .... | ./ptp_log.pl -s <ptp4l|phc2sys> -m <measurement_name>\n-EOF\n-\n-\texit 1;\n-}\n-\n-sub get_args\n-{\n-\tGetOptions(\n-\t\t\"help\"          => \\$help,\n-\t\t\"source=s\"      => \\$source,\n-\t\t\"broker=s\"      => \\$broker,\n-\t\t\"port=i\"        => \\$port,\n-\t\t\"measurement=s\" => \\$measurement\n-\t) || print_usage_and_die();\n-\tprint_usage_and_die() if $help;\n-\tprint_usage_and_die() unless $source;\n-\tprint_usage_and_die() unless $measurement;\n-\tprint_usage_and_die() if $source ne \"ptp4l\" && $source ne \"phc2sys\";\n-}\n-\n-sub main\n-{\n-\tmy ($mqtt, $line);\n-\n-\tget_args();\n-\n-\t$mqtt = Net::MQTT::Simple->new(\"$broker:$port\") or die \"Failed to connect to MQTT broker!\";\n-\n-\twhile ($line = <STDIN>) {\n-\t\tmy ($payload, $rms_ns, $max_ns, $offset_ns, $freq_ppb, $delay_ns, $sec, $usec);\n-\n-\t\t$max_ns = $rms_ns = $offset_ns = $freq_ppb = $delay_ns = 0;\n-\n-\t\tif ($source eq \"ptp4l\") {\n-\t\t\tnext\n-\t\t\t\tunless (($rms_ns, $max_ns, $freq_ppb, $delay_ns) = $line =~\n-\t\t\t\t/rms\\s+(-?\\d+)\\s+max\\s+(-?\\d+).*?freq\\s+(-?\\d+).*?delay\\s+(-?\\d+)/);\n-\t\t} else {\n-\t\t\tnext\n-\t\t\t\tunless (($offset_ns, $freq_ppb, $delay_ns) = $line =~\n-\t\t\t\t/offset\\s+(-?\\d+)\\s+\\w+\\s+freq\\s+([+-]?\\d+)\\s+delay\\s+(\\d+)/);\n-\t\t}\n-\n-\t\t($sec, $usec) = gettimeofday();\n-\t\t$payload = encode_json(\n-\t\t\t{\n-\t\t\t\tptp => {\n-\t\t\t\t\tTimestamp       => $sec * 1_000_000_000 + $usec * 1_000,\n-\t\t\t\t\tMeasurementName => $measurement,\n-\t\t\t\t\tstats           => {\n-\t\t\t\t\t\tmax_ns    => int($max_ns),\n-\t\t\t\t\t\trms_ns    => int($rms_ns),\n-\t\t\t\t\t\toffset_ns => int($offset_ns),\n-\t\t\t\t\t\tfreq_ppb  => int($freq_ppb),\n-\t\t\t\t\t\tdelay_ns  => int($delay_ns),\n-\t\t\t\t\t},\n-\t\t\t\t},\n-\t\t\t}\n-\t\t);\n-\n-\t\t$mqtt->publish($topic => $payload);\n-\t}\n-}\n-\n-main();\n-\n-exit 0;\ndiff --git a/scripts/stat.pl b/scripts/stat.pl\nindex eb6ccca..ea66dcb 100755\n--- a/scripts/stat.pl\n+++ b/scripts/stat.pl\n@@ -32,9 +32,9 @@ my ($server, $port, $help, $measurement, $tc, @stats);\n \n sub print_usage\n {\n-\tselect(STDERR);\n+    select(STDERR);\n \n-\tprint <<'EOF';\n+    print <<'EOF';\n usage: stat.pl [options]\n \n options:\n@@ -50,48 +50,46 @@ EOF\n \n sub get_args\n {\n-\tGetOptions(\n-\t\t\"help\"          => \\$help,\n-\t\t\"port=s\"        => \\$port,\n-\t\t\"measurement=s\" => \\$measurement,\n-\t\t\"tc=s\"          => \\$tc,\n-\t\t\"stats=s\"       => \\@stats\n-\t) || print_usage;\n-\tprint_usage if $help;\n-\tprint_usage unless $port;\n+    GetOptions(\"help\"          => \\$help,\n+\t       \"port=s\"        => \\$port,\n+\t       \"measurement=s\" => \\$measurement,\n+\t       \"tc=s\"          => \\$tc,\n+\t       \"stats=s\"       => \\@stats)\n+\t|| print_usage;\n+    print_usage if $help;\n+    print_usage unless $port;\n }\n \n sub main\n {\n-\tmy ($peer, $data, $json);\n-\n-\t# Open UDP server socket\n-\t$server = IO::Socket::IP->new(\n-\t\tLocalPort => $port,\n-\t\tProto     => \"udp\"\n-\t) or die \"Could not open UDP socket on $port: $@\\n\";\n-\n-\t# Receive datagrams\n-\twhile ($peer = $server->recv($data, 4096)) {\n-\t\t$json = decode_json $data;\n-\n-\t\tif ($measurement) {\n-\t\t\tnext unless $json->{testbench}{MeasurementName} eq $measurement;\n-\t\t}\n-\n-\t\tif ($tc) {\n-\t\t\tnext unless $json->{testbench}{stats}{TCName} eq $tc;\n-\t\t}\n-\n-\t\tprint \"Measurement: $measurement -- TC: $json->{testbench}{stats}{TCName}\\n\";\n-\t\tforeach my $stat (sort keys %{ $json->{testbench}{stats} }) {\n-\t\t\tif (scalar @stats) {\n-\t\t\t\tnext unless grep $stat =~ /$_/, @stats;\n-\t\t\t}\n-\n-\t\t\tprint \"  $stat: $json->{testbench}{stats}{$stat}\\n\";\n-\t\t}\n+    my ($peer, $data, $json);\n+\n+    # Open UDP server socket\n+    $server = IO::Socket::IP->new(LocalPort => $port,\n+\t\t\t\t  Proto     => \"udp\")\n+\tor die \"Could not open UDP socket on $port: $@\\n\";\n+\n+    # Receive datagrams\n+    while ($peer = $server->recv($data, 4096)) {\n+\t$json = decode_json $data;\n+\n+\tif ($measurement) {\n+\t    next unless $json->{reference}{MeasurementName} eq $measurement;\n+\t}\n+\n+\tif ($tc) {\n+\t    next unless $json->{reference}{stats}{TCName} eq $tc;\n+\t}\n+\n+\tprint \"Measurement: $measurement -- TC: $json->{reference}{stats}{TCName}\\n\";\n+\tforeach my $stat (sort keys %{ $json->{reference}{stats} }) {\n+\t    if (scalar @stats) {\n+\t\tnext unless grep $stat =~ /$_/, @stats;\n+\t    }\n+\n+\t    print \"  $stat: $json->{reference}{stats}{$stat}\\n\";\n \t}\n+    }\n }\n \n get_args;\ndiff --git a/src/config.c b/src/config.c\nindex 708ed6f..4c6e300 100644\n--- a/src/config.c\n+++ b/src/config.c\n@@ -1,6 +1,6 @@\n // SPDX-License-Identifier: BSD-2-Clause\n /*\n- * Copyright (C) 2020-2026 Linutronix GmbH\n+ * Copyright (C) 2020-2025 Linutronix GmbH\n  * Author Kurt Kanzenbach <kurt@linutronix.de>\n  */\n \n@@ -60,10 +60,9 @@ static enum stat_frame_type config_opt_to_type(const char *opt)\n \treturn NUM_FRAME_TYPES;\n }\n \n-bool config_is_traffic_class_active(enum stat_frame_type type)\n+bool config_is_traffic_class_active(const char *traffic_class)\n {\n-\tif (type >= NUM_FRAME_TYPES)\n-\t\treturn false;\n+\tenum stat_frame_type type = config_opt_to_type(traffic_class);\n \n \treturn app_config.classes[type].enabled &&\n \t       app_config.classes[type].num_frames_per_cycle > 0;\n@@ -145,82 +144,6 @@ static int config_parse_time(const char *value, uint64_t *ret)\n \treturn -EINVAL;\n }\n \n-static int config_parse_cpu_list(const char *value, int *array, int array_len, int *num)\n-{\n-\tint ret, i = 0;\n-\tchar *tmp, *p;\n-\n-\t/* Make a copy for strtok() */\n-\ttmp = strdup(value);\n-\tif (!tmp)\n-\t\treturn -ENOMEM;\n-\n-\tp = strtok(tmp, \" ,\");\n-\twhile (p) {\n-\t\tarray[i] = atoi(p);\n-\t\tif (array[i] < 0) {\n-\t\t\tret = -EINVAL;\n-\t\t\tgoto out;\n-\t\t}\n-\t\tp = strtok(NULL, \" ,\");\n-\t\ti++;\n-\n-\t\t/* CPU list too long? */\n-\t\tif (i >= array_len && p) {\n-\t\t\tret = -ERANGE;\n-\t\t\tgoto out;\n-\t\t}\n-\t}\n-\n-\tif (i == 0) {\n-\t\tret = -EINVAL;\n-\t\tgoto out;\n-\t}\n-\n-\t*num = i;\n-\tret = 0;\n-\n-out:\n-\tfree(tmp);\n-\treturn ret;\n-}\n-\n-static int config_parse_clockid(const char *value, clockid_t *clock)\n-{\n-\tif (!strcmp(value, \"CLOCK_TAI\")) {\n-\t\t*clock = CLOCK_TAI;\n-\t\treturn 0;\n-\t}\n-\n-\tif (!strcmp(value, \"CLOCK_MONOTONIC\")) {\n-\t\t*clock = CLOCK_MONOTONIC;\n-\t\treturn 0;\n-\t}\n-\n-\tif (!strcmp(value, \"CLOCK_REALTIME\")) {\n-\t\t*clock = CLOCK_REALTIME;\n-\t\treturn 0;\n-\t}\n-\n-\tif (!strncmp(value, \"CLOCK_AUX\", strlen(\"CLOCK_AUX\"))) {\n-\t\tint ret;\n-\t\tlong id;\n-\n-\t\tret = config_parse_int(value + strlen(\"CLOCK_AUX\"), &id);\n-\t\tif (ret)\n-\t\t\treturn -EINVAL;\n-\n-\t\tif (id < 0 || id >= MAX_AUX_CLOCKS)\n-\t\t\treturn -EINVAL;\n-\n-\t\t*clock = CLOCK_AUX + id;\n-\t\treturn 0;\n-\t}\n-\n-\t/* No valid clock id found. */\n-\treturn -EINVAL;\n-}\n-\n /* The configuration file is YAML based. Use libyaml to parse it. */\n int config_read_from_file(const char *config_file)\n {\n@@ -273,7 +196,6 @@ int config_read_from_file(const char *config_file)\n \t\t\t\t/* Save key */\n \t\t\t\tkey = strdup(value);\n \t\t\t\tif (!key) {\n-\t\t\t\t\tret = -ENOMEM;\n \t\t\t\t\tfprintf(stderr, \"No memory left!\\n\");\n \t\t\t\t\tgoto err_parse;\n \t\t\t\t}\n@@ -334,24 +256,21 @@ int config_read_from_file(const char *config_file)\n \t\t\t\t\t\t      rx_workload_enabled);\n \t\t\tCONFIG_STORE_BOOL_PARAM_CLASS(TsnHighRxWorkloadPrewarm,\n \t\t\t\t\t\t      rx_workload_prewarm);\n-\t\t\tCONFIG_STORE_ULONG_PARAM_CLASS(TsnHighRxWorkloadSkipCount,\n-\t\t\t\t\t\t       rx_workload_skip_count);\n+\t\t\tCONFIG_STORE_INT_PARAM_CLASS(TsnHighRxWorkloadSkipCount,\n+\t\t\t\t\t\t     rx_workload_skip_count);\n \t\t\tCONFIG_STORE_STRING_PARAM_CLASS(TsnHighRxWorkloadFile, workload_file);\n \t\t\tCONFIG_STORE_STRING_PARAM_CLASS(TsnHighRxWorkloadSetupFunction,\n \t\t\t\t\t\t\tworkload_setup_function);\n \t\t\tCONFIG_STORE_STRING_PARAM_CLASS(TsnHighRxWorkloadSetupArguments,\n \t\t\t\t\t\t\tworkload_setup_arguments);\n-\t\t\tCONFIG_STORE_STRING_PARAM_CLASS(TsnHighRxWorkloadTeardownFunction,\n-\t\t\t\t\t\t\tworkload_teardown_function);\n \t\t\tCONFIG_STORE_STRING_PARAM_CLASS(TsnHighRxWorkloadFunction,\n \t\t\t\t\t\t\tworkload_function);\n \t\t\tCONFIG_STORE_STRING_PARAM_CLASS(TsnHighRxWorkloadArguments,\n \t\t\t\t\t\t\tworkload_arguments);\n-\t\t\tCONFIG_STORE_CPU_LIST_PARAM_CLASS(TsnHighRxWorkloadThreadCpu,\n-\t\t\t\t\t\t\t  workload_thread_cpus);\n+\t\t\tCONFIG_STORE_INT_PARAM_CLASS(TsnHighRxWorkloadThreadCpu,\n+\t\t\t\t\t\t     workload_thread_cpu);\n \t\t\tCONFIG_STORE_INT_PARAM_CLASS(TsnHighRxWorkloadThreadPriority,\n \t\t\t\t\t\t     workload_thread_priority);\n-\n \t\t\tCONFIG_STORE_BOOL_PARAM_CLASS(TsnLowEnabled, enabled);\n \t\t\tCONFIG_STORE_BOOL_PARAM_CLASS(TsnLowXdpEnabled, xdp_enabled);\n \t\t\tCONFIG_STORE_BOOL_PARAM_CLASS(TsnLowXdpSkbMode, xdp_skb_mode);\n@@ -411,23 +330,6 @@ int config_read_from_file(const char *config_file)\n \t\t\tCONFIG_STORE_INT_PARAM_CLASS(RtcRxThreadCpu, rx_thread_cpu);\n \t\t\tCONFIG_STORE_INTERFACE_PARAM_CLASS(RtcInterface, interface);\n \t\t\tCONFIG_STORE_MAC_PARAM_CLASS(RtcDestination, l2_destination);\n-\t\t\tCONFIG_STORE_BOOL_PARAM_CLASS(RtcRxWorkloadEnabled, rx_workload_enabled);\n-\t\t\tCONFIG_STORE_BOOL_PARAM_CLASS(RtcRxWorkloadPrewarm, rx_workload_prewarm);\n-\t\t\tCONFIG_STORE_ULONG_PARAM_CLASS(RtcRxWorkloadSkipCount,\n-\t\t\t\t\t\t       rx_workload_skip_count);\n-\t\t\tCONFIG_STORE_STRING_PARAM_CLASS(RtcRxWorkloadFile, workload_file);\n-\t\t\tCONFIG_STORE_STRING_PARAM_CLASS(RtcRxWorkloadSetupFunction,\n-\t\t\t\t\t\t\tworkload_setup_function);\n-\t\t\tCONFIG_STORE_STRING_PARAM_CLASS(RtcRxWorkloadSetupArguments,\n-\t\t\t\t\t\t\tworkload_setup_arguments);\n-\t\t\tCONFIG_STORE_STRING_PARAM_CLASS(RtcRxWorkloadTeardownFunction,\n-\t\t\t\t\t\t\tworkload_teardown_function);\n-\t\t\tCONFIG_STORE_STRING_PARAM_CLASS(RtcRxWorkloadFunction, workload_function);\n-\t\t\tCONFIG_STORE_STRING_PARAM_CLASS(RtcRxWorkloadArguments, workload_arguments);\n-\t\t\tCONFIG_STORE_CPU_LIST_PARAM_CLASS(RtcRxWorkloadThreadCpu,\n-\t\t\t\t\t\t\t  workload_thread_cpus);\n-\t\t\tCONFIG_STORE_INT_PARAM_CLASS(RtcRxWorkloadThreadPriority,\n-\t\t\t\t\t\t     workload_thread_priority);\n \n \t\t\tCONFIG_STORE_BOOL_PARAM_CLASS(RtaEnabled, enabled);\n \t\t\tCONFIG_STORE_BOOL_PARAM_CLASS(RtaXdpEnabled, xdp_enabled);\n@@ -558,27 +460,6 @@ int config_read_from_file(const char *config_file)\n \t\t\tCONFIG_STORE_INT_PARAM_CLASS(GenericL2RxThreadCpu, rx_thread_cpu);\n \t\t\tCONFIG_STORE_INTERFACE_PARAM_CLASS(GenericL2Interface, interface);\n \t\t\tCONFIG_STORE_MAC_PARAM_CLASS(GenericL2Destination, l2_destination);\n-\t\t\tCONFIG_STORE_BOOL_PARAM_CLASS(GenericL2RxWorkloadEnabled,\n-\t\t\t\t\t\t      rx_workload_enabled);\n-\t\t\tCONFIG_STORE_BOOL_PARAM_CLASS(GenericL2RxWorkloadPrewarm,\n-\t\t\t\t\t\t      rx_workload_prewarm);\n-\t\t\tCONFIG_STORE_ULONG_PARAM_CLASS(GenericL2RxWorkloadSkipCount,\n-\t\t\t\t\t\t       rx_workload_skip_count);\n-\t\t\tCONFIG_STORE_STRING_PARAM_CLASS(GenericL2RxWorkloadFile, workload_file);\n-\t\t\tCONFIG_STORE_STRING_PARAM_CLASS(GenericL2RxWorkloadSetupFunction,\n-\t\t\t\t\t\t\tworkload_setup_function);\n-\t\t\tCONFIG_STORE_STRING_PARAM_CLASS(GenericL2RxWorkloadSetupArguments,\n-\t\t\t\t\t\t\tworkload_setup_arguments);\n-\t\t\tCONFIG_STORE_STRING_PARAM_CLASS(GenericL2RxWorkloadTeardownFunction,\n-\t\t\t\t\t\t\tworkload_teardown_function);\n-\t\t\tCONFIG_STORE_STRING_PARAM_CLASS(GenericL2RxWorkloadFunction,\n-\t\t\t\t\t\t\tworkload_function);\n-\t\t\tCONFIG_STORE_STRING_PARAM_CLASS(GenericL2RxWorkloadArguments,\n-\t\t\t\t\t\t\tworkload_arguments);\n-\t\t\tCONFIG_STORE_CPU_LIST_PARAM_CLASS(GenericL2RxWorkloadThreadCpu,\n-\t\t\t\t\t\t\t  workload_thread_cpus);\n-\t\t\tCONFIG_STORE_INT_PARAM_CLASS(GenericL2RxWorkloadThreadPriority,\n-\t\t\t\t\t\t     workload_thread_priority);\n \n \t\t\tCONFIG_STORE_INT_PARAM(LogThreadPriority, log_thread_priority);\n \t\t\tCONFIG_STORE_INT_PARAM(LogThreadCpu, log_thread_cpu);\n@@ -620,10 +501,8 @@ int config_read_from_file(const char *config_file)\n \t\t\tif (!strcmp(key, \"ApplicationBaseStartTimeNS\"))\n \t\t\t\tbase_time_seen = true;\n \n-\t\t\tif (key) {\n+\t\t\tif (key)\n \t\t\t\tfree(key);\n-\t\t\t\tkey = NULL;\n-\t\t\t}\n \n \t\tdefault:\n \t\t\tbreak;\n@@ -645,11 +524,7 @@ int config_read_from_file(const char *config_file)\n \tif (app_config.application_clock_id != CLOCK_TAI && !base_time_seen) {\n \t\tstruct timespec current;\n \n-\t\tret = clock_gettime(app_config.application_clock_id, &current);\n-\t\tif (ret) {\n-\t\t\tfprintf(stderr, \"CONFIG: clock_gettime() failed: %s!\\n\", strerror(errno));\n-\t\t\tgoto err_parse;\n-\t\t}\n+\t\tclock_gettime(app_config.application_clock_id, &current);\n \t\tapp_config.application_base_start_time_ns = (current.tv_sec + 30) * NSEC_PER_SEC;\n \t}\n \n@@ -671,8 +546,8 @@ void config_print_values(void)\n \n \tprintf(\"--------------------------------------------------------------------------------\"\n \t       \"\\n\");\n-\tprintf(\"ApplicationClockId=\");\n-\tprint_clockid(app_config.application_clock_id);\n+\tprintf(\"ApplicationClockId=%s\\n\",\n+\t       app_config.application_clock_id == CLOCK_TAI ? \"CLOCK_TAI\" : \"CLOCK_MONOTONIC\");\n \tprintf(\"ApplicationBaseCycleTimeNS=%\" PRIu64 \"\\n\",\n \t       app_config.application_base_cycle_time_ns);\n \tprintf(\"ApplicationBaseStartTimeNS=%\" PRIu64 \"\\n\",\n@@ -686,333 +561,294 @@ void config_print_values(void)\n \t       \"\\n\");\n \n \tconf = &app_config.classes[TSN_HIGH_FRAME_TYPE];\n-\tif (config_is_traffic_class_active(TSN_HIGH_FRAME_TYPE)) {\n-\t\tprintf(\"TsnHighEnabled=%s\\n\", conf->enabled ? \"True\" : \"False\");\n-\t\tprintf(\"TsnHighRxMirrorEnabled=%s\\n\", conf->rx_mirror_enabled ? \"True\" : \"False\");\n-\t\tprintf(\"TsnHighXdpEnabled=%s\\n\", conf->xdp_enabled ? \"True\" : \"False\");\n-\t\tprintf(\"TsnHighXdpSkbMode=%s\\n\", conf->xdp_skb_mode ? \"True\" : \"False\");\n-\t\tprintf(\"TsnHighXdpZcMode=%s\\n\", conf->xdp_zc_mode ? \"True\" : \"False\");\n-\t\tprintf(\"TsnHighXdpWakeupMode=%s\\n\", conf->xdp_wakeup_mode ? \"True\" : \"False\");\n-\t\tprintf(\"TsnHighXdpBusyPollMode=%s\\n\", conf->xdp_busy_poll_mode ? \"True\" : \"False\");\n-\t\tprintf(\"TsnHighTxTimeEnabled=%s\\n\", conf->tx_time_enabled ? \"True\" : \"False\");\n-\t\tprintf(\"TsnHighIgnoreRxErrors=%s\\n\", conf->ignore_rx_errors ? \"True\" : \"False\");\n-\t\tprintf(\"TsnHighTxTimeOffsetNS=%\" PRIu64 \"\\n\", conf->tx_time_offset_ns);\n-\t\tprintf(\"TsnHighTxTimeStampEnabled=%s\\n\",\n-\t\t       conf->tx_hwtstamp_enabled ? \"True\" : \"False\");\n-\t\tprintf(\"TsnHighVid=%d\\n\", conf->vid);\n-\t\tprintf(\"TsnHighPcp=%d\\n\", conf->pcp);\n-\t\tprintf(\"TsnHighNumFramesPerCycle=%zu\\n\", conf->num_frames_per_cycle);\n-\t\tprintf(\"TsnHighPayloadPattern=\");\n-\t\tprint_payload_pattern(conf->payload_pattern, conf->payload_pattern_length);\n-\t\tprintf(\"TsnHighFrameLength=%zu\\n\", conf->frame_length);\n-\t\tprintf(\"TsnHighSecurityMode=%s\\n\", security_mode_to_string(conf->security_mode));\n-\t\tprintf(\"TsnHighSecurityAlgorithm=%s\\n\",\n-\t\t       security_algorithm_to_string(conf->security_algorithm));\n-\t\tprintf(\"TsnHighSecurityKey=%s\\n\", conf->security_key);\n-\t\tprintf(\"TsnHighSecurityIvPrefix=%s\\n\", conf->security_iv_prefix);\n-\t\tprintf(\"TsnHighRxQueue=%d\\n\", conf->rx_queue);\n-\t\tprintf(\"TsnHighTxQueue=%d\\n\", conf->tx_queue);\n-\t\tprintf(\"TsnHighSocketPriority=%d\\n\", conf->socket_priority);\n-\t\tprintf(\"TsnHighTxThreadPriority=%d\\n\", conf->tx_thread_priority);\n-\t\tprintf(\"TsnHighRxThreadPriority=%d\\n\", conf->rx_thread_priority);\n-\t\tprintf(\"TsnHighTxThreadCpu=%d\\n\", conf->tx_thread_cpu);\n-\t\tprintf(\"TsnHighRxThreadCpu=%d\\n\", conf->rx_thread_cpu);\n-\t\tprintf(\"TsnHighInterface=%s\\n\", conf->interface);\n-\t\tprintf(\"TsnHighDestination=\");\n-\t\tprint_mac_address(conf->l2_destination);\n-\t\tprintf(\"TsnHighRxWorkloadEnabled=%s\\n\",\n-\t\t       conf->rx_workload_enabled ? \"True\" : \"False\");\n-\t\tprintf(\"TsnHighRxWorkloadPrewarm=%s\\n\",\n-\t\t       conf->rx_workload_prewarm ? \"True\" : \"False\");\n-\t\tprintf(\"TsnHighRxWorkloadSkipCount=%\" PRIu64 \"\\n\", conf->rx_workload_skip_count);\n-\t\tprintf(\"TsnHighRxWorkloadFile=%s\\n\", conf->workload_file);\n-\t\tprintf(\"TsnHighRxWorkloadSetupFunction=%s\\n\", conf->workload_setup_function);\n-\t\tprintf(\"TsnHighRxWorkloadSetupArguments=%s\\n\", conf->workload_setup_arguments);\n-\t\tprintf(\"TsnHighRxWorkloadTeardownFunction=%s\\n\", conf->workload_teardown_function);\n-\t\tprintf(\"TsnHighRxWorkloadFunction=%s\\n\", conf->workload_function);\n-\t\tprintf(\"TsnHighRxWorkloadArguments=%s\\n\", conf->workload_arguments);\n-\t\tprintf(\"TsnHighRxWorkloadThreadCpu=\");\n-\t\tprint_cpu_list(conf->workload_thread_cpus, conf->workload_thread_cpus_num);\n-\t\tprintf(\"TsnHighRxWorkloadThreadPriority=%d\\n\", conf->workload_thread_priority);\n-\t\tprintf(\"---------------------------------------------------------------------------\"\n-\t\t       \"-----\\n\");\n-\t}\n+\tprintf(\"TsnHighEnabled=%s\\n\", conf->enabled ? \"True\" : \"False\");\n+\tprintf(\"TsnHighRxMirrorEnabled=%s\\n\", conf->rx_mirror_enabled ? \"True\" : \"False\");\n+\tprintf(\"TsnHighXdpEnabled=%s\\n\", conf->xdp_enabled ? \"True\" : \"False\");\n+\tprintf(\"TsnHighXdpSkbMode=%s\\n\", conf->xdp_skb_mode ? \"True\" : \"False\");\n+\tprintf(\"TsnHighXdpZcMode=%s\\n\", conf->xdp_zc_mode ? \"True\" : \"False\");\n+\tprintf(\"TsnHighXdpWakeupMode=%s\\n\", conf->xdp_wakeup_mode ? \"True\" : \"False\");\n+\tprintf(\"TsnHighXdpBusyPollMode=%s\\n\", conf->xdp_busy_poll_mode ? \"True\" : \"False\");\n+\tprintf(\"TsnHighTxTimeEnabled=%s\\n\", conf->tx_time_enabled ? \"True\" : \"False\");\n+\tprintf(\"TsnHighIgnoreRxErrors=%s\\n\", conf->ignore_rx_errors ? \"True\" : \"False\");\n+\tprintf(\"TsnHighTxTimeOffsetNS=%\" PRIu64 \"\\n\", conf->tx_time_offset_ns);\n+\tprintf(\"TsnHighTxTimeStampEnabled=%s\\n\", conf->tx_hwtstamp_enabled ? \"True\" : \"False\");\n+\tprintf(\"TsnHighVid=%d\\n\", conf->vid);\n+\tprintf(\"TsnHighPcp=%d\\n\", conf->pcp);\n+\tprintf(\"TsnHighNumFramesPerCycle=%zu\\n\", conf->num_frames_per_cycle);\n+\tprintf(\"TsnHighPayloadPattern=\");\n+\tprint_payload_pattern(conf->payload_pattern, conf->payload_pattern_length);\n+\tprintf(\"\\n\");\n+\tprintf(\"TsnHighFrameLength=%zu\\n\", conf->frame_length);\n+\tprintf(\"TsnHighSecurityMode=%s\\n\", security_mode_to_string(conf->security_mode));\n+\tprintf(\"TsnHighSecurityAlgorithm=%s\\n\",\n+\t       security_algorithm_to_string(conf->security_algorithm));\n+\tprintf(\"TsnHighSecurityKey=%s\\n\", conf->security_key);\n+\tprintf(\"TsnHighSecurityIvPrefix=%s\\n\", conf->security_iv_prefix);\n+\tprintf(\"TsnHighRxQueue=%d\\n\", conf->rx_queue);\n+\tprintf(\"TsnHighTxQueue=%d\\n\", conf->tx_queue);\n+\tprintf(\"TsnHighSocketPriority=%d\\n\", conf->socket_priority);\n+\tprintf(\"TsnHighTxThreadPriority=%d\\n\", conf->tx_thread_priority);\n+\tprintf(\"TsnHighRxThreadPriority=%d\\n\", conf->rx_thread_priority);\n+\tprintf(\"TsnHighTxThreadCpu=%d\\n\", conf->tx_thread_cpu);\n+\tprintf(\"TsnHighRxThreadCpu=%d\\n\", conf->rx_thread_cpu);\n+\tprintf(\"TsnHighInterface=%s\\n\", conf->interface);\n+\tprintf(\"TsnHighDestination=\");\n+\tprint_mac_address(conf->l2_destination);\n+\tprintf(\"\\n\");\n+\tprintf(\"TsnHighRxWorkloadEnabled=%s\\n\", conf->rx_workload_enabled ? \"True\" : \"False\");\n+\tprintf(\"TsnHighRxWorkloadPrewarm=%s\\n\", conf->rx_workload_prewarm ? \"True\" : \"False\");\n+\tprintf(\"TsnHighRxWorkloadSkipCount=%d\\n\", conf->rx_workload_skip_count);\n+\tprintf(\"TsnHighRxWorkloadFile=%s\\n\", conf->workload_file);\n+\tprintf(\"TsnHighRxWorkloadSetupFunction=%s\\n\", conf->workload_setup_function);\n+\tprintf(\"TsnHighRxWorkloadSetupArguments=%s\\n\", conf->workload_setup_arguments);\n+\tprintf(\"TsnHighRxWorkloadFunction=%s\\n\", conf->workload_function);\n+\tprintf(\"TsnHighRxWorkloadArguments=%s\\n\", conf->workload_arguments);\n+\tprintf(\"TsnHighRxWorkloadThreadCpu=%d\\n\", conf->workload_thread_cpu);\n+\tprintf(\"TsnHighRxWorkloadThreadPriority=%d\\n\", conf->workload_thread_priority);\n+\tprintf(\"--------------------------------------------------------------------------------\"\n+\t       \"\\n\");\n \n \tconf = &app_config.classes[TSN_LOW_FRAME_TYPE];\n-\tif (config_is_traffic_class_active(TSN_LOW_FRAME_TYPE)) {\n-\t\tprintf(\"TsnLowEnabled=%s\\n\", conf->enabled ? \"True\" : \"False\");\n-\t\tprintf(\"TsnLowRxMirrorEnabled=%s\\n\", conf->rx_mirror_enabled ? \"True\" : \"False\");\n-\t\tprintf(\"TsnLowXdpEnabled=%s\\n\", conf->xdp_enabled ? \"True\" : \"False\");\n-\t\tprintf(\"TsnLowXdpSkbMode=%s\\n\", conf->xdp_skb_mode ? \"True\" : \"False\");\n-\t\tprintf(\"TsnLowXdpZcMode=%s\\n\", conf->xdp_zc_mode ? \"True\" : \"False\");\n-\t\tprintf(\"TsnLowXdpWakeupMode=%s\\n\", conf->xdp_wakeup_mode ? \"True\" : \"False\");\n-\t\tprintf(\"TsnLowXdpBusyPollMode=%s\\n\", conf->xdp_busy_poll_mode ? \"True\" : \"False\");\n-\t\tprintf(\"TsnLowTxTimeEnabled=%s\\n\", conf->tx_time_enabled ? \"True\" : \"False\");\n-\t\tprintf(\"TsnLowIgnoreRxErrors=%s\\n\", conf->ignore_rx_errors ? \"True\" : \"False\");\n-\t\tprintf(\"TsnLowTxTimeOffsetNS=%\" PRIu64 \"\\n\", conf->tx_time_offset_ns);\n-\t\tprintf(\"TsnLowTxTimeStampEnabled=%s\\n\",\n-\t\t       conf->tx_hwtstamp_enabled ? \"True\" : \"False\");\n-\t\tprintf(\"TsnLowVid=%d\\n\", conf->vid);\n-\t\tprintf(\"TsnLowPcp=%d\\n\", conf->pcp);\n-\t\tprintf(\"TsnLowNumFramesPerCycle=%zu\\n\", conf->num_frames_per_cycle);\n-\t\tprintf(\"TsnLowPayloadPattern=\");\n-\t\tprint_payload_pattern(conf->payload_pattern, conf->payload_pattern_length);\n-\t\tprintf(\"TsnLowFrameLength=%zu\\n\", conf->frame_length);\n-\t\tprintf(\"TsnLowSecurityMode=%s\\n\", security_mode_to_string(conf->security_mode));\n-\t\tprintf(\"TsnLowSecurityAlgorithm=%s\\n\",\n-\t\t       security_algorithm_to_string(conf->security_algorithm));\n-\t\tprintf(\"TsnLowSecurityKey=%s\\n\", conf->security_key);\n-\t\tprintf(\"TsnLowSecurityIvPrefix=%s\\n\", conf->security_iv_prefix);\n-\t\tprintf(\"TsnLowRxQueue=%d\\n\", conf->rx_queue);\n-\t\tprintf(\"TsnLowTxQueue=%d\\n\", conf->tx_queue);\n-\t\tprintf(\"TsnLowSocketPriority=%d\\n\", conf->socket_priority);\n-\t\tprintf(\"TsnLowTxThreadPriority=%d\\n\", conf->tx_thread_priority);\n-\t\tprintf(\"TsnLowRxThreadPriority=%d\\n\", conf->rx_thread_priority);\n-\t\tprintf(\"TsnLowTxThreadCpu=%d\\n\", conf->tx_thread_cpu);\n-\t\tprintf(\"TsnLowRxThreadCpu=%d\\n\", conf->rx_thread_cpu);\n-\t\tprintf(\"TsnLowInterface=%s\\n\", conf->interface);\n-\t\tprintf(\"TsnLowDestination=\");\n-\t\tprint_mac_address(conf->l2_destination);\n-\t\tprintf(\"---------------------------------------------------------------------------\"\n-\t\t       \"-----\\n\");\n-\t}\n+\tprintf(\"TsnLowEnabled=%s\\n\", conf->enabled ? \"True\" : \"False\");\n+\tprintf(\"TsnLowRxMirrorEnabled=%s\\n\", conf->rx_mirror_enabled ? \"True\" : \"False\");\n+\tprintf(\"TsnLowXdpEnabled=%s\\n\", conf->xdp_enabled ? \"True\" : \"False\");\n+\tprintf(\"TsnLowXdpSkbMode=%s\\n\", conf->xdp_skb_mode ? \"True\" : \"False\");\n+\tprintf(\"TsnLowXdpZcMode=%s\\n\", conf->xdp_zc_mode ? \"True\" : \"False\");\n+\tprintf(\"TsnLowXdpWakeupMode=%s\\n\", conf->xdp_wakeup_mode ? \"True\" : \"False\");\n+\tprintf(\"TsnLowXdpBusyPollMode=%s\\n\", conf->xdp_busy_poll_mode ? \"True\" : \"False\");\n+\tprintf(\"TsnLowTxTimeEnabled=%s\\n\", conf->tx_time_enabled ? \"True\" : \"False\");\n+\tprintf(\"TsnLowIgnoreRxErrors=%s\\n\", conf->ignore_rx_errors ? \"True\" : \"False\");\n+\tprintf(\"TsnLowTxTimeOffsetNS=%\" PRIu64 \"\\n\", conf->tx_time_offset_ns);\n+\tprintf(\"TsnLowTxTimeStampEnabled=%s\\n\", conf->tx_hwtstamp_enabled ? \"True\" : \"False\");\n+\tprintf(\"TsnLowVid=%d\\n\", conf->vid);\n+\tprintf(\"TsnLowPcp=%d\\n\", conf->pcp);\n+\tprintf(\"TsnLowNumFramesPerCycle=%zu\\n\", conf->num_frames_per_cycle);\n+\tprintf(\"TsnLowPayloadPattern=\");\n+\tprint_payload_pattern(conf->payload_pattern, conf->payload_pattern_length);\n+\tprintf(\"\\n\");\n+\tprintf(\"TsnLowFrameLength=%zu\\n\", conf->frame_length);\n+\tprintf(\"TsnLowSecurityMode=%s\\n\", security_mode_to_string(conf->security_mode));\n+\tprintf(\"TsnLowSecurityAlgorithm=%s\\n\",\n+\t       security_algorithm_to_string(conf->security_algorithm));\n+\tprintf(\"TsnLowSecurityKey=%s\\n\", conf->security_key);\n+\tprintf(\"TsnLowSecurityIvPrefix=%s\\n\", conf->security_iv_prefix);\n+\tprintf(\"TsnLowRxQueue=%d\\n\", conf->rx_queue);\n+\tprintf(\"TsnLowTxQueue=%d\\n\", conf->tx_queue);\n+\tprintf(\"TsnLowSocketPriority=%d\\n\", conf->socket_priority);\n+\tprintf(\"TsnLowTxThreadPriority=%d\\n\", conf->tx_thread_priority);\n+\tprintf(\"TsnLowRxThreadPriority=%d\\n\", conf->rx_thread_priority);\n+\tprintf(\"TsnLowTxThreadCpu=%d\\n\", conf->tx_thread_cpu);\n+\tprintf(\"TsnLowRxThreadCpu=%d\\n\", conf->rx_thread_cpu);\n+\tprintf(\"TsnLowInterface=%s\\n\", conf->interface);\n+\tprintf(\"TsnLowDestination=\");\n+\tprint_mac_address(conf->l2_destination);\n+\tprintf(\"\\n\");\n+\tprintf(\"--------------------------------------------------------------------------------\"\n+\t       \"\\n\");\n \n \tconf = &app_config.classes[RTC_FRAME_TYPE];\n-\tif (config_is_traffic_class_active(RTC_FRAME_TYPE)) {\n-\t\tprintf(\"RtcEnabled=%s\\n\", conf->enabled ? \"True\" : \"False\");\n-\t\tprintf(\"RtcRxMirrorEnabled=%s\\n\", conf->rx_mirror_enabled ? \"True\" : \"False\");\n-\t\tprintf(\"RtcXdpEnabled=%s\\n\", conf->xdp_enabled ? \"True\" : \"False\");\n-\t\tprintf(\"RtcXdpSkbMode=%s\\n\", conf->xdp_skb_mode ? \"True\" : \"False\");\n-\t\tprintf(\"RtcXdpZcMode=%s\\n\", conf->xdp_zc_mode ? \"True\" : \"False\");\n-\t\tprintf(\"RtcXdpWakeupMode=%s\\n\", conf->xdp_wakeup_mode ? \"True\" : \"False\");\n-\t\tprintf(\"RtcXdpBusyPollMode=%s\\n\", conf->xdp_busy_poll_mode ? \"True\" : \"False\");\n-\t\tprintf(\"RtcIgnoreRxErrors=%s\\n\", conf->ignore_rx_errors ? \"True\" : \"False\");\n-\t\tprintf(\"RtcTxTimeStampEnabled=%s\\n\", conf->tx_hwtstamp_enabled ? \"True\" : \"False\");\n-\t\tprintf(\"RtcVid=%d\\n\", conf->vid);\n-\t\tprintf(\"RtcPcp=%d\\n\", conf->pcp);\n-\t\tprintf(\"RtcNumFramesPerCycle=%zu\\n\", conf->num_frames_per_cycle);\n-\t\tprintf(\"RtcPayloadPattern=\");\n-\t\tprint_payload_pattern(conf->payload_pattern, conf->payload_pattern_length);\n-\t\tprintf(\"RtcFrameLength=%zu\\n\", conf->frame_length);\n-\t\tprintf(\"RtcSecurityMode=%s\\n\", security_mode_to_string(conf->security_mode));\n-\t\tprintf(\"RtcSecurityAlgorithm=%s\\n\",\n-\t\t       security_algorithm_to_string(conf->security_algorithm));\n-\t\tprintf(\"RtcSecurityKey=%s\\n\", conf->security_key);\n-\t\tprintf(\"RtcSecurityIvPrefix=%s\\n\", conf->security_iv_prefix);\n-\t\tprintf(\"RtcRxQueue=%d\\n\", conf->rx_queue);\n-\t\tprintf(\"RtcTxQueue=%d\\n\", conf->tx_queue);\n-\t\tprintf(\"RtcSocketPriority=%d\\n\", conf->socket_priority);\n-\t\tprintf(\"RtcTxThreadPriority=%d\\n\", conf->tx_thread_priority);\n-\t\tprintf(\"RtcRxThreadPriority=%d\\n\", conf->rx_thread_priority);\n-\t\tprintf(\"RtcTxThreadCpu=%d\\n\", conf->tx_thread_cpu);\n-\t\tprintf(\"RtcRxThreadCpu=%d\\n\", conf->rx_thread_cpu);\n-\t\tprintf(\"RtcInterface=%s\\n\", conf->interface);\n-\t\tprintf(\"RtcDestination=\");\n-\t\tprint_mac_address(conf->l2_destination);\n-\t\tprintf(\"RtcRxWorkloadEnabled=%s\\n\", conf->rx_workload_enabled ? \"True\" : \"False\");\n-\t\tprintf(\"RtcRxWorkloadPrewarm=%s\\n\", conf->rx_workload_prewarm ? \"True\" : \"False\");\n-\t\tprintf(\"RtcRxWorkloadSkipCount=%\" PRIu64 \"\\n\", conf->rx_workload_skip_count);\n-\t\tprintf(\"RtcRxWorkloadFile=%s\\n\", conf->workload_file);\n-\t\tprintf(\"RtcRxWorkloadSetupFunction=%s\\n\", conf->workload_setup_function);\n-\t\tprintf(\"RtcRxWorkloadSetupArguments=%s\\n\", conf->workload_setup_arguments);\n-\t\tprintf(\"RtcRxWorkloadTeardownFunction=%s\\n\", conf->workload_teardown_function);\n-\t\tprintf(\"RtcRxWorkloadFunction=%s\\n\", conf->workload_function);\n-\t\tprintf(\"RtcRxWorkloadArguments=%s\\n\", conf->workload_arguments);\n-\t\tprintf(\"RtcRxWorkloadThreadCpu=\");\n-\t\tprint_cpu_list(conf->workload_thread_cpus, conf->workload_thread_cpus_num);\n-\t\tprintf(\"RtcRxWorkloadThreadPriority=%d\\n\", conf->workload_thread_priority);\n-\t\tprintf(\"---------------------------------------------------------------------------\"\n-\t\t       \"-----\\n\");\n-\t}\n+\tprintf(\"RtcEnabled=%s\\n\", conf->enabled ? \"True\" : \"False\");\n+\tprintf(\"RtcRxMirrorEnabled=%s\\n\", conf->rx_mirror_enabled ? \"True\" : \"False\");\n+\tprintf(\"RtcXdpEnabled=%s\\n\", conf->xdp_enabled ? \"True\" : \"False\");\n+\tprintf(\"RtcXdpSkbMode=%s\\n\", conf->xdp_skb_mode ? \"True\" : \"False\");\n+\tprintf(\"RtcXdpZcMode=%s\\n\", conf->xdp_zc_mode ? \"True\" : \"False\");\n+\tprintf(\"RtcXdpWakeupMode=%s\\n\", conf->xdp_wakeup_mode ? \"True\" : \"False\");\n+\tprintf(\"RtcXdpBusyPollMode=%s\\n\", conf->xdp_busy_poll_mode ? \"True\" : \"False\");\n+\tprintf(\"RtcIgnoreRxErrors=%s\\n\", conf->ignore_rx_errors ? \"True\" : \"False\");\n+\tprintf(\"RtcTxTimeStampEnabled=%s\\n\", conf->tx_hwtstamp_enabled ? \"True\" : \"False\");\n+\tprintf(\"RtcVid=%d\\n\", conf->vid);\n+\tprintf(\"RtcPcp=%d\\n\", conf->pcp);\n+\tprintf(\"RtcNumFramesPerCycle=%zu\\n\", conf->num_frames_per_cycle);\n+\tprintf(\"RtcPayloadPattern=\");\n+\tprint_payload_pattern(conf->payload_pattern, conf->payload_pattern_length);\n+\tprintf(\"\\n\");\n+\tprintf(\"RtcFrameLength=%zu\\n\", conf->frame_length);\n+\tprintf(\"RtcSecurityMode=%s\\n\", security_mode_to_string(conf->security_mode));\n+\tprintf(\"RtcSecurityAlgorithm=%s\\n\", security_algorithm_to_string(conf->security_algorithm));\n+\tprintf(\"RtcSecurityKey=%s\\n\", conf->security_key);\n+\tprintf(\"RtcSecurityIvPrefix=%s\\n\", conf->security_iv_prefix);\n+\tprintf(\"RtcRxQueue=%d\\n\", conf->rx_queue);\n+\tprintf(\"RtcTxQueue=%d\\n\", conf->tx_queue);\n+\tprintf(\"RtcSocketPriority=%d\\n\", conf->socket_priority);\n+\tprintf(\"RtcTxThreadPriority=%d\\n\", conf->tx_thread_priority);\n+\tprintf(\"RtcRxThreadPriority=%d\\n\", conf->rx_thread_priority);\n+\tprintf(\"RtcTxThreadCpu=%d\\n\", conf->tx_thread_cpu);\n+\tprintf(\"RtcRxThreadCpu=%d\\n\", conf->rx_thread_cpu);\n+\tprintf(\"RtcInterface=%s\\n\", conf->interface);\n+\tprintf(\"RtcDestination=\");\n+\tprint_mac_address(conf->l2_destination);\n+\tprintf(\"\\n\");\n+\tprintf(\"--------------------------------------------------------------------------------\"\n+\t       \"\\n\");\n \n \tconf = &app_config.classes[RTA_FRAME_TYPE];\n-\tif (config_is_traffic_class_active(RTA_FRAME_TYPE)) {\n-\t\tprintf(\"RtaEnabled=%s\\n\", conf->enabled ? \"True\" : \"False\");\n-\t\tprintf(\"RtaRxMirrorEnabled=%s\\n\", conf->rx_mirror_enabled ? \"True\" : \"False\");\n-\t\tprintf(\"RtaXdpEnabled=%s\\n\", conf->xdp_enabled ? \"True\" : \"False\");\n-\t\tprintf(\"RtaXdpSkbMode=%s\\n\", conf->xdp_skb_mode ? \"True\" : \"False\");\n-\t\tprintf(\"RtaXdpZcMode=%s\\n\", conf->xdp_zc_mode ? \"True\" : \"False\");\n-\t\tprintf(\"RtaXdpWakeupMode=%s\\n\", conf->xdp_wakeup_mode ? \"True\" : \"False\");\n-\t\tprintf(\"RtaXdpBusyPollMode=%s\\n\", conf->xdp_busy_poll_mode ? \"True\" : \"False\");\n-\t\tprintf(\"RtaIgnoreRxErrors=%s\\n\", conf->ignore_rx_errors ? \"True\" : \"False\");\n-\t\tprintf(\"RtaTxTimeStampEnabled=%s\\n\", conf->tx_hwtstamp_enabled ? \"True\" : \"False\");\n-\t\tprintf(\"RtaVid=%d\\n\", conf->vid);\n-\t\tprintf(\"RtaPcp=%d\\n\", conf->pcp);\n-\t\tprintf(\"RtaBurstPeriodNS=%\" PRIu64 \"\\n\", conf->burst_period_ns);\n-\t\tprintf(\"RtaNumFramesPerCycle=%zu\\n\", conf->num_frames_per_cycle);\n-\t\tprintf(\"RtaPayloadPattern=\");\n-\t\tprint_payload_pattern(conf->payload_pattern, conf->payload_pattern_length);\n-\t\tprintf(\"RtaFrameLength=%zu\\n\", conf->frame_length);\n-\t\tprintf(\"RtaSecurityMode=%s\\n\", security_mode_to_string(conf->security_mode));\n-\t\tprintf(\"RtaSecurityAlgorithm=%s\\n\",\n-\t\t       security_algorithm_to_string(conf->security_algorithm));\n-\t\tprintf(\"RtaSecurityKey=%s\\n\", conf->security_key);\n-\t\tprintf(\"RtaSecurityIvPrefix=%s\\n\", conf->security_iv_prefix);\n-\t\tprintf(\"RtaRxQueue=%d\\n\", conf->rx_queue);\n-\t\tprintf(\"RtaTxQueue=%d\\n\", conf->tx_queue);\n-\t\tprintf(\"RtaSocketPriority=%d\\n\", conf->socket_priority);\n-\t\tprintf(\"RtaTxThreadPriority=%d\\n\", conf->tx_thread_priority);\n-\t\tprintf(\"RtaRxThreadPriority=%d\\n\", conf->rx_thread_priority);\n-\t\tprintf(\"RtaTxThreadCpu=%d\\n\", conf->tx_thread_cpu);\n-\t\tprintf(\"RtaRxThreadCpu=%d\\n\", conf->rx_thread_cpu);\n-\t\tprintf(\"RtaInterface=%s\\n\", conf->interface);\n-\t\tprintf(\"RtaDestination=\");\n-\t\tprint_mac_address(conf->l2_destination);\n-\t\tprintf(\"---------------------------------------------------------------------------\"\n-\t\t       \"-----\\n\");\n-\t}\n+\tprintf(\"RtaEnabled=%s\\n\", conf->enabled ? \"True\" : \"False\");\n+\tprintf(\"RtaRxMirrorEnabled=%s\\n\", conf->rx_mirror_enabled ? \"True\" : \"False\");\n+\tprintf(\"RtaXdpEnabled=%s\\n\", conf->xdp_enabled ? \"True\" : \"False\");\n+\tprintf(\"RtaXdpSkbMode=%s\\n\", conf->xdp_skb_mode ? \"True\" : \"False\");\n+\tprintf(\"RtaXdpZcMode=%s\\n\", conf->xdp_zc_mode ? \"True\" : \"False\");\n+\tprintf(\"RtaXdpWakeupMode=%s\\n\", conf->xdp_wakeup_mode ? \"True\" : \"False\");\n+\tprintf(\"RtaXdpBusyPollMode=%s\\n\", conf->xdp_busy_poll_mode ? \"True\" : \"False\");\n+\tprintf(\"RtaIgnoreRxErrors=%s\\n\", conf->ignore_rx_errors ? \"True\" : \"False\");\n+\tprintf(\"RtaTxTimeStampEnabled=%s\\n\", conf->tx_hwtstamp_enabled ? \"True\" : \"False\");\n+\tprintf(\"RtaVid=%d\\n\", conf->vid);\n+\tprintf(\"RtaPcp=%d\\n\", conf->pcp);\n+\tprintf(\"RtaBurstPeriodNS=%\" PRIu64 \"\\n\", conf->burst_period_ns);\n+\tprintf(\"RtaNumFramesPerCycle=%zu\\n\", conf->num_frames_per_cycle);\n+\tprintf(\"RtaPayloadPattern=\");\n+\tprint_payload_pattern(conf->payload_pattern, conf->payload_pattern_length);\n+\tprintf(\"\\n\");\n+\tprintf(\"RtaFrameLength=%zu\\n\", conf->frame_length);\n+\tprintf(\"RtaSecurityMode=%s\\n\", security_mode_to_string(conf->security_mode));\n+\tprintf(\"RtaSecurityAlgorithm=%s\\n\", security_algorithm_to_string(conf->security_algorithm));\n+\tprintf(\"RtaSecurityKey=%s\\n\", conf->security_key);\n+\tprintf(\"RtaSecurityIvPrefix=%s\\n\", conf->security_iv_prefix);\n+\tprintf(\"RtaRxQueue=%d\\n\", conf->rx_queue);\n+\tprintf(\"RtaTxQueue=%d\\n\", conf->tx_queue);\n+\tprintf(\"RtaSocketPriority=%d\\n\", conf->socket_priority);\n+\tprintf(\"RtaTxThreadPriority=%d\\n\", conf->tx_thread_priority);\n+\tprintf(\"RtaRxThreadPriority=%d\\n\", conf->rx_thread_priority);\n+\tprintf(\"RtaTxThreadCpu=%d\\n\", conf->tx_thread_cpu);\n+\tprintf(\"RtaRxThreadCpu=%d\\n\", conf->rx_thread_cpu);\n+\tprintf(\"RtaInterface=%s\\n\", conf->interface);\n+\tprintf(\"RtaDestination=\");\n+\tprint_mac_address(conf->l2_destination);\n+\tprintf(\"\\n\");\n+\tprintf(\"--------------------------------------------------------------------------------\"\n+\t       \"\\n\");\n \n \tconf = &app_config.classes[DCP_FRAME_TYPE];\n-\tif (config_is_traffic_class_active(DCP_FRAME_TYPE)) {\n-\t\tprintf(\"DcpEnabled=%s\\n\", conf->enabled ? \"True\" : \"False\");\n-\t\tprintf(\"DcpRxMirrorEnabled=%s\\n\", conf->rx_mirror_enabled ? \"True\" : \"False\");\n-\t\tprintf(\"DcpIgnoreRxErrors=%s\\n\", conf->ignore_rx_errors ? \"True\" : \"False\");\n-\t\tprintf(\"DcpVid=%d\\n\", conf->vid);\n-\t\tprintf(\"DcpPcp=%d\\n\", conf->pcp);\n-\t\tprintf(\"DcpBurstPeriodNS=%\" PRIu64 \"\\n\", conf->burst_period_ns);\n-\t\tprintf(\"DcpNumFramesPerCycle=%zu\\n\", conf->num_frames_per_cycle);\n-\t\tprintf(\"DcpPayloadPattern=\");\n-\t\tprint_payload_pattern(conf->payload_pattern, conf->payload_pattern_length);\n-\t\tprintf(\"DcpFrameLength=%zu\\n\", conf->frame_length);\n-\t\tprintf(\"DcpRxQueue=%d\\n\", conf->rx_queue);\n-\t\tprintf(\"DcpTxQueue=%d\\n\", conf->tx_queue);\n-\t\tprintf(\"DcpSocketPriority=%d\\n\", conf->socket_priority);\n-\t\tprintf(\"DcpTxThreadPriority=%d\\n\", conf->tx_thread_priority);\n-\t\tprintf(\"DcpRxThreadPriority=%d\\n\", conf->rx_thread_priority);\n-\t\tprintf(\"DcpTxThreadCpu=%d\\n\", conf->tx_thread_cpu);\n-\t\tprintf(\"DcpRxThreadCpu=%d\\n\", conf->rx_thread_cpu);\n-\t\tprintf(\"DcpInterface=%s\\n\", conf->interface);\n-\t\tprintf(\"DcpDestination=\");\n-\t\tprint_mac_address(conf->l2_destination);\n-\t\tprintf(\"---------------------------------------------------------------------------\"\n-\t\t       \"-----\\n\");\n-\t}\n+\tprintf(\"DcpEnabled=%s\\n\", conf->enabled ? \"True\" : \"False\");\n+\tprintf(\"DcpRxMirrorEnabled=%s\\n\", conf->rx_mirror_enabled ? \"True\" : \"False\");\n+\tprintf(\"DcpIgnoreRxErrors=%s\\n\", conf->ignore_rx_errors ? \"True\" : \"False\");\n+\tprintf(\"DcpVid=%d\\n\", conf->vid);\n+\tprintf(\"DcpPcp=%d\\n\", conf->pcp);\n+\tprintf(\"DcpBurstPeriodNS=%\" PRIu64 \"\\n\", conf->burst_period_ns);\n+\tprintf(\"DcpNumFramesPerCycle=%zu\\n\", conf->num_frames_per_cycle);\n+\tprintf(\"DcpPayloadPattern=\");\n+\tprint_payload_pattern(conf->payload_pattern, conf->payload_pattern_length);\n+\tprintf(\"\\n\");\n+\tprintf(\"DcpFrameLength=%zu\\n\", conf->frame_length);\n+\tprintf(\"DcpRxQueue=%d\\n\", conf->rx_queue);\n+\tprintf(\"DcpTxQueue=%d\\n\", conf->tx_queue);\n+\tprintf(\"DcpSocketPriority=%d\\n\", conf->socket_priority);\n+\tprintf(\"DcpTxThreadPriority=%d\\n\", conf->tx_thread_priority);\n+\tprintf(\"DcpRxThreadPriority=%d\\n\", conf->rx_thread_priority);\n+\tprintf(\"DcpTxThreadCpu=%d\\n\", conf->tx_thread_cpu);\n+\tprintf(\"DcpRxThreadCpu=%d\\n\", conf->rx_thread_cpu);\n+\tprintf(\"DcpInterface=%s\\n\", conf->interface);\n+\tprintf(\"DcpDestination=\");\n+\tprint_mac_address(conf->l2_destination);\n+\tprintf(\"\\n\");\n+\tprintf(\"--------------------------------------------------------------------------------\"\n+\t       \"\\n\");\n \n \tconf = &app_config.classes[LLDP_FRAME_TYPE];\n-\tif (config_is_traffic_class_active(LLDP_FRAME_TYPE)) {\n-\t\tprintf(\"LldpEnabled=%s\\n\", conf->enabled ? \"True\" : \"False\");\n-\t\tprintf(\"LldpRxMirrorEnabled=%s\\n\", conf->rx_mirror_enabled ? \"True\" : \"False\");\n-\t\tprintf(\"LldpIgnoreRxErrors=%s\\n\", conf->ignore_rx_errors ? \"True\" : \"False\");\n-\t\tprintf(\"LldpBurstPeriodNS=%\" PRIu64 \"\\n\", conf->burst_period_ns);\n-\t\tprintf(\"LldpNumFramesPerCycle=%zu\\n\", conf->num_frames_per_cycle);\n-\t\tprintf(\"LldpPayloadPattern=\");\n-\t\tprint_payload_pattern(conf->payload_pattern, conf->payload_pattern_length);\n-\t\tprintf(\"LldpFrameLength=%zu\\n\", conf->frame_length);\n-\t\tprintf(\"LldpRxQueue=%d\\n\", conf->rx_queue);\n-\t\tprintf(\"LldpTxQueue=%d\\n\", conf->tx_queue);\n-\t\tprintf(\"LldpSocketPriority=%d\\n\", conf->socket_priority);\n-\t\tprintf(\"LldpTxThreadPriority=%d\\n\", conf->tx_thread_priority);\n-\t\tprintf(\"LldpRxThreadPriority=%d\\n\", conf->rx_thread_priority);\n-\t\tprintf(\"LldpTxThreadCpu=%d\\n\", conf->tx_thread_cpu);\n-\t\tprintf(\"LldpRxThreadCpu=%d\\n\", conf->rx_thread_cpu);\n-\t\tprintf(\"LldpInterface=%s\\n\", conf->interface);\n-\t\tprintf(\"LldpDestination=\");\n-\t\tprint_mac_address(conf->l2_destination);\n-\t\tprintf(\"---------------------------------------------------------------------------\"\n-\t\t       \"-----\\n\");\n-\t}\n+\tprintf(\"LldpEnabled=%s\\n\", conf->enabled ? \"True\" : \"False\");\n+\tprintf(\"LldpRxMirrorEnabled=%s\\n\", conf->rx_mirror_enabled ? \"True\" : \"False\");\n+\tprintf(\"LldpIgnoreRxErrors=%s\\n\", conf->ignore_rx_errors ? \"True\" : \"False\");\n+\tprintf(\"LldpBurstPeriodNS=%\" PRIu64 \"\\n\", conf->burst_period_ns);\n+\tprintf(\"LldpNumFramesPerCycle=%zu\\n\", conf->num_frames_per_cycle);\n+\tprintf(\"LldpPayloadPattern=\");\n+\tprint_payload_pattern(conf->payload_pattern, conf->payload_pattern_length);\n+\tprintf(\"\\n\");\n+\tprintf(\"LldpFrameLength=%zu\\n\", conf->frame_length);\n+\tprintf(\"LldpRxQueue=%d\\n\", conf->rx_queue);\n+\tprintf(\"LldpTxQueue=%d\\n\", conf->tx_queue);\n+\tprintf(\"LldpSocketPriority=%d\\n\", conf->socket_priority);\n+\tprintf(\"LldpTxThreadPriority=%d\\n\", conf->tx_thread_priority);\n+\tprintf(\"LldpRxThreadPriority=%d\\n\", conf->rx_thread_priority);\n+\tprintf(\"LldpTxThreadCpu=%d\\n\", conf->tx_thread_cpu);\n+\tprintf(\"LldpRxThreadCpu=%d\\n\", conf->rx_thread_cpu);\n+\tprintf(\"LldpInterface=%s\\n\", conf->interface);\n+\tprintf(\"LldpDestination=\");\n+\tprint_mac_address(conf->l2_destination);\n+\tprintf(\"\\n\");\n+\tprintf(\"--------------------------------------------------------------------------------\"\n+\t       \"\\n\");\n \n \tconf = &app_config.classes[UDP_HIGH_FRAME_TYPE];\n-\tif (config_is_traffic_class_active(UDP_HIGH_FRAME_TYPE)) {\n-\t\tprintf(\"UdpHighEnabled=%s\\n\", conf->enabled ? \"True\" : \"False\");\n-\t\tprintf(\"UdpHighRxMirrorEnabled=%s\\n\", conf->rx_mirror_enabled ? \"True\" : \"False\");\n-\t\tprintf(\"UdpHighIgnoreRxErrors=%s\\n\", conf->ignore_rx_errors ? \"True\" : \"False\");\n-\t\tprintf(\"UdpHighBurstPeriodNS=%\" PRIu64 \"\\n\", conf->burst_period_ns);\n-\t\tprintf(\"UdpHighNumFramesPerCycle=%zu\\n\", conf->num_frames_per_cycle);\n-\t\tprintf(\"UdpHighPayloadPattern=\");\n-\t\tprint_payload_pattern(conf->payload_pattern, conf->payload_pattern_length);\n-\t\tprintf(\"UdpHighFrameLength=%zu\\n\", conf->frame_length);\n-\t\tprintf(\"UdpHighRxQueue=%d\\n\", conf->rx_queue);\n-\t\tprintf(\"UdpHighTxQueue=%d\\n\", conf->tx_queue);\n-\t\tprintf(\"UdpHighSocketPriority=%d\\n\", conf->socket_priority);\n-\t\tprintf(\"UdpHighTxThreadPriority=%d\\n\", conf->tx_thread_priority);\n-\t\tprintf(\"UdpHighRxThreadPriority=%d\\n\", conf->rx_thread_priority);\n-\t\tprintf(\"UdpHighTxThreadCpu=%d\\n\", conf->tx_thread_cpu);\n-\t\tprintf(\"UdpHighRxThreadCpu=%d\\n\", conf->rx_thread_cpu);\n-\t\tprintf(\"UdpHighInterface=%s\\n\", conf->interface);\n-\t\tprintf(\"UdpHighPort=%s\\n\", conf->l3_port);\n-\t\tprintf(\"UdpHighDestination=%s\\n\", conf->l3_destination);\n-\t\tprintf(\"UdpHighSource=%s\\n\", conf->l3_source);\n-\t\tprintf(\"---------------------------------------------------------------------------\"\n-\t\t       \"-----\\n\");\n-\t}\n+\tprintf(\"UdpHighEnabled=%s\\n\", conf->enabled ? \"True\" : \"False\");\n+\tprintf(\"UdpHighRxMirrorEnabled=%s\\n\", conf->rx_mirror_enabled ? \"True\" : \"False\");\n+\tprintf(\"UdpHighIgnoreRxErrors=%s\\n\", conf->ignore_rx_errors ? \"True\" : \"False\");\n+\tprintf(\"UdpHighBurstPeriodNS=%\" PRIu64 \"\\n\", conf->burst_period_ns);\n+\tprintf(\"UdpHighNumFramesPerCycle=%zu\\n\", conf->num_frames_per_cycle);\n+\tprintf(\"UdpHighPayloadPattern=\");\n+\tprint_payload_pattern(conf->payload_pattern, conf->payload_pattern_length);\n+\tprintf(\"\\n\");\n+\tprintf(\"UdpHighFrameLength=%zu\\n\", conf->frame_length);\n+\tprintf(\"UdpHighRxQueue=%d\\n\", conf->rx_queue);\n+\tprintf(\"UdpHighTxQueue=%d\\n\", conf->tx_queue);\n+\tprintf(\"UdpHighSocketPriority=%d\\n\", conf->socket_priority);\n+\tprintf(\"UdpHighTxThreadPriority=%d\\n\", conf->tx_thread_priority);\n+\tprintf(\"UdpHighRxThreadPriority=%d\\n\", conf->rx_thread_priority);\n+\tprintf(\"UdpHighTxThreadCpu=%d\\n\", conf->tx_thread_cpu);\n+\tprintf(\"UdpHighRxThreadCpu=%d\\n\", conf->rx_thread_cpu);\n+\tprintf(\"UdpHighInterface=%s\\n\", conf->interface);\n+\tprintf(\"UdpHighPort=%s\\n\", conf->l3_port);\n+\tprintf(\"UdpHighDestination=%s\\n\", conf->l3_destination);\n+\tprintf(\"UdpHighSource=%s\\n\", conf->l3_source);\n+\tprintf(\"--------------------------------------------------------------------------------\"\n+\t       \"\\n\");\n \n \tconf = &app_config.classes[UDP_LOW_FRAME_TYPE];\n-\tif (config_is_traffic_class_active(UDP_LOW_FRAME_TYPE)) {\n-\t\tprintf(\"UdpLowEnabled=%s\\n\", conf->enabled ? \"True\" : \"False\");\n-\t\tprintf(\"UdpLowRxMirrorEnabled=%s\\n\", conf->rx_mirror_enabled ? \"True\" : \"False\");\n-\t\tprintf(\"UdpLowIgnoreRxErrors=%s\\n\", conf->ignore_rx_errors ? \"True\" : \"False\");\n-\t\tprintf(\"UdpLowBurstPeriodNS=%\" PRIu64 \"\\n\", conf->burst_period_ns);\n-\t\tprintf(\"UdpLowNumFramesPerCycle=%zu\\n\", conf->num_frames_per_cycle);\n-\t\tprintf(\"UdpLowPayloadPattern=\");\n-\t\tprint_payload_pattern(conf->payload_pattern, conf->payload_pattern_length);\n-\t\tprintf(\"UdpLowFrameLength=%zu\\n\", conf->frame_length);\n-\t\tprintf(\"UdpLowRxQueue=%d\\n\", conf->rx_queue);\n-\t\tprintf(\"UdpLowTxQueue=%d\\n\", conf->tx_queue);\n-\t\tprintf(\"UdpLowSocketPriority=%d\\n\", conf->socket_priority);\n-\t\tprintf(\"UdpLowTxThreadPriority=%d\\n\", conf->tx_thread_priority);\n-\t\tprintf(\"UdpLowRxThreadPriority=%d\\n\", conf->rx_thread_priority);\n-\t\tprintf(\"UdpLowTxThreadCpu=%d\\n\", conf->tx_thread_cpu);\n-\t\tprintf(\"UdpLowRxThreadCpu=%d\\n\", conf->rx_thread_cpu);\n-\t\tprintf(\"UdpLowInterface=%s\\n\", conf->interface);\n-\t\tprintf(\"UdpLowPort=%s\\n\", conf->l3_port);\n-\t\tprintf(\"UdpLowDestination=%s\\n\", conf->l3_destination);\n-\t\tprintf(\"UdpLowSource=%s\\n\", conf->l3_source);\n-\t\tprintf(\"---------------------------------------------------------------------------\"\n-\t\t       \"-----\\n\");\n-\t}\n+\tprintf(\"UdpLowEnabled=%s\\n\", conf->enabled ? \"True\" : \"False\");\n+\tprintf(\"UdpLowRxMirrorEnabled=%s\\n\", conf->rx_mirror_enabled ? \"True\" : \"False\");\n+\tprintf(\"UdpLowIgnoreRxErrors=%s\\n\", conf->ignore_rx_errors ? \"True\" : \"False\");\n+\tprintf(\"UdpLowBurstPeriodNS=%\" PRIu64 \"\\n\", conf->burst_period_ns);\n+\tprintf(\"UdpLowNumFramesPerCycle=%zu\\n\", conf->num_frames_per_cycle);\n+\tprintf(\"UdpLowPayloadPattern=\");\n+\tprint_payload_pattern(conf->payload_pattern, conf->payload_pattern_length);\n+\tprintf(\"\\n\");\n+\tprintf(\"UdpLowFrameLength=%zu\\n\", conf->frame_length);\n+\tprintf(\"UdpLowRxQueue=%d\\n\", conf->rx_queue);\n+\tprintf(\"UdpLowTxQueue=%d\\n\", conf->tx_queue);\n+\tprintf(\"UdpLowSocketPriority=%d\\n\", conf->socket_priority);\n+\tprintf(\"UdpLowTxThreadPriority=%d\\n\", conf->tx_thread_priority);\n+\tprintf(\"UdpLowRxThreadPriority=%d\\n\", conf->rx_thread_priority);\n+\tprintf(\"UdpLowTxThreadCpu=%d\\n\", conf->tx_thread_cpu);\n+\tprintf(\"UdpLowRxThreadCpu=%d\\n\", conf->rx_thread_cpu);\n+\tprintf(\"UdpLowInterface=%s\\n\", conf->interface);\n+\tprintf(\"UdpLowPort=%s\\n\", conf->l3_port);\n+\tprintf(\"UdpLowDestination=%s\\n\", conf->l3_destination);\n+\tprintf(\"UdpLowSource=%s\\n\", conf->l3_source);\n+\tprintf(\"--------------------------------------------------------------------------------\"\n+\t       \"\\n\");\n \n \tconf = &app_config.classes[GENERICL2_FRAME_TYPE];\n-\tif (config_is_traffic_class_active(GENERICL2_FRAME_TYPE)) {\n-\t\tprintf(\"GenericL2Name=%s\\n\", conf->name);\n-\t\tprintf(\"GenericL2Enabled=%s\\n\", conf->enabled ? \"True\" : \"False\");\n-\t\tprintf(\"GenericL2RxMirrorEnabled=%s\\n\", conf->rx_mirror_enabled ? \"True\" : \"False\");\n-\t\tprintf(\"GenericL2XdpEnabled=%s\\n\", conf->xdp_enabled ? \"True\" : \"False\");\n-\t\tprintf(\"GenericL2XdpSkbMode=%s\\n\", conf->xdp_skb_mode ? \"True\" : \"False\");\n-\t\tprintf(\"GenericL2XdpZcMode=%s\\n\", conf->xdp_zc_mode ? \"True\" : \"False\");\n-\t\tprintf(\"GenericL2XdpWakeupMode=%s\\n\", conf->xdp_wakeup_mode ? \"True\" : \"False\");\n-\t\tprintf(\"GenericL2XdpBusyPollMode=%s\\n\",\n-\t\t       conf->xdp_busy_poll_mode ? \"True\" : \"False\");\n-\t\tprintf(\"GenericL2TxTimeEnabled=%s\\n\", conf->tx_time_enabled ? \"True\" : \"False\");\n-\t\tprintf(\"GenericL2IgnoreRxErrors=%s\\n\", conf->ignore_rx_errors ? \"True\" : \"False\");\n-\t\tprintf(\"GenericL2TxTimeOffsetNS=%\" PRIu64 \"\\n\", conf->tx_time_offset_ns);\n-\t\tprintf(\"GenericL2TxTimeStampEnabled=%s\\n\",\n-\t\t       conf->tx_hwtstamp_enabled ? \"True\" : \"False\");\n-\t\tprintf(\"GenericL2Vid=%d\\n\", conf->vid);\n-\t\tprintf(\"GenericL2Pcp=%d\\n\", conf->pcp);\n-\t\tprintf(\"GenericL2EtherType=0x%04x\\n\", conf->ether_type);\n-\t\tprintf(\"GenericL2NumFramesPerCycle=%zu\\n\", conf->num_frames_per_cycle);\n-\t\tprintf(\"GenericL2PayloadPattern=\");\n-\t\tprint_payload_pattern(conf->payload_pattern, conf->payload_pattern_length);\n-\t\tprintf(\"GenericL2FrameLength=%zu\\n\", conf->frame_length);\n-\t\tprintf(\"GenericL2RxQueue=%d\\n\", conf->rx_queue);\n-\t\tprintf(\"GenericL2TxQueue=%d\\n\", conf->tx_queue);\n-\t\tprintf(\"GenericL2SocketPriority=%d\\n\", conf->socket_priority);\n-\t\tprintf(\"GenericL2TxThreadPriority=%d\\n\", conf->tx_thread_priority);\n-\t\tprintf(\"GenericL2RxThreadPriority=%d\\n\", conf->rx_thread_priority);\n-\t\tprintf(\"GenericL2TxThreadCpu=%d\\n\", conf->tx_thread_cpu);\n-\t\tprintf(\"GenericL2RxThreadCpu=%d\\n\", conf->rx_thread_cpu);\n-\t\tprintf(\"GenericL2Interface=%s\\n\", conf->interface);\n-\t\tprintf(\"GenericL2Destination=\");\n-\t\tprint_mac_address(conf->l2_destination);\n-\t\tprintf(\"GenericL2RxWorkloadEnabled=%s\\n\",\n-\t\t       conf->rx_workload_enabled ? \"True\" : \"False\");\n-\t\tprintf(\"GenericL2RxWorkloadPrewarm=%s\\n\",\n-\t\t       conf->rx_workload_prewarm ? \"True\" : \"False\");\n-\t\tprintf(\"GenericL2RxWorkloadSkipCount=%\" PRIu64 \"\\n\", conf->rx_workload_skip_count);\n-\t\tprintf(\"GenericL2RxWorkloadFile=%s\\n\", conf->workload_file);\n-\t\tprintf(\"GenericL2RxWorkloadSetupFunction=%s\\n\", conf->workload_setup_function);\n-\t\tprintf(\"GenericL2RxWorkloadSetupArguments=%s\\n\", conf->workload_setup_arguments);\n-\t\tprintf(\"GenericL2RxWorkloadTeardownFunction=%s\\n\",\n-\t\t       conf->workload_teardown_function);\n-\t\tprintf(\"GenericL2RxWorkloadFunction=%s\\n\", conf->workload_function);\n-\t\tprintf(\"GenericL2RxWorkloadArguments=%s\\n\", conf->workload_arguments);\n-\t\tprintf(\"GenericL2RxWorkloadThreadCpu=\");\n-\t\tprint_cpu_list(conf->workload_thread_cpus, conf->workload_thread_cpus_num);\n-\t\tprintf(\"GenericL2RxWorkloadThreadPriority=%d\\n\", conf->workload_thread_priority);\n-\t\tprintf(\"---------------------------------------------------------------------------\"\n-\t\t       \"-----\\n\");\n-\t}\n+\tprintf(\"GenericL2Name=%s\\n\", conf->name);\n+\tprintf(\"GenericL2Enabled=%s\\n\", conf->enabled ? \"True\" : \"False\");\n+\tprintf(\"GenericL2RxMirrorEnabled=%s\\n\", conf->rx_mirror_enabled ? \"True\" : \"False\");\n+\tprintf(\"GenericL2XdpEnabled=%s\\n\", conf->xdp_enabled ? \"True\" : \"False\");\n+\tprintf(\"GenericL2XdpSkbMode=%s\\n\", conf->xdp_skb_mode ? \"True\" : \"False\");\n+\tprintf(\"GenericL2XdpZcMode=%s\\n\", conf->xdp_zc_mode ? \"True\" : \"False\");\n+\tprintf(\"GenericL2XdpWakeupMode=%s\\n\", conf->xdp_wakeup_mode ? \"True\" : \"False\");\n+\tprintf(\"GenericL2XdpBusyPollMode=%s\\n\", conf->xdp_busy_poll_mode ? \"True\" : \"False\");\n+\tprintf(\"GenericL2TxTimeEnabled=%s\\n\", conf->tx_time_enabled ? \"True\" : \"False\");\n+\tprintf(\"GenericL2IgnoreRxErrors=%s\\n\", conf->ignore_rx_errors ? \"True\" : \"False\");\n+\tprintf(\"GenericL2TxTimeOffsetNS=%\" PRIu64 \"\\n\", conf->tx_time_offset_ns);\n+\tprintf(\"GenericL2TxTimeStampEnabled=%s\\n\", conf->tx_hwtstamp_enabled ? \"True\" : \"False\");\n+\tprintf(\"GenericL2Vid=%d\\n\", conf->vid);\n+\tprintf(\"GenericL2Pcp=%d\\n\", conf->pcp);\n+\tprintf(\"GenericL2EtherType=0x%04x\\n\", conf->ether_type);\n+\tprintf(\"GenericL2NumFramesPerCycle=%zu\\n\", conf->num_frames_per_cycle);\n+\tprintf(\"GenericL2PayloadPattern=\");\n+\tprint_payload_pattern(conf->payload_pattern, conf->payload_pattern_length);\n+\tprintf(\"\\n\");\n+\tprintf(\"GenericL2FrameLength=%zu\\n\", conf->frame_length);\n+\tprintf(\"GenericL2RxQueue=%d\\n\", conf->rx_queue);\n+\tprintf(\"GenericL2TxQueue=%d\\n\", conf->tx_queue);\n+\tprintf(\"GenericL2SocketPriority=%d\\n\", conf->socket_priority);\n+\tprintf(\"GenericL2TxThreadPriority=%d\\n\", conf->tx_thread_priority);\n+\tprintf(\"GenericL2RxThreadPriority=%d\\n\", conf->rx_thread_priority);\n+\tprintf(\"GenericL2TxThreadCpu=%d\\n\", conf->tx_thread_cpu);\n+\tprintf(\"GenericL2RxThreadCpu=%d\\n\", conf->rx_thread_cpu);\n+\tprintf(\"GenericL2Interface=%s\\n\", conf->interface);\n+\tprintf(\"GenericL2Destination=\");\n+\tprint_mac_address(conf->l2_destination);\n+\tprintf(\"\\n\");\n+\tprintf(\"--------------------------------------------------------------------------------\"\n+\t       \"\\n\");\n \n \tprintf(\"LogThreadPriority=%d\\n\", app_config.log_thread_priority);\n \tprintf(\"LogThreadCpu=%d\\n\", app_config.log_thread_cpu);\n@@ -1028,45 +864,36 @@ void config_print_values(void)\n \tprintf(\"DebugMonitorMode=%s\\n\", app_config.debug_monitor_mode ? \"True\" : \"False\");\n \tprintf(\"DebugMonitorDestination=\");\n \tprint_mac_address(app_config.debug_monitor_destination);\n+\tprintf(\"\\n\");\n \tprintf(\"--------------------------------------------------------------------------------\"\n \t       \"\\n\");\n \n-\tif (app_config.stats_histogram_enabled) {\n-\t\tprintf(\"StatsHistogramEnabled=%s\\n\",\n-\t\t       app_config.stats_histogram_enabled ? \"True\" : \"False\");\n-\t\tprintf(\"StatsHistogramMinimumNS=%\" PRIu64 \"\\n\",\n-\t\t       app_config.stats_histogram_minimum_ns);\n-\t\tprintf(\"StatsHistogramMaximumNS=%\" PRIu64 \"\\n\",\n-\t\t       app_config.stats_histogram_maximum_ns);\n-\t\tprintf(\"StatsHistogramFile=%s\\n\", app_config.stats_histogram_file);\n-\t\tprintf(\"StatsCollectionIntervalNS=%\" PRIu64 \"\\n\",\n-\t\t       app_config.stats_collection_interval_ns);\n-\t\tprintf(\"---------------------------------------------------------------------------\"\n-\t\t       \"-----\\n\");\n-\t}\n+\tprintf(\"StatsHistogramEnabled=%s\\n\", app_config.stats_histogram_enabled ? \"True\" : \"False\");\n+\tprintf(\"StatsHistogramMinimumNS=%\" PRIu64 \"\\n\", app_config.stats_histogram_minimum_ns);\n+\tprintf(\"StatsHistogramMaximumNS=%\" PRIu64 \"\\n\", app_config.stats_histogram_maximum_ns);\n+\tprintf(\"StatsHistogramFile=%s\\n\", app_config.stats_histogram_file);\n+\tprintf(\"StatsCollectionIntervalNS=%\" PRIu64 \"\\n\", app_config.stats_collection_interval_ns);\n+\tprintf(\"--------------------------------------------------------------------------------\"\n+\t       \"\\n\");\n \n-\tif (app_config.log_mqtt) {\n-\t\tprintf(\"LogMqtt=%s\\n\", app_config.log_mqtt ? \"True\" : \"False\");\n-\t\tprintf(\"LogMqttThreadPriority=%d\\n\", app_config.log_mqtt_thread_priority);\n-\t\tprintf(\"LogMqttThreadCpu=%d\\n\", app_config.log_mqtt_thread_cpu);\n-\t\tprintf(\"LogMqttBrokerIP=%s\\n\", app_config.log_mqtt_broker_ip);\n-\t\tprintf(\"LogMqttBrokerPort=%d\\n\", app_config.log_mqtt_broker_port);\n-\t\tprintf(\"LogMqttKeepAliveSecs=%d\\n\", app_config.log_mqtt_keep_alive_secs);\n-\t\tprintf(\"LogMqttMeasurementName=%s\\n\", app_config.log_mqtt_measurement_name);\n-\t\tprintf(\"---------------------------------------------------------------------------\"\n-\t\t       \"-----\\n\");\n-\t}\n+\tprintf(\"LogMqtt=%s\\n\", app_config.log_mqtt ? \"True\" : \"False\");\n+\tprintf(\"LogMqttThreadPriority=%d\\n\", app_config.log_mqtt_thread_priority);\n+\tprintf(\"LogMqttThreadCpu=%d\\n\", app_config.log_mqtt_thread_cpu);\n+\tprintf(\"LogMqttBrokerIP=%s\\n\", app_config.log_mqtt_broker_ip);\n+\tprintf(\"LogMqttBrokerPort=%d\\n\", app_config.log_mqtt_broker_port);\n+\tprintf(\"LogMqttKeepAliveSecs=%d\\n\", app_config.log_mqtt_keep_alive_secs);\n+\tprintf(\"LogMqttMeasurementName=%s\\n\", app_config.log_mqtt_measurement_name);\n+\tprintf(\"--------------------------------------------------------------------------------\"\n+\t       \"\\n\");\n \n-\tif (app_config.log_json) {\n-\t\tprintf(\"LogJson=%s\\n\", app_config.log_json ? \"True\" : \"False\");\n-\t\tprintf(\"LogJsonThreadPriority=%d\\n\", app_config.log_json_thread_priority);\n-\t\tprintf(\"LogJsonThreadCpu=%d\\n\", app_config.log_json_thread_cpu);\n-\t\tprintf(\"LogJsonHost=%s\\n\", app_config.log_json_host);\n-\t\tprintf(\"LogJsonPort=%s\\n\", app_config.log_json_port);\n-\t\tprintf(\"LogJsonMeasurementName=%s\\n\", app_config.log_json_measurement_name);\n-\t\tprintf(\"---------------------------------------------------------------------------\"\n-\t\t       \"-----\\n\");\n-\t}\n+\tprintf(\"LogJson=%s\\n\", app_config.log_json ? \"True\" : \"False\");\n+\tprintf(\"LogJsonThreadPriority=%d\\n\", app_config.log_json_thread_priority);\n+\tprintf(\"LogJsonThreadCpu=%d\\n\", app_config.log_json_thread_cpu);\n+\tprintf(\"LogJsonHost=%s\\n\", app_config.log_json_host);\n+\tprintf(\"LogJsonPort=%s\\n\", app_config.log_json_port);\n+\tprintf(\"LogJsonMeasurementName=%s\\n\", app_config.log_json_measurement_name);\n+\tprintf(\"--------------------------------------------------------------------------------\"\n+\t       \"\\n\");\n }\n \n int config_set_defaults(bool mirror_enabled)\n@@ -1141,8 +968,7 @@ int config_set_defaults(bool mirror_enabled)\n \tconf->workload_arguments = NULL;\n \tconf->workload_setup_function = NULL;\n \tconf->workload_setup_arguments = NULL;\n-\tconf->workload_teardown_function = NULL;\n-\tmemset(conf->workload_thread_cpus, '\\0', sizeof(conf->workload_thread_cpus));\n+\tconf->workload_thread_cpu = 0;\n \tconf->workload_thread_priority = 98;\n \tstrncpy(conf->interface, \"enp3s0\", sizeof(conf->interface) - 1);\n \tmemcpy((void *)conf->l2_destination, default_destination, ETH_ALEN);\n@@ -1212,17 +1038,6 @@ int config_set_defaults(bool mirror_enabled)\n \tconf->rx_thread_priority = 98;\n \tconf->tx_thread_cpu = 0;\n \tconf->rx_thread_cpu = 0;\n-\tconf->rx_workload_enabled = false;\n-\tconf->rx_workload_prewarm = false;\n-\tconf->rx_workload_skip_count = 0;\n-\tconf->workload_file = NULL;\n-\tconf->workload_function = NULL;\n-\tconf->workload_arguments = NULL;\n-\tconf->workload_setup_function = NULL;\n-\tconf->workload_setup_arguments = NULL;\n-\tconf->workload_teardown_function = NULL;\n-\tmemset(conf->workload_thread_cpus, '\\0', sizeof(conf->workload_thread_cpus));\n-\tconf->workload_thread_priority = 98;\n \tstrncpy(conf->interface, \"enp3s0\", sizeof(conf->interface) - 1);\n \tmemcpy((void *)conf->l2_destination, default_destination, ETH_ALEN);\n \n@@ -1274,10 +1089,6 @@ int config_set_defaults(bool mirror_enabled)\n \t\tgoto out;\n \tconf->payload_pattern_length = strlen(conf->payload_pattern);\n \tconf->frame_length = 200;\n-\tconf->security_mode = SECURITY_MODE_NONE;\n-\tconf->security_algorithm = SECURITY_ALGORITHM_AES256_GCM;\n-\tconf->security_key = NULL;\n-\tconf->security_iv_prefix = NULL;\n \tconf->rx_queue = 1;\n \tconf->tx_queue = 1;\n \tconf->socket_priority = 1;\n@@ -1300,10 +1111,6 @@ int config_set_defaults(bool mirror_enabled)\n \t\tgoto out;\n \tconf->payload_pattern_length = strlen(conf->payload_pattern);\n \tconf->frame_length = 200;\n-\tconf->security_mode = SECURITY_MODE_NONE;\n-\tconf->security_algorithm = SECURITY_ALGORITHM_AES256_GCM;\n-\tconf->security_key = NULL;\n-\tconf->security_iv_prefix = NULL;\n \tconf->rx_queue = 1;\n \tconf->tx_queue = 1;\n \tconf->socket_priority = 1;\n@@ -1326,10 +1133,6 @@ int config_set_defaults(bool mirror_enabled)\n \t\tgoto out;\n \tconf->payload_pattern_length = strlen(conf->payload_pattern);\n \tconf->frame_length = 1400;\n-\tconf->security_mode = SECURITY_MODE_NONE;\n-\tconf->security_algorithm = SECURITY_ALGORITHM_AES256_GCM;\n-\tconf->security_key = NULL;\n-\tconf->security_iv_prefix = NULL;\n \tconf->rx_queue = 0;\n \tconf->tx_queue = 0;\n \tconf->socket_priority = 0;\n@@ -1360,10 +1163,6 @@ int config_set_defaults(bool mirror_enabled)\n \t\tgoto out;\n \tconf->payload_pattern_length = strlen(conf->payload_pattern);\n \tconf->frame_length = 1400;\n-\tconf->security_mode = SECURITY_MODE_NONE;\n-\tconf->security_algorithm = SECURITY_ALGORITHM_AES256_GCM;\n-\tconf->security_key = NULL;\n-\tconf->security_iv_prefix = NULL;\n \tconf->rx_queue = 0;\n \tconf->tx_queue = 0;\n \tconf->socket_priority = 0;\n@@ -1407,10 +1206,6 @@ int config_set_defaults(bool mirror_enabled)\n \t\tgoto out;\n \tconf->payload_pattern_length = strlen(conf->payload_pattern);\n \tconf->frame_length = 200;\n-\tconf->security_mode = SECURITY_MODE_NONE;\n-\tconf->security_algorithm = SECURITY_ALGORITHM_AES256_GCM;\n-\tconf->security_key = NULL;\n-\tconf->security_iv_prefix = NULL;\n \tconf->rx_queue = 1;\n \tconf->tx_queue = 1;\n \tconf->socket_priority = 1;\n@@ -1418,17 +1213,6 @@ int config_set_defaults(bool mirror_enabled)\n \tconf->rx_thread_priority = 90;\n \tconf->tx_thread_cpu = 0;\n \tconf->rx_thread_cpu = 0;\n-\tconf->rx_workload_enabled = false;\n-\tconf->rx_workload_prewarm = false;\n-\tconf->rx_workload_skip_count = 0;\n-\tconf->workload_file = NULL;\n-\tconf->workload_function = NULL;\n-\tconf->workload_arguments = NULL;\n-\tconf->workload_setup_function = NULL;\n-\tconf->workload_setup_arguments = NULL;\n-\tconf->workload_teardown_function = NULL;\n-\tmemset(conf->workload_thread_cpus, '\\0', sizeof(conf->workload_thread_cpus));\n-\tconf->workload_thread_priority = 98;\n \tstrncpy(conf->interface, \"enp3s0\", sizeof(conf->interface) - 1);\n \tmemcpy((void *)conf->l2_destination, default_destination, ETH_ALEN);\n \n@@ -1532,15 +1316,12 @@ bool config_sanity_check(void)\n \tsize_t min_frame_size;\n \n \t/* Either GenericL2 or PROFINET should be active. */\n-\tif (config_is_traffic_class_active(GENERICL2_FRAME_TYPE) &&\n-\t    (config_is_traffic_class_active(TSN_HIGH_FRAME_TYPE) ||\n-\t     config_is_traffic_class_active(TSN_LOW_FRAME_TYPE) ||\n-\t     config_is_traffic_class_active(RTC_FRAME_TYPE) ||\n-\t     config_is_traffic_class_active(RTA_FRAME_TYPE) ||\n-\t     config_is_traffic_class_active(DCP_FRAME_TYPE) ||\n-\t     config_is_traffic_class_active(LLDP_FRAME_TYPE) ||\n-\t     config_is_traffic_class_active(UDP_HIGH_FRAME_TYPE) ||\n-\t     config_is_traffic_class_active(UDP_LOW_FRAME_TYPE))) {\n+\tif (config_is_traffic_class_active(\"GenericL2\") &&\n+\t    (config_is_traffic_class_active(\"TsnHigh\") ||\n+\t     config_is_traffic_class_active(\"TsnLow\") || config_is_traffic_class_active(\"Rtc\") ||\n+\t     config_is_traffic_class_active(\"Rta\") || config_is_traffic_class_active(\"Dcp\") ||\n+\t     config_is_traffic_class_active(\"Lldp\") || config_is_traffic_class_active(\"UdpHigh\") ||\n+\t     config_is_traffic_class_active(\"UdpLow\"))) {\n \t\tfprintf(stderr, \"Either use PROFINET or GenericL2!\\n\");\n \t\tfprintf(stderr, \"For simulation of PROFINET and other middlewares in parallel \"\n \t\t\t\t\"start multiple instances of ref&mirror application(s) with \"\n@@ -1665,7 +1446,7 @@ bool config_sanity_check(void)\n \t     (app_config.classes[TSN_LOW_FRAME_TYPE].tx_hwtstamp_enabled &&\n \t      app_config.classes[TSN_LOW_FRAME_TYPE].xdp_enabled))) {\n \t\tfprintf(stderr, \"XDP Tx HW Timestamp requires TX_TIMESTAMP build support!\\n\");\n-\t\tfprintf(stderr, \"Rebuild with -DTX_TIMESTAMP=ON (requires libxdp >= v1.5.2 and \"\n+\t\tfprintf(stderr, \"Rebuild with -DTX_TIMESTAMP=ON (requires libxdp >= v1.4.1 and \"\n \t\t\t\t\"Linux kernel >= v6.8).\\n\");\n \t\treturn false;\n \t}\ndiff --git a/src/config.h b/src/config.h\nindex b04501b..36c8f80 100644\n--- a/src/config.h\n+++ b/src/config.h\n@@ -1,6 +1,6 @@\n /* SPDX-License-Identifier: BSD-2-Clause */\n /*\n- * Copyright (C) 2020-2026 Linutronix GmbH\n+ * Copyright (C) 2020-2025 Linutronix GmbH\n  * Author Kurt Kanzenbach <kurt@linutronix.de>\n  */\n \n@@ -77,14 +77,13 @@ struct traffic_class_config {\n \tint rx_thread_priority;\n \tint tx_thread_cpu;\n \tint rx_thread_cpu;\n-\tint workload_thread_cpus[WORKLOAD_MAX];\n-\tint workload_thread_cpus_num;\n+\tint workload_thread_cpu;\n \tint workload_thread_priority;\n \n \t/* Workload settings */\n \tbool rx_workload_enabled;\n \tbool rx_workload_prewarm;\n-\tuint64_t rx_workload_skip_count;\n+\tint rx_workload_skip_count;\n \tchar *workload_file;\n \tsize_t workload_file_length;\n \tchar *workload_function;\n@@ -95,8 +94,6 @@ struct traffic_class_config {\n \tsize_t workload_setup_function_length;\n \tchar *workload_setup_arguments;\n \tsize_t workload_setup_arguments_length;\n-\tchar *workload_teardown_function;\n-\tsize_t workload_teardown_function_length;\n };\n \n struct application_config {\n@@ -348,14 +345,15 @@ void config_free(void);\n #define CONFIG_STORE_CLOCKID_PARAM(name, var)                                                      \\\n \tdo {                                                                                       \\\n \t\tif (!strcmp(key, #name)) {                                                         \\\n-\t\t\tclockid_t clock;                                                           \\\n-                                                                                                   \\\n-\t\t\tret = config_parse_clockid(value, &clock);                                 \\\n-\t\t\tif (ret) {                                                                 \\\n+\t\t\tif (strcmp(value, \"CLOCK_TAI\") && strcmp(value, \"CLOCK_MONOTONIC\")) {      \\\n \t\t\t\tfprintf(stderr, \"Invalid clockid specified!\\n\");                   \\\n \t\t\t\tgoto err_parse;                                                    \\\n \t\t\t}                                                                          \\\n-\t\t\tapp_config.var = clock;                                                    \\\n+                                                                                                   \\\n+\t\t\tif (!strcmp(value, \"CLOCK_TAI\"))                                           \\\n+\t\t\t\tapp_config.var = CLOCK_TAI;                                        \\\n+\t\t\tif (!strcmp(value, \"CLOCK_MONOTONIC\"))                                     \\\n+\t\t\t\tapp_config.var = CLOCK_MONOTONIC;                                  \\\n \t\t}                                                                                  \\\n \t} while (0)\n \n@@ -381,7 +379,6 @@ void config_free(void);\n \t\t\tif (strcasecmp(value, \"none\") && strcasecmp(value, \"ao\") &&                \\\n \t\t\t    strcasecmp(value, \"ae\")) {                                             \\\n \t\t\t\tfprintf(stderr, \"Invalid security mode specified!\\n\");             \\\n-\t\t\t\tret = -EINVAL;                                                     \\\n \t\t\t\tgoto err_parse;                                                    \\\n \t\t\t}                                                                          \\\n                                                                                                    \\\n@@ -402,7 +399,6 @@ void config_free(void);\n \t\t\tif (strcasecmp(value, \"aes256-gcm\") && strcasecmp(value, \"aes128-gcm\") &&  \\\n \t\t\t    strcasecmp(value, \"chacha20-poly1305\")) {                              \\\n \t\t\t\tfprintf(stderr, \"Invalid security algorithm specified!\\n\");        \\\n-\t\t\t\tret = -EINVAL;                                                     \\\n \t\t\t\tgoto err_parse;                                                    \\\n \t\t\t}                                                                          \\\n \t\t\tif (!strcasecmp(value, \"aes256-gcm\"))                                      \\\n@@ -415,22 +411,7 @@ void config_free(void);\n \t\t}                                                                                  \\\n \t} while (0)\n \n-#define CONFIG_STORE_CPU_LIST_PARAM_CLASS(name, var)                                               \\\n-\tdo {                                                                                       \\\n-\t\tif (!strcmp(key, #name)) {                                                         \\\n-\t\t\tenum stat_frame_type type = config_opt_to_type(#name);                     \\\n-                                                                                                   \\\n-\t\t\tret = config_parse_cpu_list(value, app_config.classes[type].var,           \\\n-\t\t\t\t\t\t    WORKLOAD_MAX,                                  \\\n-\t\t\t\t\t\t    &app_config.classes[type].var##_num);          \\\n-\t\t\tif (ret) {                                                                 \\\n-\t\t\t\tfprintf(stderr, \"The value for \" #name \" is invalid!\\n\");          \\\n-\t\t\t\tgoto err_parse;                                                    \\\n-\t\t\t}                                                                          \\\n-\t\t}                                                                                  \\\n-\t} while (0)\n-\n-bool config_is_traffic_class_active(enum stat_frame_type type);\n+bool config_is_traffic_class_active(const char *traffic_class);\n \n static inline bool config_have_busy_poll(void)\n {\ndiff --git a/src/dcp_thread.c b/src/dcp_thread.c\nindex abbae3f..cb727bb 100644\n--- a/src/dcp_thread.c\n+++ b/src/dcp_thread.c\n@@ -1,6 +1,6 @@\n // SPDX-License-Identifier: BSD-2-Clause\n /*\n- * Copyright (C) 2020-2026 Linutronix GmbH\n+ * Copyright (C) 2020-2025 Linutronix GmbH\n  * Author Kurt Kanzenbach <kurt@linutronix.de>\n  */\n \n@@ -24,7 +24,6 @@\n #include \"log.h\"\n #include \"net.h\"\n #include \"packet.h\"\n-#include \"profinet.h\"\n #include \"security.h\"\n #include \"stat.h\"\n #include \"utils.h\"\n@@ -79,7 +78,7 @@ static void dcp_build_frame_from_rx(struct thread_context *thread_context,\n \teth_new->vlan_encapsulated_proto = htons(ETH_P_PROFINET_RT);\n \n \trt = (struct profinet_rt_header *)(new_frame + sizeof(*eth_new));\n-\tset_mirror_tx_timestamp_est(&rt->meta_data);\n+\tset_mirror_tx_timestamp(&rt->meta_data);\n }\n \n static int dcp_send_messages(struct thread_context *thread_context, int socket_fd,\n@@ -110,10 +109,6 @@ static int dcp_send_frames(struct thread_context *thread_context, unsigned char\n \tconst struct traffic_class_config *dcp_config = thread_context->conf;\n \tint len, i;\n \n-\t/* Adjust meta data */\n-\tset_mirror_tx_timestamp(dcp_config, frame_data, dcp_config->frame_length, num_frames,\n-\t\t\t\tthread_context->meta_data_offset);\n-\n \t/* Send them */\n \tlen = dcp_send_messages(thread_context, socket_fd, destination, frame_data, num_frames);\n \n@@ -140,7 +135,7 @@ static void dcp_gen_and_send_frames(struct thread_context *thread_context, int s\n \tstruct timespec tx_time = {};\n \tint len, i;\n \n-\tapp_clock_get(&tx_time);\n+\tclock_gettime(app_config.application_clock_id, &tx_time);\n \n \t/* Adjust meta data */\n \tfor (i = 0; i < dcp_config->num_frames_per_cycle; i++) {\n@@ -156,8 +151,8 @@ static void dcp_gen_and_send_frames(struct thread_context *thread_context, int s\n \tlen = dcp_send_messages(thread_context, socket_fd, destination,\n \t\t\t\tthread_context->tx_frame_data, dcp_config->num_frames_per_cycle);\n \n-\tif (len > 0)\n-\t\tstat_frames_sent_batch(DCP_FRAME_TYPE, sequence_counter_begin, len);\n+\tfor (i = 0; i < len; i++)\n+\t\tstat_frame_sent(DCP_FRAME_TYPE, sequence_counter_begin + i);\n }\n \n static void *dcp_tx_thread_routine(void *data)\n@@ -424,7 +419,7 @@ int dcp_threads_create(struct thread_context *thread_context)\n \tstruct traffic_class_config *dcp_config;\n \tint ret;\n \n-\tif (!config_is_traffic_class_active(DCP_FRAME_TYPE))\n+\tif (!config_is_traffic_class_active(\"Dcp\"))\n \t\tgoto out;\n \n \tthread_context->conf = dcp_config = &app_config.classes[DCP_FRAME_TYPE];\n@@ -481,35 +476,34 @@ int dcp_threads_create(struct thread_context *thread_context)\n \t\t}\n \t}\n \n-\tthread_context->meta_data_offset = get_meta_data_offset(DCP_FRAME_TYPE, SECURITY_MODE_NONE);\n-\n-\tret = create_rt_thread(&thread_context->tx_task_id, dcp_config->tx_thread_priority,\n-\t\t\t       dcp_config->tx_thread_cpu, dcp_tx_thread_routine, thread_context,\n-\t\t\t       \"DcpTxThread\");\n+\tret = create_rt_thread(&thread_context->tx_task_id, \"DcpTxThread\",\n+\t\t\t       dcp_config->tx_thread_priority, dcp_config->tx_thread_cpu,\n+\t\t\t       dcp_tx_thread_routine, thread_context);\n \tif (ret) {\n \t\tfprintf(stderr, \"Failed to create Dcp Tx Thread!\\n\");\n \t\tgoto err_thread;\n \t}\n \n \tif (!dcp_config->rx_mirror_enabled) {\n-\t\tret = create_rt_thread(&thread_context->tx_gen_task_id,\n+\t\tret = create_rt_thread(&thread_context->tx_gen_task_id, \"DcpTxGenThread\",\n \t\t\t\t       dcp_config->tx_thread_priority, dcp_config->tx_thread_cpu,\n-\t\t\t\t       dcp_tx_generation_thread_routine, thread_context,\n-\t\t\t\t       \"DcpTxGenThread\");\n+\t\t\t\t       dcp_tx_generation_thread_routine, thread_context);\n \t\tif (ret) {\n \t\t\tfprintf(stderr, \"Failed to create Dcp TxGen Thread!\\n\");\n \t\t\tgoto err_thread_txgen;\n \t\t}\n \t}\n \n-\tret = create_rt_thread(&thread_context->rx_task_id, dcp_config->rx_thread_priority,\n-\t\t\t       dcp_config->rx_thread_cpu, dcp_rx_thread_routine, thread_context,\n-\t\t\t       \"DcpRxThread\");\n+\tret = create_rt_thread(&thread_context->rx_task_id, \"DcpRxThread\",\n+\t\t\t       dcp_config->rx_thread_priority, dcp_config->rx_thread_cpu,\n+\t\t\t       dcp_rx_thread_routine, thread_context);\n \tif (ret) {\n \t\tfprintf(stderr, \"Failed to create Dcp Rx Thread!\\n\");\n \t\tgoto err_thread_rx;\n \t}\n \n+\tthread_context->meta_data_offset = get_meta_data_offset(DCP_FRAME_TYPE, SECURITY_MODE_NONE);\n+\n out:\n \treturn 0;\n \ndiff --git a/src/layer2_thread.c b/src/layer2_thread.c\nindex 49650d2..7db09e9 100644\n--- a/src/layer2_thread.c\n+++ b/src/layer2_thread.c\n@@ -1,6 +1,6 @@\n // SPDX-License-Identifier: BSD-2-Clause\n /*\n- * Copyright (C) 2022-2026 Linutronix GmbH\n+ * Copyright (C) 2022-2025 Linutronix GmbH\n  * Author Kurt Kanzenbach <kurt@linutronix.de>\n  */\n \n@@ -33,8 +33,6 @@\n #include \"thread.h\"\n #include \"tx_time.h\"\n #include \"utils.h\"\n-#include \"workload.h\"\n-#include \"xdp.h\"\n \n static void generic_l2_initialize_frame(struct thread_context *thread_context,\n \t\t\t\t\tunsigned char *frame_data, const unsigned char *source,\n@@ -131,12 +129,8 @@ static int generic_l2_send_frames(struct thread_context *thread_context, unsigne\n \tsize_t frame_length;\n \tint len, i;\n \n-\t/* Adjust meta data */\n-\tframe_length = l2_config->frame_length;\n-\tset_mirror_tx_timestamp(l2_config, frame_data, frame_length, num_frames,\n-\t\t\t\tthread_context->meta_data_offset);\n-\n \t/* Send it */\n+\tframe_length = l2_config->frame_length;\n \tlen = generic_l2_send_messages(thread_context, socket_fd, destination, frame_data,\n \t\t\t\t       num_frames, duration);\n \n@@ -162,7 +156,7 @@ static int generic_l2_gen_and_send_frames(struct thread_context *thread_context,\n \tstruct timespec tx_time = {};\n \tint len, i;\n \n-\tapp_clock_get(&tx_time);\n+\tclock_gettime(app_config.application_clock_id, &tx_time);\n \n \t/* Adjust meta data */\n \tfor (i = 0; i < num_frames_per_cycle; i++) {\n@@ -179,16 +173,16 @@ static int generic_l2_gen_and_send_frames(struct thread_context *thread_context,\n \t\t\t\t       thread_context->tx_frame_data, num_frames_per_cycle,\n \t\t\t\t       duration);\n \n-\tif (len > 0)\n-\t\tstat_frames_sent_batch(GENERICL2_FRAME_TYPE, sequence_counter_begin, len);\n+\tfor (i = 0; i < len; i++)\n+\t\tstat_frame_sent(GENERICL2_FRAME_TYPE, sequence_counter_begin + i);\n \n \treturn len;\n }\n \n static void generic_l2_gen_and_send_xdp_frames(struct thread_context *thread_context,\n \t\t\t\t\t       size_t num_frames_per_cycle,\n-\t\t\t\t\t       uint64_t sequence_counter, uint64_t duration,\n-\t\t\t\t\t       uint32_t *frame_number)\n+\t\t\t\t\t       uint64_t sequence_counter, uint64_t wakeup_time,\n+\t\t\t\t\t       uint64_t duration, uint32_t *frame_number)\n {\n \tconst struct traffic_class_config *l2_config = thread_context->conf;\n \tstruct xdp_tx_time tx_time = {\n@@ -287,8 +281,6 @@ static void *generic_l2_tx_thread_routine(void *data)\n \t\t\treturn NULL;\n \t\t}\n \n-\t\tworkload_check_finished(thread_context);\n-\n \t\tif (!mirror_enabled) {\n \t\t\tgeneric_l2_gen_and_send_frames(thread_context,\n \t\t\t\t\t\t       l2_config->num_frames_per_cycle, socket_fd,\n@@ -333,7 +325,6 @@ static void *generic_l2_xdp_tx_thread_routine(void *data)\n \txsk = thread_context->xsk;\n \txsk->tx_hwts.rtt = &round_trip_contexts[GENERICL2_FRAME_TYPE];\n \txsk->tx_hwts.frames_per_cycle = l2_config->num_frames_per_cycle;\n-\txsk->tx_hwts.meta_data_offset = thread_context->meta_data_offset;\n \n \tret = get_interface_mac_address(l2_config->interface, source, ETH_ALEN);\n \tif (ret < 0) {\n@@ -379,15 +370,14 @@ static void *generic_l2_xdp_tx_thread_routine(void *data)\n \t\t\treturn NULL;\n \t\t}\n \n-\t\tworkload_check_finished(thread_context);\n-\n \t\tif (!mirror_enabled) {\n \t\t\tgeneric_l2_gen_and_send_xdp_frames(thread_context, num_frames,\n-\t\t\t\t\t\t\t   sequence_counter, duration,\n-\t\t\t\t\t\t\t   &frame_number);\n+\t\t\t\t\t\t\t   sequence_counter, ts_to_ns(&wakeup_time),\n+\t\t\t\t\t\t\t   duration, &frame_number);\n \t\t\tsequence_counter += num_frames;\n \t\t} else {\n \t\t\tunsigned int received;\n+\t\t\tuint64_t i;\n \n \t\t\tpthread_mutex_lock(&thread_context->xdp_data_mutex);\n \n@@ -403,14 +393,37 @@ static void *generic_l2_xdp_tx_thread_routine(void *data)\n \n \t\t\txsk_ring_prod__submit(&xsk->tx, received);\n \n-\t\t\tif (received > 0)\n-\t\t\t\tstat_frames_sent_batch(GENERICL2_FRAME_TYPE, sequence_counter,\n-\t\t\t\t\t\t       received);\n+\t\t\tif (received > 0) {\n+\t\t\t\tif (l2_config->tx_hwtstamp_enabled) {\n+\t\t\t\t\t/*\n+\t\t\t\t\t * Once-per-cycle: record TX SW timestamp for the first\n+\t\t\t\t\t * packet in this cycle and count all frames\n+\t\t\t\t\t */\n+\t\t\t\t\tstat_frames_sent_batch(GENERICL2_FRAME_TYPE,\n+\t\t\t\t\t\t\t       sequence_counter, received);\n+\t\t\t\t} else {\n+\t\t\t\t\tfor (i = sequence_counter; i < sequence_counter + received;\n+\t\t\t\t\t     ++i)\n+\t\t\t\t\t\tstat_frame_sent(GENERICL2_FRAME_TYPE, i);\n+\t\t\t\t}\n+\t\t\t}\n \n \t\t\txsk->outstanding_tx += received;\n \t\t\tthread_context->received_frames = 0;\n \t\t\txdp_complete_tx(xsk);\n \n+#ifdef TX_TIMESTAMP\n+\t\t\tif (received > 0 && l2_config->tx_hwtstamp_enabled) {\n+\t\t\t\t/*\n+\t\t\t\t * TX HW timestamp becomes available in the next cycle\n+\t\t\t\t * after the packet is transmitted.\n+\t\t\t\t * This is one cycle later than SW timestamp tracked\n+\t\t\t\t * using sequence_counter\n+\t\t\t\t */\n+\t\t\t\txsk->tx_hwts.seq_lagged = sequence_counter;\n+\t\t\t}\n+#endif\n+\n \t\t\tpthread_mutex_unlock(&thread_context->xdp_data_mutex);\n \t\t}\n \n@@ -478,7 +491,7 @@ static int generic_l2_rx_frame(void *data, unsigned char *frame_data, size_t len\n \tsequence_counter = meta_data_to_sequence_counter(&l2->meta_data, num_frames_per_cycle);\n \n \ttx_timestamp = meta_data_to_tx_timestamp(&l2->meta_data);\n-\tset_mirror_tx_timestamp_est(&l2->meta_data);\n+\tset_mirror_tx_timestamp(&l2->meta_data);\n \n \txdp_get_timestamp_metadata(frame_data, &rx_hw_timestamp, &rx_sw_timestamp);\n \tout_of_order = sequence_counter != thread_context->rx_sequence_counter;\n@@ -515,7 +528,7 @@ static int generic_l2_rx_frame(void *data, unsigned char *frame_data, size_t len\n \tif (l2_config->xdp_enabled) {\n \t\t/* Re-add vlan tag */\n \t\tif (vlan_tag_missing)\n-\t\t\tinsert_vlan_tag(frame_data, len, l2_config->ether_type,\n+\t\t\tinsert_vlan_tag(frame_data, len,\n \t\t\t\t\tl2_config->vid | l2_config->pcp << VLAN_PCP_SHIFT);\n \n \t\t/* Swap mac addresses inline */\n@@ -537,8 +550,8 @@ static void *generic_l2_rx_thread_routine(void *data)\n {\n \tstruct thread_context *thread_context = data;\n \tconst uint64_t cycle_time_ns = app_config.application_base_cycle_time_ns;\n-\tint socket_fd, ret, received;\n \tstruct timespec wakeup_time;\n+\tint socket_fd, ret;\n \n \tsocket_fd = thread_context->socket_fd;\n \n@@ -573,9 +586,7 @@ static void *generic_l2_rx_thread_routine(void *data)\n \t\t}\n \n \t\t/* Receive Layer 2 frames. */\n-\t\treceived = packet_receive_messages(thread_context->packet_context, &recv_req);\n-\n-\t\tworkload_signal(thread_context, received);\n+\t\tpacket_receive_messages(thread_context->packet_context, &recv_req);\n \t}\n \n \treturn NULL;\n@@ -639,8 +650,6 @@ static void *generic_l2_xdp_rx_thread_routine(void *data)\n \t\t\t\t\t      generic_l2_rx_frame, thread_context, &tx_time);\n \t\tthread_context->received_frames = received;\n \t\tpthread_mutex_unlock(&thread_context->xdp_data_mutex);\n-\n-\t\tworkload_signal(thread_context, received);\n \t}\n \n \treturn NULL;\n@@ -650,13 +659,14 @@ struct thread_context *generic_l2_threads_create(void)\n {\n \tstruct traffic_class_config *l2_config;\n \tstruct thread_context *thread_context;\n+\tchar thread_name[128];\n \tint ret;\n \n \tthread_context = calloc(1, sizeof(*thread_context));\n \tif (!thread_context)\n \t\treturn NULL;\n \n-\tif (!config_is_traffic_class_active(GENERICL2_FRAME_TYPE))\n+\tif (!config_is_traffic_class_active(\"GenericL2\"))\n \t\tgoto out;\n \n \tthread_context->conf = l2_config = &app_config.classes[GENERICL2_FRAME_TYPE];\n@@ -720,42 +730,36 @@ struct thread_context *generic_l2_threads_create(void)\n \t\t}\n \t}\n \n-\tthread_context->meta_data_offset =\n-\t\tget_meta_data_offset(GENERICL2_FRAME_TYPE, SECURITY_MODE_NONE);\n+\tsnprintf(thread_name, sizeof(thread_name), \"%sTxThread\", l2_config->name);\n \n-\tret = create_rt_thread(&thread_context->tx_task_id, l2_config->tx_thread_priority,\n-\t\t\t       l2_config->tx_thread_cpu,\n+\tret = create_rt_thread(&thread_context->tx_task_id, thread_name,\n+\t\t\t       l2_config->tx_thread_priority, l2_config->tx_thread_cpu,\n \t\t\t       l2_config->xdp_enabled ? generic_l2_xdp_tx_thread_routine\n \t\t\t\t\t\t      : generic_l2_tx_thread_routine,\n-\t\t\t       thread_context, \"%sTxThread\", l2_config->name);\n+\t\t\t       thread_context);\n \tif (ret) {\n \t\tfprintf(stderr, \"Failed to create GenericL2 Tx Thread!\\n\");\n \t\tgoto err_thread;\n \t}\n \n-\tret = create_rt_thread(&thread_context->rx_task_id, l2_config->rx_thread_priority,\n-\t\t\t       l2_config->rx_thread_cpu,\n+\tsnprintf(thread_name, sizeof(thread_name), \"%sRxThread\", l2_config->name);\n+\n+\tret = create_rt_thread(&thread_context->rx_task_id, thread_name,\n+\t\t\t       l2_config->rx_thread_priority, l2_config->rx_thread_cpu,\n \t\t\t       l2_config->xdp_enabled ? generic_l2_xdp_rx_thread_routine\n \t\t\t\t\t\t      : generic_l2_rx_thread_routine,\n-\t\t\t       thread_context, \"%sRxThread\", l2_config->name);\n+\t\t\t       thread_context);\n \tif (ret) {\n \t\tfprintf(stderr, \"Failed to create GenericL2 Rx Thread!\\n\");\n \t\tgoto err_thread_rx;\n \t}\n \n-\t/* Create workload thread for execution after network RX */\n-\tret = workload_context_init(thread_context);\n-\tif (ret) {\n-\t\tfprintf(stderr, \"Failed to create L2 Workload context!\\n\");\n-\t\tgoto err_thread_wl;\n-\t}\n+\tthread_context->meta_data_offset =\n+\t\tget_meta_data_offset(GENERICL2_FRAME_TYPE, SECURITY_MODE_NONE);\n \n out:\n \treturn thread_context;\n \n-err_thread_wl:\n-\tthread_context->stop = 1;\n-\tpthread_join(thread_context->rx_task_id, NULL);\n err_thread_rx:\n \tthread_context->stop = 1;\n \tpthread_join(thread_context->tx_task_id, NULL);\n@@ -800,8 +804,6 @@ void generic_l2_threads_free(struct thread_context *thread_context)\n \t\txdp_close_socket(thread_context->xsk, l2_config->interface,\n \t\t\t\t l2_config->xdp_skb_mode);\n \n-\tworkload_thread_free(thread_context);\n-\n \tfree(thread_context);\n }\n \n@@ -810,8 +812,6 @@ void generic_l2_threads_wait_for_finish(struct thread_context *thread_context)\n \tif (!thread_context)\n \t\treturn;\n \n-\tworkload_thread_wait_for_finish(thread_context);\n-\n \tif (thread_context->rx_task_id)\n \t\tpthread_join(thread_context->rx_task_id, NULL);\n \tif (thread_context->tx_task_id)\ndiff --git a/src/lldp_thread.c b/src/lldp_thread.c\nindex 7cae8c8..96b75c4 100644\n--- a/src/lldp_thread.c\n+++ b/src/lldp_thread.c\n@@ -1,6 +1,6 @@\n // SPDX-License-Identifier: BSD-2-Clause\n /*\n- * Copyright (C) 2020-2026 Linutronix GmbH\n+ * Copyright (C) 2020-2025 Linutronix GmbH\n  * Author Kurt Kanzenbach <kurt@linutronix.de>\n  */\n \n@@ -39,7 +39,7 @@ static void lldp_build_frame_from_rx(unsigned char *frame_data, const unsigned c\n \n \t/* One task: Set the tx timestamp. */\n \tmeta = (struct reference_meta_data *)(frame_data + sizeof(*eth));\n-\tset_mirror_tx_timestamp_est(meta);\n+\tset_mirror_tx_timestamp(meta);\n }\n \n static void lldp_initialize_frame(struct thread_context *thread_context, unsigned char *frame_data,\n@@ -116,10 +116,6 @@ static int lldp_send_frames(struct thread_context *thread_context, unsigned char\n \tconst struct traffic_class_config *lldp_config = thread_context->conf;\n \tint len, i;\n \n-\t/* Adjust meta data */\n-\tset_mirror_tx_timestamp(lldp_config, frame_data, lldp_config->frame_length, num_frames,\n-\t\t\t\tthread_context->meta_data_offset);\n-\n \t/* Send them */\n \tlen = lldp_send_messages(thread_context, socket_fd, destination, frame_data, num_frames);\n \n@@ -146,7 +142,7 @@ static int lldp_gen_and_send_frames(struct thread_context *thread_context, int s\n \tstruct ethhdr *eth;\n \tint len, i;\n \n-\tapp_clock_get(&tx_time);\n+\tclock_gettime(app_config.application_clock_id, &tx_time);\n \n \t/* Adjust meta data */\n \tfor (i = 0; i < lldp_config->num_frames_per_cycle; i++) {\n@@ -162,8 +158,8 @@ static int lldp_gen_and_send_frames(struct thread_context *thread_context, int s\n \tlen = lldp_send_messages(thread_context, socket_fd, destination,\n \t\t\t\t thread_context->tx_frame_data, lldp_config->num_frames_per_cycle);\n \n-\tif (len > 0)\n-\t\tstat_frames_sent_batch(LLDP_FRAME_TYPE, sequence_counter_begin, len);\n+\tfor (i = 0; i < len; i++)\n+\t\tstat_frame_sent(LLDP_FRAME_TYPE, sequence_counter_begin + i);\n \n \treturn len;\n }\n@@ -444,7 +440,7 @@ int lldp_threads_create(struct thread_context *thread_context)\n \tstruct traffic_class_config *lldp_config;\n \tint ret;\n \n-\tif (!config_is_traffic_class_active(LLDP_FRAME_TYPE))\n+\tif (!config_is_traffic_class_active(\"Lldp\"))\n \t\tgoto out;\n \n \tthread_context->conf = lldp_config = &app_config.classes[LLDP_FRAME_TYPE];\n@@ -493,36 +489,35 @@ int lldp_threads_create(struct thread_context *thread_context)\n \t\t}\n \t}\n \n-\tthread_context->meta_data_offset =\n-\t\tget_meta_data_offset(LLDP_FRAME_TYPE, SECURITY_MODE_NONE);\n-\n-\tret = create_rt_thread(&thread_context->tx_task_id, lldp_config->tx_thread_priority,\n-\t\t\t       lldp_config->tx_thread_cpu, lldp_tx_thread_routine, thread_context,\n-\t\t\t       \"LldpTxThread\");\n+\tret = create_rt_thread(&thread_context->tx_task_id, \"LldpTxThread\",\n+\t\t\t       lldp_config->tx_thread_priority, lldp_config->tx_thread_cpu,\n+\t\t\t       lldp_tx_thread_routine, thread_context);\n \tif (ret) {\n \t\tfprintf(stderr, \"Failed to create Lldp Tx Thread!\\n\");\n \t\tgoto err_thread;\n \t}\n \n \tif (!lldp_config->rx_mirror_enabled) {\n-\t\tret = create_rt_thread(&thread_context->tx_gen_task_id,\n+\t\tret = create_rt_thread(&thread_context->tx_gen_task_id, \"LldpTxGenThread\",\n \t\t\t\t       lldp_config->tx_thread_priority, lldp_config->tx_thread_cpu,\n-\t\t\t\t       lldp_tx_generation_thread_routine, thread_context,\n-\t\t\t\t       \"LldpTxGenThread\");\n+\t\t\t\t       lldp_tx_generation_thread_routine, thread_context);\n \t\tif (ret) {\n \t\t\tfprintf(stderr, \"Failed to create Lldp TxGen Thread!\\n\");\n \t\t\tgoto err_thread_txgen;\n \t\t}\n \t}\n \n-\tret = create_rt_thread(&thread_context->rx_task_id, lldp_config->rx_thread_priority,\n-\t\t\t       lldp_config->rx_thread_cpu, lldp_rx_thread_routine, thread_context,\n-\t\t\t       \"LldpRxThread\");\n+\tret = create_rt_thread(&thread_context->rx_task_id, \"LldpRxThread\",\n+\t\t\t       lldp_config->rx_thread_priority, lldp_config->rx_thread_cpu,\n+\t\t\t       lldp_rx_thread_routine, thread_context);\n \tif (ret) {\n \t\tfprintf(stderr, \"Failed to create Lldp Rx Thread!\\n\");\n \t\tgoto err_thread_rx;\n \t}\n \n+\tthread_context->meta_data_offset =\n+\t\tget_meta_data_offset(LLDP_FRAME_TYPE, SECURITY_MODE_NONE);\n+\n out:\n \tret = 0;\n \ndiff --git a/src/log.c b/src/log.c\nindex 32b04e4..b3a608e 100644\n--- a/src/log.c\n+++ b/src/log.c\n@@ -1,6 +1,6 @@\n // SPDX-License-Identifier: BSD-2-Clause\n /*\n- * Copyright (C) 2020-2026 Linutronix GmbH\n+ * Copyright (C) 2020-2025 Linutronix GmbH\n  * Author Kurt Kanzenbach <kurt@linutronix.de>\n  */\n #include <errno.h>\n@@ -42,7 +42,7 @@ int log_init(void)\n \t\tcurrent_log_level = LOG_LEVEL_ERROR;\n \n \tif (app_config.debug_stop_trace_on_error) {\n-\t\tfile_tracing_on = fopen(\"/sys/kernel/tracing/tracing_on\", \"w\");\n+\t\tfile_tracing_on = fopen(\"/sys/kernel/debug/tracing/tracing_on\", \"w\");\n \t\tif (!file_tracing_on)\n \t\t\treturn -errno;\n \t}\n@@ -75,10 +75,9 @@ static const char *log_level_to_string(enum log_level level)\n void log_message(enum log_level level, const char *format, ...)\n {\n \tunsigned char buffer[4096];\n+\tint written, len, ret;\n \tstruct timespec time;\n-\tsize_t len, written;\n \tva_list args;\n-\tint ret;\n \tchar *p;\n \n \t/* Stop trace on error if desired. */\n@@ -94,228 +93,110 @@ void log_message(enum log_level level, const char *format, ...)\n \tif (ret)\n \t\tmemset(&time, '\\0', sizeof(time));\n \n-\tlen = sizeof(buffer);\n+\tlen = sizeof(buffer) - 1;\n \tp = (char *)buffer;\n \n-\tret = snprintf(p, len, \"[%08lld.%09ld]: [%s]: \", (long long int)time.tv_sec, time.tv_nsec,\n-\t\t       log_level_to_string(level));\n-\tret = snprintf_err_handling(&p, &len, ret);\n-\tif (ret)\n-\t\treturn;\n+\twritten = snprintf(p, len, \"[%8ld.%9ld]: [%s]: \", time.tv_sec, time.tv_nsec,\n+\t\t\t   log_level_to_string(level));\n+\tp += written;\n+\tlen -= written;\n \n \tva_start(args, format);\n-\tret = vsnprintf(p, len, format, args);\n-\tret = snprintf_err_handling(&p, &len, ret);\n+\twritten += vsnprintf(p, len, format, args);\n \tva_end(args);\n \n-\tif (ret)\n-\t\treturn;\n-\n-\twritten = sizeof(buffer) - len;\n \tring_buffer_add(global_log_ring_buffer, buffer, written);\n }\n \n-static int log_add_stats(const char *name, enum stat_frame_type frame_type, char **buffer,\n-\t\t\t size_t *length)\n+static void log_add_traffic_class(const char *name, enum stat_frame_type frame_type, char **buffer,\n+\t\t\t\t  size_t *length)\n {\n \tconst struct statistics *stat = &global_statistics[frame_type];\n-\tint ret;\n+\tint written;\n \n-\tret = snprintf(*buffer, *length,\n-\t\t       \"%sSent=%\" PRIu64 \" | %sReceived=%\" PRIu64 \" | %sRttMin=%\" PRIu64\n-\t\t       \" [us] | %sRttMax=%\" PRIu64\n-\t\t       \" [us] | %sRttAvg=%lf [us] | %sOnewayMin=%\" PRIi64\n-\t\t       \" [us] | %sOnewayMax=%\" PRIi64 \" [us] | %sOnewayAvg=%lf [us] | \",\n-\t\t       name, stat->frames_sent, name, stat->frames_received, name,\n-\t\t       stat->round_trip_min, name, stat->round_trip_max, name, stat->round_trip_avg,\n-\t\t       name, stat->oneway_min, name, stat->oneway_max, name, stat->oneway_avg);\n+\twritten = snprintf(\n+\t\t*buffer, *length,\n+\t\t\"%sSent=%\" PRIu64 \" | %sReceived=%\" PRIu64 \" | %sRttMin=%\" PRIu64\n+\t\t\" [us] | %sRttMax=%\" PRIu64 \" [us] | %sRttAvg=%lf [us] | %sOnewayMin=%\" PRIu64\n+\t\t\" [us] | %sOnewayMax=%\" PRIu64 \" [us] | %sOnewayAvg=%lf [us] | \",\n+\t\tname, stat->frames_sent, name, stat->frames_received, name, stat->round_trip_min,\n+\t\tname, stat->round_trip_max, name, stat->round_trip_avg, name, stat->oneway_min,\n+\t\tname, stat->oneway_max, name, stat->oneway_avg);\n \n-\treturn snprintf_err_handling(buffer, length, ret);\n-}\n-\n-static int log_add_proc_first_stats(const char *name, enum stat_frame_type frame_type,\n-\t\t\t\t    char **buffer, size_t *length)\n-{\n-\tconst struct statistics *stat = &global_statistics[frame_type];\n-\tint ret;\n+\t*buffer += written;\n+\t*length -= written;\n \n \tif (app_config.classes[frame_type].tx_hwtstamp_enabled && config_have_rx_timestamp() &&\n \t    app_config.classes[frame_type].xdp_enabled && stat->proc_first_count > 0) {\n-\t\tret = snprintf(*buffer, *length,\n-\t\t\t       \"%sProcFirstMin=%\" PRIu64 \" [us] | %sProcFirstMax=%\" PRIu64\n-\t\t\t       \" [us] | %sProcFirstAvg=%lf [us] | \"\n-\t\t\t       \"%sProcFirstOutliers=%\" PRIu64 \" | \",\n-\t\t\t       name, stat->proc_first_min, name, stat->proc_first_max, name,\n-\t\t\t       stat->proc_first_avg, name, stat->proc_first_outliers);\n-\t\treturn snprintf_err_handling(buffer, length, ret);\n+\t\twritten = snprintf(*buffer, *length,\n+\t\t\t\t   \"%sProcFirstMin=%\" PRIu64 \" [us] | %sProcFirstMax=%\" PRIu64\n+\t\t\t\t   \" [us] | %sProcFirstAvg=%lf [us] | \",\n+\t\t\t\t   name, stat->proc_first_min, name, stat->proc_first_max, name,\n+\t\t\t\t   stat->proc_first_avg);\n+\t\t*buffer += written;\n+\t\t*length -= written;\n \t}\n \n-\treturn 0;\n-}\n-\n-static int log_add_proc_batch_stats(const char *name, enum stat_frame_type frame_type,\n-\t\t\t\t    char **buffer, size_t *length)\n-{\n-\tconst struct statistics *stat = &global_statistics[frame_type];\n-\tint ret;\n-\n \tif (app_config.classes[frame_type].tx_hwtstamp_enabled && config_have_rx_timestamp() &&\n \t    app_config.classes[frame_type].xdp_enabled && stat->proc_batch_count > 0) {\n-\t\tret = snprintf(*buffer, *length,\n-\t\t\t       \"%sProcBatchMin=%\" PRIu64 \" [us] | %sProcBatchMax=%\" PRIu64\n-\t\t\t       \" [us] | %sProcBatchAvg=%lf [us] | \"\n-\t\t\t       \"%sProcBatchOutliers=%\" PRIu64 \" | \",\n-\t\t\t       name, stat->proc_batch_min, name, stat->proc_batch_max, name,\n-\t\t\t       stat->proc_batch_avg, name, stat->proc_batch_outliers);\n-\n-\t\treturn snprintf_err_handling(buffer, length, ret);\n+\t\twritten = snprintf(*buffer, *length,\n+\t\t\t\t   \"%sProcBatchMin=%\" PRIu64 \" [us] | %sProcBatchMax=%\" PRIu64\n+\t\t\t\t   \" [us] | %sProcBatchAvg=%lf [us] | \",\n+\t\t\t\t   name, stat->proc_batch_min, name, stat->proc_batch_max, name,\n+\t\t\t\t   stat->proc_batch_avg);\n+\t\t*buffer += written;\n+\t\t*length -= written;\n \t}\n \n-\treturn 0;\n-}\n-\n-static int log_add_xdp_rx_stats(const char *name, enum stat_frame_type frame_type, char **buffer,\n-\t\t\t\tsize_t *length)\n-{\n-\tconst struct statistics *stat = &global_statistics[frame_type];\n-\tint ret;\n-\n \tif (config_have_rx_timestamp() && app_config.classes[frame_type].xdp_enabled) {\n-\t\tret = snprintf(*buffer, *length,\n-\t\t\t       \"%sRxMin=%\" PRIu64 \" [us] | %sRxMax=%\" PRIu64\n-\t\t\t       \" [us] | %sRxAvg=%lf [us] | \"\n-\t\t\t       \"%sRxHw2XdpMin=%\" PRIu64 \" [us] | %sRxHw2XdpMax=%\" PRIu64\n-\t\t\t       \" [us] | %sRxHw2XdpAvg=%lf [us] | \"\n-\t\t\t       \"%sRxXdp2AppMin=%\" PRIu64 \" [us] | %sRxXdp2AppMax=%\" PRIu64\n-\t\t\t       \" [us] | %sRxXdp2AppAvg=%lf [us] | \",\n-\t\t\t       name, stat->rx_min, name, stat->rx_max, name, stat->rx_avg, name,\n-\t\t\t       stat->rx_hw2xdp_min, name, stat->rx_hw2xdp_max, name,\n-\t\t\t       stat->rx_hw2xdp_avg, name, stat->rx_xdp2app_min, name,\n-\t\t\t       stat->rx_xdp2app_max, name, stat->rx_xdp2app_avg);\n-\n-\t\treturn snprintf_err_handling(buffer, length, ret);\n+\t\twritten = snprintf(*buffer, *length,\n+\t\t\t\t   \"%sRxMin=%\" PRIu64 \" [us] | %sRxMax=%\" PRIu64\n+\t\t\t\t   \" [us] | %sRxAvg=%lf [us] | \"\n+\t\t\t\t   \"%sRxHw2XdpMin=%\" PRIu64 \" [us] | %sRxHw2XdpMax=%\" PRIu64\n+\t\t\t\t   \" [us] | %sRxHw2XdpAvg=%lf [us] | \"\n+\t\t\t\t   \"%sRxXdp2AppMin=%\" PRIu64 \" [us] | %sRxXdp2AppMax=%\" PRIu64\n+\t\t\t\t   \" [us] | %sRxXdp2AppAvg=%lf [us] | \",\n+\t\t\t\t   name, stat->rx_min, name, stat->rx_max, name, stat->rx_avg, name,\n+\t\t\t\t   stat->rx_hw2xdp_min, name, stat->rx_hw2xdp_max, name,\n+\t\t\t\t   stat->rx_hw2xdp_avg, name, stat->rx_xdp2app_min, name,\n+\t\t\t\t   stat->rx_xdp2app_max, name, stat->rx_xdp2app_avg);\n+\n+\t\t*buffer += written;\n+\t\t*length -= written;\n \t}\n \n-\treturn 0;\n-}\n-\n-static int log_add_xdp_tx_stats(const char *name, enum stat_frame_type frame_type, char **buffer,\n-\t\t\t\tsize_t *length)\n-{\n-\tconst struct statistics *stat = &global_statistics[frame_type];\n-\tint ret;\n-\n \tif (app_config.classes[frame_type].tx_hwtstamp_enabled &&\n \t    app_config.classes[frame_type].xdp_enabled) {\n-\t\tret = snprintf(*buffer, *length,\n-\t\t\t       \"%sTxMin=%\" PRIu64 \" [us] | %sTxMax=%\" PRIu64\n-\t\t\t       \" [us] | %sTxAvg=%lf [us] | %sTxHwTimestampMissing=%\" PRIu64 \" | \",\n-\t\t\t       name, stat->tx_min, name, stat->tx_max, name, stat->tx_avg, name,\n-\t\t\t       stat->tx_hw_timestamp_missing);\n-\n-\t\treturn snprintf_err_handling(buffer, length, ret);\n+\t\twritten =\n+\t\t\tsnprintf(*buffer, *length,\n+\t\t\t\t \"%sTxMin=%\" PRIu64 \" [us] | %sTxMax=%\" PRIu64\n+\t\t\t\t \" [us] | %sTxAvg=%lf [us] | %sTxHwTimestampMissing=%\" PRIu64 \" | \",\n+\t\t\t\t name, stat->tx_min, name, stat->tx_max, name, stat->tx_avg, name,\n+\t\t\t\t stat->tx_hw_timestamp_missing);\n+\t\t*buffer += written;\n+\t\t*length -= written;\n \t}\n \n-\treturn 0;\n-}\n-\n-static int log_add_outlier_stats(const char *name, enum stat_frame_type frame_type, char **buffer,\n-\t\t\t\t size_t *length)\n-{\n-\tconst struct statistics *stat = &global_statistics[frame_type];\n-\tint ret;\n-\n \tif (stat_frame_type_is_real_time(frame_type)) {\n-\t\tret = snprintf(*buffer, *length,\n-\t\t\t       \"%sRttOutliers=%\" PRIu64 \" | %sOnewayOutliers=%\" PRIu64 \" | \", name,\n-\t\t\t       stat->round_trip_outliers, name, stat->oneway_outliers);\n-\n-\t\treturn snprintf_err_handling(buffer, length, ret);\n-\t}\n-\n-\treturn 0;\n-}\n-\n-static int log_add_workload_stats(const char *name, enum stat_frame_type frame_type, char **buffer,\n-\t\t\t\t  size_t *length)\n-{\n-\tconst struct statistics *stat = &global_statistics[frame_type];\n-\tint ret, num;\n-\n-\tif (!app_config.classes[frame_type].rx_workload_enabled)\n-\t\treturn 0;\n-\n-\tnum = app_config.classes[frame_type].workload_thread_cpus_num;\n-\n-\t/* Keep old statistic names if num == 1. */\n-\tif (num == 1) {\n-\t\tret = snprintf(*buffer, *length,\n-\t\t\t       \"%sRxWorkloadMin=%\" PRIu64 \" [us] | \"\n-\t\t\t       \"%sRxWorkloadMax=%\" PRIu64 \" [us] | \"\n-\t\t\t       \"%sRxWorkloadAvg=%lf [us] | \"\n-\t\t\t       \"%sRxWorkloadOutliers=%\" PRIu64 \" | \",\n-\t\t\t       name, stat->workload[0].rx_workload_min, name,\n-\t\t\t       stat->workload[0].rx_workload_max, name,\n-\t\t\t       stat->workload[0].rx_workload_avg, name,\n-\t\t\t       stat->workload[0].rx_workload_outliers);\n-\n-\t\treturn snprintf_err_handling(buffer, length, ret);\n+\t\twritten = snprintf(*buffer, *length,\n+\t\t\t\t   \"%sRttOutliers=%\" PRIu64 \" | %sOnewayOutliers=%\" PRIu64 \" | \",\n+\t\t\t\t   name, stat->round_trip_outliers, name, stat->oneway_outliers);\n+\t\t*buffer += written;\n+\t\t*length -= written;\n \t}\n \n-\t/* If num > 1 add the id into statistic names. */\n-\tfor (int i = 0; i < num; i++) {\n-\t\tconst struct workload_statistics *wl = &stat->workload[i];\n-\n-\t\tret = snprintf(*buffer, *length,\n-\t\t\t       \"%sRxWorkload%dMin=%\" PRIu64 \" [us] | \"\n-\t\t\t       \"%sRxWorkload%dMax=%\" PRIu64 \" [us] | \"\n-\t\t\t       \"%sRxWorkload%dAvg=%lf [us] | \"\n-\t\t\t       \"%sRxWorkload%dOutliers=%\" PRIu64 \" | \",\n-\t\t\t       name, i, wl->rx_workload_min, name, i, wl->rx_workload_max, name, i,\n-\t\t\t       wl->rx_workload_avg, name, i, wl->rx_workload_outliers);\n-\n-\t\tret = snprintf_err_handling(buffer, length, ret);\n-\t\tif (ret)\n-\t\t\treturn ret;\n+\tif (app_config.classes[frame_type].rx_workload_enabled) {\n+\t\twritten = snprintf(*buffer, *length,\n+\t\t\t\t   \"%sRxWorkloadMin=%\" PRIu64 \" [us] | \"\n+\t\t\t\t   \"%sRxWorkloadMax=%\" PRIu64 \" [us] | \"\n+\t\t\t\t   \"%sRxWorkloadAvg=%lf [us] | \"\n+\t\t\t\t   \"%sRxWorkloadOutliers=%\" PRIu64 \" | \",\n+\t\t\t\t   name, stat->rx_workload_min, name, stat->rx_workload_max, name,\n+\t\t\t\t   stat->rx_workload_avg, name, stat->rx_workload_outliers);\n+\n+\t\t*buffer += written;\n+\t\t*length -= written;\n \t}\n-\n-\treturn 0;\n-}\n-\n-static int log_add_traffic_class(const char *name, enum stat_frame_type frame_type, char **buffer,\n-\t\t\t\t size_t *length)\n-{\n-\tint ret;\n-\n-\tret = log_add_stats(name, frame_type, buffer, length);\n-\tif (ret)\n-\t\treturn ret;\n-\n-\tret = log_add_proc_first_stats(name, frame_type, buffer, length);\n-\tif (ret)\n-\t\treturn ret;\n-\n-\tret = log_add_proc_batch_stats(name, frame_type, buffer, length);\n-\tif (ret)\n-\t\treturn ret;\n-\n-\tret = log_add_xdp_rx_stats(name, frame_type, buffer, length);\n-\tif (ret)\n-\t\treturn ret;\n-\n-\tret = log_add_xdp_tx_stats(name, frame_type, buffer, length);\n-\tif (ret)\n-\t\treturn ret;\n-\n-\tret = log_add_outlier_stats(name, frame_type, buffer, length);\n-\tif (ret)\n-\t\treturn ret;\n-\n-\tret = log_add_workload_stats(name, frame_type, buffer, length);\n-\tif (ret)\n-\t\treturn ret;\n-\n-\treturn 0;\n }\n \n static void *log_thread_routine(void *data)\n@@ -331,21 +212,20 @@ static void *log_thread_routine(void *data)\n \t */\n \tret = clock_gettime(app_config.application_clock_id, &time);\n \tif (ret) {\n-\t\tfprintf(stderr, \"LOG: clock_gettime() failed: %s!\\n\", strerror(errno));\n+\t\tfprintf(stderr, \"Log: clock_gettime() failed: %s!\", strerror(errno));\n \t\treturn NULL;\n \t}\n \n \twhile (!log_context->stop) {\n \t\tsize_t log_data_len, stat_message_length;\n \t\tchar stat_message[4096] = {}, *p;\n-\t\tbool success = true;\n \t\tint i;\n \n \t\t/* Wait until next period */\n \t\tincrement_period(&time, period);\n \t\tret = clock_nanosleep(app_config.application_clock_id, TIMER_ABSTIME, &time, NULL);\n \t\tif (ret) {\n-\t\t\tpthread_error(ret, \"LOG: clock_nanosleep() failed\");\n+\t\t\tpthread_error(ret, \"clock_nanosleep() failed\");\n \t\t\treturn NULL;\n \t\t}\n \n@@ -354,20 +234,20 @@ static void *log_thread_routine(void *data)\n \n \t\t/* Log statistics once per logging period. */\n \t\tp = stat_message;\n-\t\tstat_message_length = sizeof(stat_message);\n+\t\tstat_message_length = sizeof(stat_message) - 1;\n \n \t\tfor (i = 0; i < NUM_FRAME_TYPES; i++) {\n-\t\t\tif (config_is_traffic_class_active(i)) {\n-\t\t\t\tconst char *name = stat_frame_type_to_string(i);\n+\t\t\tif (config_is_traffic_class_active(stat_frame_type_to_string(i))) {\n+\t\t\t\tconst char *name =\n+\t\t\t\t\ti == GENERICL2_FRAME_TYPE\n+\t\t\t\t\t\t? app_config.classes[GENERICL2_FRAME_TYPE].name\n+\t\t\t\t\t\t: stat_frame_type_to_string(i);\n \n-\t\t\t\tret = log_add_traffic_class(name, i, &p, &stat_message_length);\n-\t\t\t\tif (ret)\n-\t\t\t\t\tsuccess = false;\n+\t\t\t\tlog_add_traffic_class(name, i, &p, &stat_message_length);\n \t\t\t}\n \t\t}\n \n-\t\tif (success)\n-\t\t\tlog_message(LOG_LEVEL_INFO, \"%s\\n\", stat_message);\n+\t\tlog_message(LOG_LEVEL_INFO, \"%s\\n\", stat_message);\n \n \t\t/* Fetch data */\n \t\tring_buffer_fetch(log_context->log_ring_buffer, log_context->log_data,\n@@ -393,7 +273,7 @@ struct log_thread_context *log_thread_create(void)\n \tif (!log_context)\n \t\treturn NULL;\n \n-\tlog_context->log_data = calloc(LOG_BUFFER_SIZE, sizeof(unsigned char));\n+\tlog_context->log_data = calloc(LOG_BUFFER_SIZE, sizeof(char));\n \tif (!log_context->log_data)\n \t\tgoto err_log_data;\n \n@@ -403,9 +283,8 @@ struct log_thread_context *log_thread_create(void)\n \tif (!log_context->file_handle)\n \t\tgoto err_fopen;\n \n-\tret = create_rt_thread(&log_context->log_task_id, app_config.log_thread_priority,\n-\t\t\t       app_config.log_thread_cpu, log_thread_routine, log_context,\n-\t\t\t       \"Logger\");\n+\tret = create_rt_thread(&log_context->log_task_id, \"Logger\", app_config.log_thread_priority,\n+\t\t\t       app_config.log_thread_cpu, log_thread_routine, log_context);\n \tif (ret)\n \t\tgoto err_thread;\n \ndiff --git a/src/log_json.c b/src/log_json.c\nindex 5ac7e87..3c5b6f8 100644\n--- a/src/log_json.c\n+++ b/src/log_json.c\n@@ -1,6 +1,6 @@\n // SPDX-License-Identifier: BSD-2-Clause\n /*\n- * Copyright (C) 2025-2026 Linutronix GmbH\n+ * Copyright (C) 2025 Linutronix GmbH\n  */\n \n #include <errno.h>\n@@ -24,15 +24,13 @@\n #include \"utils.h\"\n \n static void log_json_add_traffic_class(struct log_json_thread_context *ctx,\n-\t\t\t\t       enum stat_frame_type frame_type,\n-\t\t\t\t       const struct statistics *stat)\n+\t\t\t\t       const struct statistics *stat, const char *tc)\n {\n-\tconst char *tc = stat_frame_type_to_string(frame_type);\n \tchar stat_message[4096];\n \tint ret;\n \n \t/* Convert stats to json */\n-\tret = stat_to_json(stat_message, sizeof(stat_message), frame_type, stat, tc,\n+\tret = stat_to_json(stat_message, sizeof(stat_message), stat, tc,\n \t\t\t   app_config.log_json_measurement_name);\n \tif (ret) {\n \t\tlog_message(LOG_LEVEL_DEBUG, \"JsonTx: Failed to compose JSON message: %s\\n\",\n@@ -73,7 +71,7 @@ void *log_json_publisher(void *data)\n \t */\n \tret = clock_gettime(app_config.application_clock_id, &time);\n \tif (ret) {\n-\t\tfprintf(stderr, \"JSON: clock_gettime() failed: %s!\\n\", strerror(errno));\n+\t\tfprintf(stderr, \"JSON: clock_gettime() failed: %s!\", strerror(errno));\n \t\tgoto out;\n \t}\n \n@@ -90,8 +88,9 @@ void *log_json_publisher(void *data)\n \n \t\t/* Publish via UDP */\n \t\tfor (int i = 0; i < NUM_FRAME_TYPES; i++) {\n-\t\t\tif (config_is_traffic_class_active(i))\n-\t\t\t\tlog_json_add_traffic_class(ctx, i, &stats[i]);\n+\t\t\tif (config_is_traffic_class_active(stat_frame_type_to_string(i)))\n+\t\t\t\tlog_json_add_traffic_class(ctx, &stats[i],\n+\t\t\t\t\t\t\t   stat_frame_type_to_string(i));\n \t\t}\n \t}\n \n@@ -120,9 +119,8 @@ struct log_json_thread_context *log_json_thread_create(void)\n \t}\n \n \t/* Start dedicated JSON/UDP pubslishing thread */\n-\tret = create_rt_thread(&ctx->pid, app_config.log_json_thread_priority,\n-\t\t\t       app_config.log_json_thread_cpu, log_json_publisher, ctx,\n-\t\t\t       \"LoggerJson\");\n+\tret = create_rt_thread(&ctx->pid, \"LoggerJson\", app_config.log_json_thread_priority,\n+\t\t\t       app_config.log_json_thread_cpu, log_json_publisher, ctx);\n \n \tif (ret) {\n \t\tfprintf(stderr, \"JSON: Failed to start JSON Logger thread!\\n\");\ndiff --git a/src/log_mqtt.c b/src/log_mqtt.c\nindex 8327c84..16bb22f 100644\n--- a/src/log_mqtt.c\n+++ b/src/log_mqtt.c\n@@ -2,7 +2,7 @@\n /*\n  * Copyright (C) 2024 Intel Corporation.\n  * Author Walfred Tedeschi <walfred.tedeschi@intel.com>\n- * Copyright (C) 2025-2026 Linutronix GmbH\n+ * Copyright (C) 2025 Linutronix GmbH\n  * Author Kurt Kanzenbach <kurt@linutronix.de>\n  */\n \n@@ -26,8 +26,6 @@\n #include \"utils.h\"\n \n #ifndef WITH_MQTT\n-#pragma GCC diagnostic push\n-#pragma GCC diagnostic ignored \"-Wunused-parameter\"\n struct log_mqtt_thread_context *log_mqtt_thread_create(void)\n {\n \treturn NULL;\n@@ -40,30 +38,28 @@ void log_mqtt_thread_wait_for_finish(struct log_mqtt_thread_context *thread_cont\n void log_mqtt_thread_free(struct log_mqtt_thread_context *thread_context)\n {\n }\n-#pragma GCC diagnostic pop\n+\n #else\n \n static struct statistics statistics_per_period[NUM_FRAME_TYPES];\n \n static void log_mqtt_add_traffic_class(struct mosquitto *mosq, const char *mqtt_base_topic_name,\n-\t\t\t\t       enum stat_frame_type frame_type, struct statistics *stat)\n+\t\t\t\t       struct statistics *stat, const char *tc)\n {\n-\tconst char *tc = stat_frame_type_to_string(frame_type);\n \tchar stat_message[4096] = {};\n \tint result_pub;\n \n \t/* Convert stats to json */\n-\tstat_to_json(stat_message, sizeof(stat_message), frame_type, stat, tc,\n-\t\t     mqtt_base_topic_name);\n+\tstat_to_json(stat_message, sizeof(stat_message), stat, tc, mqtt_base_topic_name);\n \n \t/* Publish */\n \tresult_pub = mosquitto_publish(mosq, NULL, \"testbench\", strlen(stat_message), stat_message,\n \t\t\t\t       2, false);\n \tif (result_pub != MOSQ_ERR_SUCCESS)\n-\t\tfprintf(stderr, \"MQTT: Error publishing: %s\\n\", mosquitto_strerror(result_pub));\n+\t\tfprintf(stderr, \"Error publishing: %s\\n\", mosquitto_strerror(result_pub));\n }\n \n-static void log_mqtt_on_connect(struct mosquitto *mosq, void __unused *obj, int reason_code)\n+static void log_mqtt_on_connect(struct mosquitto *mosq, void *obj, int reason_code)\n {\n \tif (reason_code != 0)\n \t\tmosquitto_disconnect(mosq);\n@@ -80,7 +76,7 @@ static void *log_mqtt_thread_routine(void *data)\n \n \tmqtt_context->mosq = mosquitto_new(NULL, true, NULL);\n \tif (mqtt_context->mosq == NULL) {\n-\t\tfprintf(stderr, \"MQTT: Out of memory.\\n\");\n+\t\tfprintf(stderr, \"MQTTLog Error: Out of memory.\\n\");\n \t\tgoto err_mqtt_outof_memory;\n \t}\n \n@@ -88,7 +84,8 @@ static void *log_mqtt_thread_routine(void *data)\n \t\t\t\t\t   app_config.log_mqtt_broker_port,\n \t\t\t\t\t   app_config.log_mqtt_keep_alive_secs);\n \tif (connect_status != MOSQ_ERR_SUCCESS) {\n-\t\tfprintf(stderr, \"MQTT: Error by connect: %s\\n\", mosquitto_strerror(connect_status));\n+\t\tfprintf(stderr, \"MQTTLog Error by connect: %s\\n\",\n+\t\t\tmosquitto_strerror(connect_status));\n \t\tgoto err_mqtt_connect;\n \t}\n \n@@ -96,7 +93,7 @@ static void *log_mqtt_thread_routine(void *data)\n \n \tret = mosquitto_loop_start(mqtt_context->mosq);\n \tif (ret != MOSQ_ERR_SUCCESS) {\n-\t\tfprintf(stderr, \"MQTT: Error: %s\\n\", mosquitto_strerror(ret));\n+\t\tfprintf(stderr, \"Log Via MQTT Error: %s\\n\", mosquitto_strerror(ret));\n \t\tgoto err_mqtt_start;\n \t}\n \n@@ -106,7 +103,7 @@ static void *log_mqtt_thread_routine(void *data)\n \t */\n \tret = clock_gettime(app_config.application_clock_id, &time);\n \tif (ret) {\n-\t\tfprintf(stderr, \"MQTT: clock_gettime() failed: %s!\\n\", strerror(errno));\n+\t\tfprintf(stderr, \"Log Via MQTT: clock_gettime() failed: %s!\", strerror(errno));\n \t\tgoto err_time;\n \t}\n \n@@ -116,7 +113,7 @@ static void *log_mqtt_thread_routine(void *data)\n \t\tincrement_period(&time, period_ns);\n \t\tret = clock_nanosleep(app_config.application_clock_id, TIMER_ABSTIME, &time, NULL);\n \t\tif (ret) {\n-\t\t\tpthread_error(ret, \"MQTT: clock_nanosleep() failed\");\n+\t\t\tpthread_error(ret, \"clock_nanosleep() failed\");\n \t\t\tgoto err_time;\n \t\t}\n \n@@ -125,13 +122,15 @@ static void *log_mqtt_thread_routine(void *data)\n \n \t\t/* Publish via MQTT */\n \t\tfor (i = 0; i < NUM_FRAME_TYPES; i++) {\n-\t\t\tif (config_is_traffic_class_active(i))\n-\t\t\t\tlog_mqtt_add_traffic_class(mqtt_context->mosq,\n-\t\t\t\t\t\t\t   app_config.log_mqtt_measurement_name, i,\n-\t\t\t\t\t\t\t   &statistics_per_period[i]);\n+\t\t\tif (config_is_traffic_class_active(stat_frame_type_to_string(i)))\n+\t\t\t\tlog_mqtt_add_traffic_class(\n+\t\t\t\t\tmqtt_context->mosq, app_config.log_mqtt_measurement_name,\n+\t\t\t\t\t&statistics_per_period[i], stat_frame_type_to_string(i));\n \t\t}\n \t}\n \n+\treturn NULL;\n+\n err_mqtt_outof_memory:\n err_mqtt_connect:\n err_mqtt_start:\n@@ -154,9 +153,9 @@ struct log_mqtt_thread_context *log_mqtt_thread_create(void)\n \tif (!mqtt_context)\n \t\treturn NULL;\n \n-\tret = create_rt_thread(&mqtt_context->mqtt_log_task_id, app_config.log_mqtt_thread_priority,\n-\t\t\t       app_config.log_mqtt_thread_cpu, log_mqtt_thread_routine,\n-\t\t\t       mqtt_context, \"LoggerGraph\");\n+\tret = create_rt_thread(&mqtt_context->mqtt_log_task_id, \"LoggerGraph\",\n+\t\t\t       app_config.log_mqtt_thread_priority, app_config.log_mqtt_thread_cpu,\n+\t\t\t       log_mqtt_thread_routine, mqtt_context);\n \n \tif (ret)\n \t\tgoto err_thread;\n@@ -173,6 +172,12 @@ void log_mqtt_thread_free(struct log_mqtt_thread_context *thread_context)\n \tif (!thread_context)\n \t\treturn;\n \n+\tif (app_config.log_mqtt) {\n+\t\tif (thread_context->mosq)\n+\t\t\tmosquitto_destroy(thread_context->mosq);\n+\t\tmosquitto_lib_cleanup();\n+\t}\n+\n \tfree(thread_context);\n }\n \ndiff --git a/src/mirror.c b/src/mirror.c\nindex fbd52d0..cb4dcb2 100644\n--- a/src/mirror.c\n+++ b/src/mirror.c\n@@ -1,24 +1,317 @@\n // SPDX-License-Identifier: BSD-2-Clause\n /*\n- * Copyright (C) 2020-2026 Linutronix GmbH\n+ * Copyright (C) 2020-2025 Linutronix GmbH\n  * Author Kurt Kanzenbach <kurt@linutronix.de>\n  */\n \n-#include <stdbool.h>\n+#include <stdio.h>\n #include <stdlib.h>\n \n-#include \"tb.h\"\n+#include <getopt.h>\n+#include <signal.h>\n+#include <stdio.h>\n+#include <stdlib.h>\n+\n+#include <sys/mman.h>\n+\n+#include \"app_config.h\"\n+#include \"config.h\"\n+#include \"dcp_thread.h\"\n+#include \"layer2_thread.h\"\n+#include \"lldp_thread.h\"\n+#include \"log.h\"\n+#include \"log_json.h\"\n+#include \"log_mqtt.h\"\n+#include \"print.h\"\n+#include \"rta_thread.h\"\n+#include \"rtc_thread.h\"\n+#include \"stat.h\"\n+#include \"thread.h\"\n+#include \"tsn_thread.h\"\n+#include \"udp_thread.h\"\n+#include \"utils.h\"\n+\n+static struct option long_options[] = {\n+\t{\"config\", optional_argument, NULL, 'c'},\n+\t{\"help\", no_argument, NULL, 'h'},\n+\t{\"version\", no_argument, NULL, 'V'},\n+\t{NULL},\n+};\n+\n+static struct log_mqtt_thread_context *log_mqtt_thread;\n+static struct log_json_thread_context *log_json_thread;\n+static struct log_thread_context *log_thread;\n+static struct thread_context *g2_threads;\n+static struct thread_context *threads;\n+\n+static void term_handler(int sig)\n+{\n+\tint i;\n+\n+\tprintf(\"Stopping all application threads...\\n\");\n+\n+\tprint_stop = 1;\n+\n+\tif (log_mqtt_thread)\n+\t\tlog_mqtt_thread->stop = 1;\n+\n+\tif (log_json_thread)\n+\t\tlog_json_thread->stop = 1;\n+\n+\tif (log_thread)\n+\t\tlog_thread->stop = 1;\n+\n+\tif (g2_threads)\n+\t\tg2_threads->stop = 1;\n+\n+\tif (threads)\n+\t\tfor (i = 0; i < NUM_PN_THREAD_TYPES; i++)\n+\t\t\tthreads[i].stop = 1;\n+}\n+\n+static void reset_stats_handler(int sig)\n+{\n+\treset_stats = 1;\n+}\n+\n+static void setup_signals(void)\n+{\n+\tstruct sigaction sa1, sa2;\n+\n+\tsigemptyset(&sa1.sa_mask);\n+\tsa1.sa_handler = term_handler;\n+\tsa1.sa_flags = 0;\n+\n+\tsigemptyset(&sa2.sa_mask);\n+\tsa2.sa_handler = reset_stats_handler;\n+\tsa2.sa_flags = 0;\n+\n+\tif (sigaction(SIGTERM, &sa1, NULL))\n+\t\tperror(\"sigaction() failed\");\n+\tif (sigaction(SIGINT, &sa1, NULL))\n+\t\tperror(\"sigaction() failed\");\n+\tif (sigaction(SIGUSR1, &sa2, NULL))\n+\t\tperror(\"sigaction() failed\");\n+}\n+\n+static void print_usage_and_die(void)\n+{\n+\tfprintf(stderr, \"usage: mirror [options]\\n\");\n+\tfprintf(stderr, \"  options:\\n\");\n+\tfprintf(stderr, \"    -h, --help:    Print this help text\\n\");\n+\tfprintf(stderr, \"    -V, --version: Print version\\n\");\n+\tfprintf(stderr, \"    -c, --config:  Path to config file\\n\");\n+\n+\texit(EXIT_SUCCESS);\n+}\n+\n+static void print_version_and_die(void)\n+{\n+\tprintf(\"mirror: version \\\"%s\\\"\\n\", VERSION);\n+\texit(EXIT_SUCCESS);\n+}\n \n int main(int argc, char *argv[])\n {\n-\tstruct tb_startup_mode mode = {\n-\t\t.binary_name = \"mirror\",\n-\t\t.stat_mode = LOG_MIRROR,\n-\t\t.use_histogram = false,\n-\t\t.is_mirror = true,\n-\t};\n-\n-\ttb_startup(argc, argv, &mode);\n+\tbool enable_tx_latency_logging = false;\n+\tconst char *config_file = NULL;\n+\tint c, ret;\n+\n+\twhile ((c = getopt_long(argc, argv, \"c:hV\", long_options, NULL)) != -1) {\n+\t\tswitch (c) {\n+\t\tcase 'V':\n+\t\t\tprint_version_and_die();\n+\t\t\tbreak;\n+\t\tcase 'c':\n+\t\t\tconfig_file = optarg;\n+\t\t\tbreak;\n+\t\tcase 'h':\n+\t\tdefault:\n+\t\t\tprint_usage_and_die();\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * The \"mirror\" application only mirrors traffic and never generate frames itself. Make\n+\t * sure, the corresponding options are set.\n+\t *\n+\t * Note: The user cannot override this.\n+\t */\n+\tret = config_set_defaults(true);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to set default config values!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tif (!config_file) {\n+\t\tfprintf(stderr, \"Specifying an configuration file is mandatory. See tests/ \"\n+\t\t\t\t\"directory for examples!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tret = config_read_from_file(config_file);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to parse configuration file!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tconfig_print_values();\n+\n+\tif (!config_sanity_check()) {\n+\t\tfprintf(stderr, \"Configuration failed sanity checks!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tif (mlockall(MCL_CURRENT | MCL_FUTURE)) {\n+\t\tperror(\"mlockall() failed\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tconfigure_cpu_latency();\n+\n+\tsetup_signals();\n+\n+\tret = log_init();\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to initialize logging!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tfor (int i = 0; i < NUM_FRAME_TYPES; i++) {\n+\t\tif (app_config.classes[i].tx_hwtstamp_enabled) {\n+\t\t\tenable_tx_latency_logging = true;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (enable_tx_latency_logging) {\n+\t\tret = stat_init(LOG_TX_TIMESTAMPS);\n+\t} else {\n+\t\tret = stat_init(LOG_MIRROR);\n+\t}\n+\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to initialize statistics!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tlog_thread = log_thread_create();\n+\tif (!log_thread) {\n+\t\tfprintf(stderr, \"Failed to create and start Log Thread!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tlog_mqtt_thread = log_mqtt_thread_create();\n+\tif (!log_mqtt_thread && app_config.log_mqtt) {\n+\t\tfprintf(stderr, \"Failed to create and start Log via MQTT Thread!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tlog_json_thread = log_json_thread_create();\n+\tif (!log_json_thread && app_config.log_json) {\n+\t\tfprintf(stderr, \"Failed to create and start Log via JSON Thread!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tg2_threads = generic_l2_threads_create();\n+\tif (!g2_threads) {\n+\t\tfprintf(stderr, \"Failed to create and start Generic L2 Threads!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tthreads = calloc(NUM_PN_THREAD_TYPES, sizeof(struct thread_context));\n+\tif (!threads) {\n+\t\tfprintf(stderr, \"Failed to allocate PN threads!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tif (link_pn_threads(threads)) {\n+\t\tfprintf(stderr, \"Failed to determine PN traffic classes order!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tret = udp_low_threads_create(&threads[UDP_LOW_THREAD]);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to create and start UDP Low Threads!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tret = udp_high_threads_create(&threads[UDP_HIGH_THREAD]);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to create and start UDP High Threads!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tret = lldp_threads_create(&threads[LLDP_THREAD]);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to create and start LLDP Threads!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tret = dcp_threads_create(&threads[DCP_THREAD]);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to create and start DCP Threads!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tret = rta_threads_create(&threads[RTA_THREAD]);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to create and start RTA Threads!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tret = rtc_threads_create(&threads[RTC_THREAD]);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to create and start RTC Threads!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tret = tsn_low_threads_create(&threads[TSN_LOW_THREAD]);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to create and start TSN Low Threads!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tret = tsn_high_threads_create(&threads[TSN_HIGH_THREAD]);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to create and start TSN High Threads!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tprint_stats();\n+\n+\ttsn_high_threads_wait_for_finish(&threads[TSN_HIGH_THREAD]);\n+\ttsn_low_threads_wait_for_finish(&threads[TSN_LOW_THREAD]);\n+\trtc_threads_wait_for_finish(&threads[RTC_THREAD]);\n+\trta_threads_wait_for_finish(&threads[RTA_THREAD]);\n+\tdcp_threads_wait_for_finish(&threads[DCP_THREAD]);\n+\tlldp_threads_wait_for_finish(&threads[LLDP_THREAD]);\n+\tudp_high_threads_wait_for_finish(&threads[UDP_HIGH_THREAD]);\n+\tudp_low_threads_wait_for_finish(&threads[UDP_LOW_THREAD]);\n+\tgeneric_l2_threads_wait_for_finish(g2_threads);\n+\tlog_mqtt_thread_wait_for_finish(log_mqtt_thread);\n+\tlog_json_thread_wait_for_finish(log_json_thread);\n+\tlog_thread_wait_for_finish(log_thread);\n+\n+\ttsn_high_threads_free(&threads[TSN_HIGH_THREAD]);\n+\ttsn_low_threads_free(&threads[TSN_LOW_THREAD]);\n+\trtc_threads_free(&threads[RTC_THREAD]);\n+\trta_threads_free(&threads[RTA_THREAD]);\n+\tdcp_threads_free(&threads[DCP_THREAD]);\n+\tlldp_threads_free(&threads[LLDP_THREAD]);\n+\tudp_high_threads_free(&threads[UDP_HIGH_THREAD]);\n+\tudp_low_threads_free(&threads[UDP_LOW_THREAD]);\n+\tgeneric_l2_threads_free(g2_threads);\n+\tlog_mqtt_thread_free(log_mqtt_thread);\n+\tlog_json_thread_free(log_json_thread);\n+\tlog_thread_free(log_thread);\n+\n+\tstat_free();\n+\tlog_free();\n+\tconfig_free();\n+\tfree(threads);\n+\n+\trestore_cpu_latency();\n \n \treturn EXIT_SUCCESS;\n }\ndiff --git a/src/napictl.c b/src/napictl.c\ndeleted file mode 100644\nindex c10c71c..0000000\n--- a/src/napictl.c\n+++ /dev/null\n@@ -1,320 +0,0 @@\n-// SPDX-License-Identifier: BSD-2-Clause\n-/*\n- * Copyright (C) 2025 Linutronix GmbH\n- *\n- * Configure NAPI defer-hard-irqs and/or gro-flush-timeout via netlink genl API.\n- *\n- * Note: Requires recent kernel and driver support. Works for igc and igb.\n- */\n-#include <errno.h>\n-#include <getopt.h>\n-#include <limits.h>\n-#include <stdarg.h>\n-#include <stdbool.h>\n-#include <stdio.h>\n-#include <stdlib.h>\n-#include <string.h>\n-#include <unistd.h>\n-\n-#include <linux/netdev.h>\n-#include <net/if.h>\n-#include <netlink/genl/ctrl.h>\n-#include <netlink/genl/genl.h>\n-#include <netlink/netlink.h>\n-\n-#include \"app_config.h\"\n-\n-#define __unused __attribute__((unused))\n-\n-static struct option long_options[] = {\n-\t{\"interface\", optional_argument, NULL, 'i'},\n-\t{\"queue\", optional_argument, NULL, 'q'},\n-\t{\"defer-hard-irqs\", optional_argument, NULL, 'd'},\n-\t{\"gro-flush-timeout\", optional_argument, NULL, 'g'},\n-\t{\"help\", no_argument, NULL, 'h'},\n-\t{\"verbose\", no_argument, NULL, 'v'},\n-\t{\"version\", no_argument, NULL, 'V'},\n-\t{},\n-};\n-\n-static const char *interface;\n-static int verbose;\n-static unsigned queue;\n-static unsigned int defer_hard_irqs;\n-static unsigned int gro_flush_timeout;\n-\n-static void print_usage_and_die(void)\n-{\n-\tfprintf(stderr, \"usage: napictl [options]\\n\");\n-\tfprintf(stderr, \"  options:\\n\");\n-\tfprintf(stderr, \"    -i, --interface:         Network interface to configure\\n\");\n-\tfprintf(stderr, \"    -q, --queue:             Queue of network interface to configure\\n\");\n-\tfprintf(stderr, \"    -d, --defer-hard-irqs:   Set defer-hard-irqs\\n\");\n-\tfprintf(stderr, \"    -g, --gro-flush-timeout: Set gro-flush-timeout\\n\");\n-\tfprintf(stderr, \"    -h, --help:              Print this help text\\n\");\n-\tfprintf(stderr, \"    -v, --verbose:           Print verbose messages\\n\");\n-\tfprintf(stderr, \"    -V, --version:           Print version\\n\");\n-\n-\texit(EXIT_SUCCESS);\n-}\n-\n-static void print_version_and_die(void)\n-{\n-\tprintf(\"napictl: version \\\"%s\\\"\\n\", VERSION);\n-\texit(EXIT_SUCCESS);\n-}\n-\n-static void vprint(const char *format, ...)\n-{\n-\tva_list args;\n-\n-\tif (!verbose)\n-\t\treturn;\n-\n-\tva_start(args, format);\n-\tvprintf(format, args);\n-\tva_end(args);\n-}\n-\n-/* NAPI ID(s) for queue */\n-static unsigned int napi_id_tx;\n-static unsigned int napi_id_rx;\n-\n-static int parse_queue(struct nl_msg *msg, void __unused *arg)\n-{\n-\tbool q_found = false, n_found = false, t_found = false;\n-\tstruct nlattr *attrs[NETDEV_A_QUEUE_MAX + 1];\n-\tstruct nlmsghdr *nlh = nlmsg_hdr(msg);\n-\tunsigned int q, n, t;\n-\n-\tgenlmsg_parse(nlh, 0, attrs, NETDEV_A_QUEUE_MAX, NULL);\n-\n-\tif (attrs[NETDEV_A_QUEUE_ID]) {\n-\t\tq = nla_get_u32(attrs[NETDEV_A_QUEUE_ID]);\n-\t\tq_found = true;\n-\t\tvprint(\">> Queue ID: %u\\n\", q);\n-\t}\n-\tif (attrs[NETDEV_A_QUEUE_NAPI_ID]) {\n-\t\tn = nla_get_u32(attrs[NETDEV_A_QUEUE_NAPI_ID]);\n-\t\tn_found = true;\n-\t\tvprint(\">> Queue NAPI ID: %u\\n\", n);\n-\t}\n-\n-\tif (attrs[NETDEV_A_QUEUE_TYPE]) {\n-\t\tt = nla_get_u32(attrs[NETDEV_A_QUEUE_TYPE]);\n-\t\tt_found = true;\n-\t\tvprint(\">> Queue Type: %u\\n\", t);\n-\t}\n-\n-\tif (q_found && n_found && t_found && q == queue) {\n-\t\tif (t == NETDEV_QUEUE_TYPE_RX)\n-\t\t\tnapi_id_rx = n;\n-\t\tif (t == NETDEV_QUEUE_TYPE_TX)\n-\t\t\tnapi_id_tx = n;\n-\t}\n-\n-\treturn NL_OK;\n-}\n-\n-static int get_napi_id(int ifindex)\n-{\n-\tstruct nl_sock *sock;\n-\tstruct nl_msg *msg;\n-\tint family, ret;\n-\n-\tsock = nl_socket_alloc();\n-\tif (!sock) {\n-\t\tfprintf(stderr, \"nl_socket_alloc() failed: %m\\n\");\n-\t\treturn -errno;\n-\t}\n-\n-\tret = genl_connect(sock);\n-\tif (ret) {\n-\t\tfprintf(stderr, \"genl_connect() failed: %m\\n\");\n-\t\tgoto out;\n-\t}\n-\n-\tfamily = genl_ctrl_resolve(sock, NETDEV_FAMILY_NAME);\n-\tif (family < 0) {\n-\t\tfprintf(stderr, \"genl_ctrl_resolve() failed: %s\\n\", nl_geterror(family));\n-\t\tret = family;\n-\t\tgoto out;\n-\t}\n-\n-\tmsg = nlmsg_alloc();\n-\tif (!msg) {\n-\t\tfprintf(stderr, \"nlmsg_alloc() failed: %m\\n\");\n-\t\tret = -errno;\n-\t\tgoto out;\n-\t}\n-\n-\t/* NETDEV_CMD_QUEUE_GET */\n-\tgenlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, family, 0, NLM_F_DUMP, NETDEV_CMD_QUEUE_GET, 0);\n-\tnla_put_u32(msg, NETDEV_A_QUEUE_IFINDEX, ifindex);\n-\tnl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, parse_queue, NULL);\n-\n-\tret = nl_send_auto(sock, msg);\n-\tif (ret < 0) {\n-\t\tfprintf(stderr, \"nl_send_auto() failed: %s\\n\", nl_geterror(ret));\n-\t\tnlmsg_free(msg);\n-\t\tgoto out;\n-\t}\n-\tnlmsg_free(msg);\n-\n-\tvprint(\"> Query queues and NAPI ids\\n\");\n-\tret = nl_recvmsgs_default(sock);\n-\tif (ret < 0)\n-\t\tfprintf(stderr, \"nl_recvmsgs_default() failed: %s\\n\", nl_geterror(ret));\n-\n-out:\n-\tnl_socket_free(sock);\n-\treturn ret;\n-}\n-\n-static int set_napi_attributes(unsigned int napi_id)\n-{\n-\tstruct nl_sock *sock;\n-\tstruct nl_msg *msg;\n-\tint family, ret;\n-\n-\tsock = nl_socket_alloc();\n-\tif (!sock) {\n-\t\tfprintf(stderr, \"nl_socket_alloc() failed: %m\\n\");\n-\t\treturn -errno;\n-\t}\n-\n-\tret = genl_connect(sock);\n-\tif (ret) {\n-\t\tfprintf(stderr, \"genl_connect() failed: %m\\n\");\n-\t\tgoto out;\n-\t}\n-\n-\tfamily = genl_ctrl_resolve(sock, NETDEV_FAMILY_NAME);\n-\tif (family < 0) {\n-\t\tfprintf(stderr, \"genl_ctrl_resolve() failed: %s\\n\", nl_geterror(family));\n-\t\tret = family;\n-\t\tgoto out;\n-\t}\n-\n-\tmsg = nlmsg_alloc();\n-\tif (!msg) {\n-\t\tfprintf(stderr, \"nlmsg_alloc() failed: %m\\n\");\n-\t\tret = -errno;\n-\t\tgoto out;\n-\t}\n-\n-\t/* NETDEV_CMD_NAPI_SET */\n-\tgenlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, family, 0, NLM_F_REQUEST | NLM_F_ACK,\n-\t\t    NETDEV_CMD_NAPI_SET, 0);\n-\n-\tnla_put_u32(msg, NETDEV_A_NAPI_ID, napi_id);\n-\tnla_put_u32(msg, NETDEV_A_NAPI_DEFER_HARD_IRQS, defer_hard_irqs);\n-\tnla_put_u32(msg, NETDEV_A_NAPI_GRO_FLUSH_TIMEOUT, gro_flush_timeout);\n-\n-\tret = nl_send_auto(sock, msg);\n-\tif (ret < 0) {\n-\t\tfprintf(stderr, \"nl_send_auto() failed: %s\\n\", nl_geterror(ret));\n-\t\tnlmsg_free(msg);\n-\t\tgoto out;\n-\t}\n-\tnlmsg_free(msg);\n-\n-\tvprint(\"> Query queues and NAPI ids\\n\");\n-\tret = nl_recvmsgs_default(sock);\n-\tif (ret < 0)\n-\t\tfprintf(stderr, \"nl_recvmsgs_default() failed: %s\\n\", nl_geterror(ret));\n-\n-out:\n-\tnl_socket_free(sock);\n-\treturn ret;\n-}\n-\n-static int parse_uint(const char *value, unsigned int *ret)\n-{\n-\tunsigned long tmp;\n-\tchar *endptr;\n-\n-\terrno = 0;\n-\ttmp = strtoul(value, &endptr, 10);\n-\tif (errno != 0 || endptr == value || *endptr != '\\0')\n-\t\treturn -ERANGE;\n-\n-\tif (tmp > UINT_MAX)\n-\t\treturn -ERANGE;\n-\n-\t*ret = tmp;\n-\n-\treturn 0;\n-}\n-\n-int main(int argc, char **argv)\n-{\n-\tint c, ret, ifindex;\n-\n-\twhile ((c = getopt_long(argc, argv, \"i:q:d:g:hvV\", long_options, NULL)) != -1) {\n-\t\tswitch (c) {\n-\t\tcase 'V':\n-\t\t\tprint_version_and_die();\n-\t\t\tbreak;\n-\t\tcase 'v':\n-\t\t\tverbose = 1;\n-\t\t\tbreak;\n-\t\tcase 'i':\n-\t\t\tinterface = optarg;\n-\t\t\tbreak;\n-\t\tcase 'q':\n-\t\t\tif (parse_uint(optarg, &queue))\n-\t\t\t\tprint_usage_and_die();\n-\t\t\tbreak;\n-\t\tcase 'd':\n-\t\t\tif (parse_uint(optarg, &defer_hard_irqs))\n-\t\t\t\tprint_usage_and_die();\n-\t\t\tbreak;\n-\t\tcase 'g':\n-\t\t\tif (parse_uint(optarg, &gro_flush_timeout))\n-\t\t\t\tprint_usage_and_die();\n-\t\t\tbreak;\n-\t\tcase 'h':\n-\t\tdefault:\n-\t\t\tprint_usage_and_die();\n-\t\t}\n-\t}\n-\n-\tif (!interface)\n-\t\tprint_usage_and_die();\n-\n-\tifindex = if_nametoindex(interface);\n-\tif (!ifindex) {\n-\t\tfprintf(stderr, \"if_nametoindex() failed\\n\");\n-\t\treturn EXIT_FAILURE;\n-\t}\n-\n-\tret = get_napi_id(ifindex);\n-\tif (ret) {\n-\t\tfprintf(stderr, \"get_napi_id() failed\\n\");\n-\t\treturn EXIT_FAILURE;\n-\t}\n-\n-\tvprint(\"> Tx NAPI ID for queue %u: %u\\n\", queue, napi_id_tx);\n-\tvprint(\"> Rx NAPI ID for queue %u: %u\\n\", queue, napi_id_rx);\n-\n-\tvprint(\"> Set defer-hard-irqs %u and gro-flush-time %u to NAPI ID %u\\n\", defer_hard_irqs,\n-\t       gro_flush_timeout, napi_id_rx);\n-\n-\tret = set_napi_attributes(napi_id_rx);\n-\tif (ret) {\n-\t\tfprintf(stderr, \"set_napi_attributes() failed\\n\");\n-\t\treturn EXIT_FAILURE;\n-\t}\n-\n-\tvprint(\"> Set defer-hard-irqs %u and gro-flush-time %u to NAPI ID %u\\n\", defer_hard_irqs,\n-\t       gro_flush_timeout, napi_id_tx);\n-\n-\tret = set_napi_attributes(napi_id_tx);\n-\tif (ret) {\n-\t\tfprintf(stderr, \"set_napi_attributes() failed\\n\");\n-\t\treturn EXIT_FAILURE;\n-\t}\n-\n-\treturn EXIT_SUCCESS;\n-}\ndiff --git a/src/net.c b/src/net.c\nindex 408920b..bfe0e31 100644\n--- a/src/net.c\n+++ b/src/net.c\n@@ -1,6 +1,6 @@\n // SPDX-License-Identifier: BSD-2-Clause\n /*\n- * Copyright (C) 2020-2026 Linutronix GmbH\n+ * Copyright (C) 2020-2025 Linutronix GmbH\n  * Author Kurt Kanzenbach <kurt@linutronix.de>\n  */\n \n@@ -31,31 +31,94 @@\n #include \"utils.h\"\n \n /*\n- * Filter for Profinet frames:\n+ * Filter for Profinet TSN High Frames:\n  *   ldh [12]\n  *   jne #0x8892, drop\n  *   ld vlan_tci\n  *   jne #VlanTCI, drop\n  *   ldh [14]\n- *   jlt #frame_id_low, drop\n- *   jgt #frame_id_high, drop\n+ *   jlt #0x0100, drop\n+ *   jgt #0x01ff, drop\n  *   ret #-1\n  *   drop: ret #0\n  */\n-#define PROFINET_FRAME_FILTER(frame_id_low, frame_id_high)                                         \\\n-\t{                                                                                          \\\n-\t\t{0x28, 0, 0, 0x0000000c},      {0x15, 0, 6, 0x00008892},                           \\\n-\t\t{0x20, 0, 0, 0xfffff02c},      {0x15, 0, 4, 0x00001234},                           \\\n-\t\t{0x28, 0, 0, 0x0000000e},      {0x35, 0, 2, (frame_id_low)},                       \\\n-\t\t{0x25, 1, 0, (frame_id_high)}, {0x06, 0, 0, 0xffffffff},                           \\\n-\t\t{0x06, 0, 0, 0000000000},                                                          \\\n-\t}\n+static struct sock_filter tsn_high_frame_filter[] = {\n+\t{0x28, 0, 0, 0x0000000c}, {0x15, 0, 6, 0x00008892}, {0x20, 0, 0, 0xfffff02c},\n+\t{0x15, 0, 4, 0x00001234}, {0x28, 0, 0, 0x0000000e}, {0x35, 0, 2, 0x00000100},\n+\t{0x25, 1, 0, 0x000001ff}, {0x06, 0, 0, 0xffffffff}, {0x06, 0, 0, 0000000000},\n+};\n+\n+/*\n+ * Filter for Profinet TSN Low Frames:\n+ *   ldh [12]\n+ *   jne #0x8892, drop\n+ *   ld vlan_tci\n+ *   jne #VlanTCI, drop\n+ *   ldh [14]\n+ *   jlt #0x0200, drop\n+ *   jgt #0x03ff, drop\n+ *   ret #-1\n+ *   drop: ret #0\n+ */\n+static struct sock_filter tsn_low_frame_filter[] = {\n+\t{0x28, 0, 0, 0x0000000c}, {0x15, 0, 6, 0x00008892}, {0x20, 0, 0, 0xfffff02c},\n+\t{0x15, 0, 4, 0x00001234}, {0x28, 0, 0, 0x0000000e}, {0x35, 0, 2, 0x00000200},\n+\t{0x25, 1, 0, 0x000003ff}, {0x06, 0, 0, 0xffffffff}, {0x06, 0, 0, 0000000000},\n+};\n+\n+/*\n+ * Filter for Profinet RTC Frames:\n+ *   ldh [12]\n+ *   jne #0x8892, drop\n+ *   ld vlan_tci\n+ *   jne #VlanTCI, drop\n+ *   ldh [14]\n+ *   jlt #0x8000, drop\n+ *   jgt #0xbbff, drop\n+ *   ret #-1\n+ *   drop: ret #0\n+ */\n+static struct sock_filter rtc_frame_filter[] = {\n+\t{0x28, 0, 0, 0x0000000c}, {0x15, 0, 6, 0x00008892}, {0x20, 0, 0, 0xfffff02c},\n+\t{0x15, 0, 4, 0x00001234}, {0x28, 0, 0, 0x0000000e}, {0x35, 0, 2, 0x00008000},\n+\t{0x25, 1, 0, 0x0000bbff}, {0x06, 0, 0, 0xffffffff}, {0x06, 0, 0, 0000000000},\n+};\n+\n+/*\n+ * Filter for Profinet RTA Frames:\n+ *   ldh [12]\n+ *   jne #0x8892, drop\n+ *   ld vlan_tci\n+ *   jne #VlanTCI, drop\n+ *   ldh [14]\n+ *   jlt #0xfc01, drop\n+ *   jgt #0xfc02, drop\n+ *   ret #-1\n+ *   drop: ret #0\n+ */\n+static struct sock_filter rta_frame_filter[] = {\n+\t{0x28, 0, 0, 0x0000000c}, {0x15, 0, 6, 0x00008892}, {0x20, 0, 0, 0xfffff02c},\n+\t{0x15, 0, 4, 0x00001234}, {0x28, 0, 0, 0x0000000e}, {0x35, 0, 2, 0x0000fc01},\n+\t{0x25, 1, 0, 0x0000fc02}, {0x06, 0, 0, 0xffffffff}, {0x06, 0, 0, 0000000000},\n+};\n \n-static struct sock_filter tsn_high_frame_filter[] = PROFINET_FRAME_FILTER(0x0100, 0x01ff);\n-static struct sock_filter tsn_low_frame_filter[] = PROFINET_FRAME_FILTER(0x0200, 0x03ff);\n-static struct sock_filter rtc_frame_filter[] = PROFINET_FRAME_FILTER(0x8000, 0xbbff);\n-static struct sock_filter rta_frame_filter[] = PROFINET_FRAME_FILTER(0xfc01, 0xfc02);\n-static struct sock_filter dcp_frame_filter[] = PROFINET_FRAME_FILTER(0xfefe, 0xfeff);\n+/*\n+ * Filter for Profinet DCP Frames:\n+ *   ldh [12]\n+ *   jne #0x8892, drop\n+ *   ld vlan_tci\n+ *   jne #VlanTCI, drop\n+ *   ldh [14]\n+ *   jlt #0xfefe, drop\n+ *   jgt #0xfeff, drop\n+ *   ret #-1\n+ *   drop: ret #0\n+ */\n+static struct sock_filter dcp_frame_filter[] = {\n+\t{0x28, 0, 0, 0x0000000c}, {0x15, 0, 6, 0x00008892}, {0x20, 0, 0, 0xfffff02c},\n+\t{0x15, 0, 4, 0x00001234}, {0x28, 0, 0, 0x0000000e}, {0x35, 0, 2, 0x0000fefe},\n+\t{0x25, 1, 0, 0x0000feff}, {0x06, 0, 0, 0xffffffff}, {0x06, 0, 0, 0000000000},\n+};\n \n /*\n  * Filter for LLDP Frames:\n@@ -135,7 +198,7 @@ static int create_raw_socket(const char *if_name, int socket_priority)\n \n \tinterface = if_nametoindex(if_name);\n \tif (!interface) {\n-\t\tperror(\"if_nametoindex() failed\");\n+\t\tperror(\"ioctl() failed\");\n \t\tgoto err_index;\n \t}\n \n@@ -234,23 +297,26 @@ int get_interface_link_speed(const char *if_name, uint32_t *speed)\n \treturn 0;\n }\n \n-static int create_socket(enum stat_frame_type frame_type, struct sock_filter *filter,\n-\t\t\t size_t filter_len)\n+int create_tsn_high_socket(void)\n {\n-\tconst struct sock_fprog filter_program = {.len = filter_len, .filter = filter};\n+\tconst struct sock_fprog tsn_high_filter_program = {.len = ARRAY_SIZE(tsn_high_frame_filter),\n+\t\t\t\t\t\t\t   .filter = tsn_high_frame_filter};\n \tstruct sock_txtime sk_txtime;\n \tint socket_fd, ret;\n \n-\tsocket_fd = create_raw_socket(app_config.classes[frame_type].interface,\n-\t\t\t\t      app_config.classes[frame_type].socket_priority);\n+\tsocket_fd = create_raw_socket(app_config.classes[TSN_HIGH_FRAME_TYPE].interface,\n+\t\t\t\t      app_config.classes[TSN_HIGH_FRAME_TYPE].socket_priority);\n \tif (socket_fd < 0) {\n-\t\tfprintf(stderr, \"Failed to create RAW socket for %s Frames!\\n\",\n-\t\t\tstat_frame_type_to_string(frame_type));\n+\t\tfprintf(stderr, \"Failed to create RAW socket for Profinet TSN High Frames!\\n\");\n \t\treturn socket_fd;\n \t}\n \n-\tret = setsockopt(socket_fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_program,\n-\t\t\t sizeof(filter_program));\n+\t/* Adjust filter: VLAN TCI */\n+\ttsn_high_frame_filter[3].k = app_config.classes[TSN_HIGH_FRAME_TYPE].vid |\n+\t\t\t\t     app_config.classes[TSN_HIGH_FRAME_TYPE].pcp << VLAN_PCP_SHIFT;\n+\n+\tret = setsockopt(socket_fd, SOL_SOCKET, SO_ATTACH_FILTER, &tsn_high_filter_program,\n+\t\t\t sizeof(tsn_high_filter_program));\n \tif (ret < 0) {\n \t\tperror(\"setsockopt() failed\");\n \t\tgoto err_filter;\n@@ -259,13 +325,12 @@ static int create_socket(enum stat_frame_type frame_type, struct sock_filter *fi\n \t/* Mark socket as non-blocking */\n \tret = mark_socket_non_blocking(socket_fd);\n \tif (ret) {\n-\t\tfprintf(stderr, \"Failed to mark %s socket as non-blocking!\\n\",\n-\t\t\tstat_frame_type_to_string(frame_type));\n+\t\tfprintf(stderr, \"Failed to mark TSN High socket as non-blocking!\\n\");\n \t\tgoto err_filter;\n \t}\n \n \t/* Enable SO_TXTIME */\n-\tif (!app_config.classes[frame_type].tx_time_enabled)\n+\tif (!app_config.classes[TSN_HIGH_FRAME_TYPE].tx_time_enabled)\n \t\tgoto out;\n \n \tsk_txtime.clockid = CLOCK_TAI; /* For hardware offload CLOCK_TAI is mandatory */\n@@ -284,68 +349,258 @@ err_filter:\n \treturn -errno;\n }\n \n-int create_tsn_high_socket(void)\n+int create_tsn_low_socket(void)\n {\n-\t/* Adjust filter: VLAN TCI */\n-\ttsn_high_frame_filter[3].k = app_config.classes[TSN_HIGH_FRAME_TYPE].vid |\n-\t\t\t\t     app_config.classes[TSN_HIGH_FRAME_TYPE].pcp << VLAN_PCP_SHIFT;\n+\tconst struct sock_fprog tsn_low_filter_program = {.len = ARRAY_SIZE(tsn_low_frame_filter),\n+\t\t\t\t\t\t\t  .filter = tsn_low_frame_filter};\n+\tstruct sock_txtime sk_txtime;\n+\tint socket_fd, ret;\n \n-\treturn create_socket(TSN_HIGH_FRAME_TYPE, tsn_high_frame_filter,\n-\t\t\t     ARRAY_SIZE(tsn_high_frame_filter));\n-}\n+\tsocket_fd = create_raw_socket(app_config.classes[TSN_LOW_FRAME_TYPE].interface,\n+\t\t\t\t      app_config.classes[TSN_LOW_FRAME_TYPE].socket_priority);\n+\tif (socket_fd < 0) {\n+\t\tfprintf(stderr, \"Failed to create RAW socket for Profinet TSN Low Frames!\\n\");\n+\t\treturn socket_fd;\n+\t}\n \n-int create_tsn_low_socket(void)\n-{\n \t/* Adjust filter: VLAN TCI */\n \ttsn_low_frame_filter[3].k = app_config.classes[TSN_LOW_FRAME_TYPE].vid |\n \t\t\t\t    app_config.classes[TSN_LOW_FRAME_TYPE].pcp << VLAN_PCP_SHIFT;\n \n-\treturn create_socket(TSN_LOW_FRAME_TYPE, tsn_low_frame_filter,\n-\t\t\t     ARRAY_SIZE(tsn_low_frame_filter));\n+\tret = setsockopt(socket_fd, SOL_SOCKET, SO_ATTACH_FILTER, &tsn_low_filter_program,\n+\t\t\t sizeof(tsn_low_filter_program));\n+\tif (ret < 0) {\n+\t\tperror(\"setsockopt() failed\");\n+\t\tgoto err_filter;\n+\t}\n+\n+\t/* Mark socket as non-blocking */\n+\tret = mark_socket_non_blocking(socket_fd);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to mark TSN Low socket as non-blocking!\\n\");\n+\t\tgoto err_filter;\n+\t}\n+\n+\t/* Enable SO_TXTIME */\n+\tif (!app_config.classes[TSN_LOW_FRAME_TYPE].tx_time_enabled)\n+\t\tgoto out;\n+\n+\tsk_txtime.clockid = CLOCK_TAI; /* For hardware offload CLOCK_TAI is mandatory */\n+\tsk_txtime.flags = 1 << 1;      /* Enable error reporting */\n+\tret = setsockopt(socket_fd, SOL_SOCKET, SO_TXTIME, &sk_txtime, sizeof(sk_txtime));\n+\tif (ret) {\n+\t\tperror(\"setsockopt() failed\");\n+\t\tgoto err_filter;\n+\t}\n+\n+out:\n+\treturn socket_fd;\n+\n+err_filter:\n+\tclose(socket_fd);\n+\treturn -errno;\n }\n \n int create_rtc_socket(void)\n {\n+\tconst struct sock_fprog rtc_filter_program = {.len = ARRAY_SIZE(rtc_frame_filter),\n+\t\t\t\t\t\t      .filter = rtc_frame_filter};\n+\tint socket_fd, ret;\n+\n+\tsocket_fd = create_raw_socket(app_config.classes[RTC_FRAME_TYPE].interface,\n+\t\t\t\t      app_config.classes[RTC_FRAME_TYPE].socket_priority);\n+\tif (socket_fd < 0) {\n+\t\tfprintf(stderr, \"Failed to create RAW socket for Profinet RTC Frames!\\n\");\n+\t\treturn socket_fd;\n+\t}\n+\n \t/* Adjust filter: VLAN TCI */\n \trtc_frame_filter[3].k = app_config.classes[RTC_FRAME_TYPE].vid |\n \t\t\t\tapp_config.classes[RTC_FRAME_TYPE].pcp << VLAN_PCP_SHIFT;\n \n-\treturn create_socket(RTC_FRAME_TYPE, rtc_frame_filter, ARRAY_SIZE(rtc_frame_filter));\n+\tret = setsockopt(socket_fd, SOL_SOCKET, SO_ATTACH_FILTER, &rtc_filter_program,\n+\t\t\t sizeof(rtc_filter_program));\n+\tif (ret < 0) {\n+\t\tperror(\"setsockopt() failed\");\n+\t\tgoto err_filter;\n+\t}\n+\n+\t/* Mark socket as non-blocking */\n+\tret = mark_socket_non_blocking(socket_fd);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to mark Rtc socket as non-blocking!\\n\");\n+\t\tgoto err_filter;\n+\t}\n+\n+\treturn socket_fd;\n+\n+err_filter:\n+\tclose(socket_fd);\n+\treturn -errno;\n }\n \n int create_rta_socket(void)\n {\n+\tconst struct sock_fprog rta_filter_program = {.len = ARRAY_SIZE(rta_frame_filter),\n+\t\t\t\t\t\t      .filter = rta_frame_filter};\n+\tint socket_fd, ret;\n+\n+\tsocket_fd = create_raw_socket(app_config.classes[RTA_FRAME_TYPE].interface,\n+\t\t\t\t      app_config.classes[RTA_FRAME_TYPE].socket_priority);\n+\tif (socket_fd < 0) {\n+\t\tfprintf(stderr, \"Failed to create RAW socket for Profinet RTA Frames!\\n\");\n+\t\treturn socket_fd;\n+\t}\n+\n \t/* Adjust filter: VLAN TCI */\n \trta_frame_filter[3].k = app_config.classes[RTA_FRAME_TYPE].vid |\n \t\t\t\tapp_config.classes[RTA_FRAME_TYPE].pcp << VLAN_PCP_SHIFT;\n \n-\treturn create_socket(RTA_FRAME_TYPE, rta_frame_filter, ARRAY_SIZE(rta_frame_filter));\n+\tret = setsockopt(socket_fd, SOL_SOCKET, SO_ATTACH_FILTER, &rta_filter_program,\n+\t\t\t sizeof(rta_filter_program));\n+\tif (ret < 0) {\n+\t\tperror(\"setsockopt() failed\");\n+\t\tgoto err_filter;\n+\t}\n+\n+\t/* Mark socket as non-blocking */\n+\tret = mark_socket_non_blocking(socket_fd);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to mark TSN High socket as non-blocking!\\n\");\n+\t\tgoto err_filter;\n+\t}\n+\n+\treturn socket_fd;\n+\n+err_filter:\n+\tclose(socket_fd);\n+\treturn -errno;\n }\n \n int create_dcp_socket(void)\n {\n+\tconst struct sock_fprog dcp_filter_program = {.len = ARRAY_SIZE(dcp_frame_filter),\n+\t\t\t\t\t\t      .filter = dcp_frame_filter};\n+\tint socket_fd, ret;\n+\n+\tsocket_fd = create_raw_socket(app_config.classes[DCP_FRAME_TYPE].interface,\n+\t\t\t\t      app_config.classes[DCP_FRAME_TYPE].socket_priority);\n+\tif (socket_fd < 0) {\n+\t\tfprintf(stderr, \"Failed to create RAW socket for Profinet DCP Frames!\\n\");\n+\t\treturn socket_fd;\n+\t}\n+\n \t/* Adjust filter: VLAN TCI */\n \tdcp_frame_filter[3].k = app_config.classes[DCP_FRAME_TYPE].vid |\n \t\t\t\tapp_config.classes[DCP_FRAME_TYPE].pcp << VLAN_PCP_SHIFT;\n \n-\treturn create_socket(DCP_FRAME_TYPE, dcp_frame_filter, ARRAY_SIZE(dcp_frame_filter));\n+\tret = setsockopt(socket_fd, SOL_SOCKET, SO_ATTACH_FILTER, &dcp_filter_program,\n+\t\t\t sizeof(dcp_filter_program));\n+\tif (ret < 0) {\n+\t\tperror(\"setsockopt() failed\");\n+\t\tgoto err_filter;\n+\t}\n+\n+\t/* Mark socket as non-blocking */\n+\tret = mark_socket_non_blocking(socket_fd);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to mark Dcp socket as non-blocking!\\n\");\n+\t\tgoto err_filter;\n+\t}\n+\n+\treturn socket_fd;\n+\n+err_filter:\n+\tclose(socket_fd);\n+\treturn -errno;\n }\n \n int create_lldp_socket(void)\n {\n-\treturn create_socket(LLDP_FRAME_TYPE, lldp_frame_filter, ARRAY_SIZE(lldp_frame_filter));\n+\tconst struct sock_fprog lldp_filter_program = {.len = ARRAY_SIZE(lldp_frame_filter),\n+\t\t\t\t\t\t       .filter = lldp_frame_filter};\n+\tint socket_fd, ret;\n+\n+\tsocket_fd = create_raw_socket(app_config.classes[LLDP_FRAME_TYPE].interface,\n+\t\t\t\t      app_config.classes[LLDP_FRAME_TYPE].socket_priority);\n+\tif (socket_fd < 0) {\n+\t\tfprintf(stderr, \"Failed to create RAW socket for LLDP Frames!\\n\");\n+\t\treturn socket_fd;\n+\t}\n+\n+\tret = setsockopt(socket_fd, SOL_SOCKET, SO_ATTACH_FILTER, &lldp_filter_program,\n+\t\t\t sizeof(lldp_filter_program));\n+\tif (ret < 0) {\n+\t\tperror(\"setsockopt() failed\");\n+\t\tgoto err_filter;\n+\t}\n+\n+\t/* Mark socket as non-blocking */\n+\tret = mark_socket_non_blocking(socket_fd);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to mark Lldp socket as non-blocking!\\n\");\n+\t\tgoto err_filter;\n+\t}\n+\n+\treturn socket_fd;\n+\n+err_filter:\n+\tclose(socket_fd);\n+\treturn -errno;\n }\n \n int create_generic_l2_socket(void)\n {\n+\tconst struct sock_fprog generic_l2_filter_program = {\n+\t\t.len = ARRAY_SIZE(generic_l2_frame_filter), .filter = generic_l2_frame_filter};\n+\tstruct sock_txtime sk_txtime;\n+\tint socket_fd, ret;\n+\n+\tsocket_fd = create_raw_socket(app_config.classes[GENERICL2_FRAME_TYPE].interface,\n+\t\t\t\t      app_config.classes[GENERICL2_FRAME_TYPE].socket_priority);\n+\tif (socket_fd < 0) {\n+\t\tfprintf(stderr, \"Failed to create RAW socket for Generic L2 Frames!\\n\");\n+\t\treturn socket_fd;\n+\t}\n+\n \t/* Adjust filter: EtherType and VLAN TCI */\n \tgeneric_l2_frame_filter[1].k = app_config.classes[GENERICL2_FRAME_TYPE].ether_type;\n \tgeneric_l2_frame_filter[3].k = app_config.classes[GENERICL2_FRAME_TYPE].vid |\n \t\t\t\t       app_config.classes[GENERICL2_FRAME_TYPE].pcp\n \t\t\t\t\t       << VLAN_PCP_SHIFT;\n \n-\treturn create_socket(GENERICL2_FRAME_TYPE, generic_l2_frame_filter,\n-\t\t\t     ARRAY_SIZE(generic_l2_frame_filter));\n+\tret = setsockopt(socket_fd, SOL_SOCKET, SO_ATTACH_FILTER, &generic_l2_filter_program,\n+\t\t\t sizeof(generic_l2_filter_program));\n+\tif (ret < 0) {\n+\t\tperror(\"setsockopt() failed\");\n+\t\tgoto err_filter;\n+\t}\n+\n+\t/* Mark socket as non-blocking */\n+\tret = mark_socket_non_blocking(socket_fd);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to mark Generic L2 socket as non-blocking!\\n\");\n+\t\tgoto err_filter;\n+\t}\n+\n+\t/* Enable SO_TXTIME */\n+\tif (!app_config.classes[GENERICL2_FRAME_TYPE].tx_time_enabled)\n+\t\tgoto out;\n+\n+\tsk_txtime.clockid = CLOCK_TAI; /* For hardware offload CLOCK_TAI is mandatory */\n+\tsk_txtime.flags = 1 << 1;      /* Enable error reporting */\n+\tret = setsockopt(socket_fd, SOL_SOCKET, SO_TXTIME, &sk_txtime, sizeof(sk_txtime));\n+\tif (ret) {\n+\t\tperror(\"setsockopt() failed\");\n+\t\tgoto err_filter;\n+\t}\n+\n+out:\n+\treturn socket_fd;\n+\n+err_filter:\n+\tclose(socket_fd);\n+\treturn -errno;\n }\n \n static int dns_lookup(const char *host, const char *port, struct sockaddr_storage *addr,\ndiff --git a/src/packet.c b/src/packet.c\nindex 366bb80..4ae1775 100644\n--- a/src/packet.c\n+++ b/src/packet.c\n@@ -193,13 +193,14 @@ int packet_receive_messages(struct packet_context *context, struct packet_receiv\n \n \t\tlen = recvmmsg(recv_req->socket_fd, msgs, context->num_frames_per_cycle, 0, NULL);\n \t\tif (len == -1) {\n-\t\t\t/* No more frames. Come back within next period. */\n-\t\t\tif (errno == EAGAIN || errno == EWOULDBLOCK)\n+\t\t\tif (errno != EAGAIN && errno != EWOULDBLOCK) {\n+\t\t\t\tlog_message(LOG_LEVEL_ERROR, \"%sRx: recvmmsg() failed: %s\\n\",\n+\t\t\t\t\t    recv_req->traffic_class, strerror(errno));\n+\t\t\t\tcontinue;\n+\t\t\t} else {\n+\t\t\t\t/* No more frames. Comeback within next period. */\n \t\t\t\tbreak;\n-\n-\t\t\tlog_message(LOG_LEVEL_ERROR, \"%sRx: recvmmsg() failed: %s\\n\",\n-\t\t\t\t    recv_req->traffic_class, strerror(errno));\n-\t\t\tbreak;\n+\t\t\t}\n \t\t}\n \n \t\t/* Process received frames. */\ndiff --git a/src/print.c b/src/print.c\nindex 135bd04..919e759 100644\n--- a/src/print.c\n+++ b/src/print.c\n@@ -22,14 +22,16 @@ void print_stats(void)\n \t\tstat_get_global_stats(global_statistics, sizeof(global_statistics));\n \n \t\tfor (int i = 0; i < NUM_FRAME_TYPES; i++) {\n+\t\t\tconst char *name = i == GENERICL2_FRAME_TYPE\n+\t\t\t\t\t\t   ? app_config.classes[GENERICL2_FRAME_TYPE].name\n+\t\t\t\t\t\t   : stat_frame_type_to_string(i);\n \t\t\tconst struct statistics *stat = &global_statistics[i];\n-\t\t\tconst char *name = stat_frame_type_to_string(i);\n \t\t\tuint64_t errors;\n \n \t\t\terrors = stat->frame_id_errors + stat->out_of_order_errors +\n \t\t\t\t stat->payload_errors;\n \n-\t\t\tif (config_is_traffic_class_active(i)) {\n+\t\t\tif (config_is_traffic_class_active(stat_frame_type_to_string(i))) {\n \t\t\t\tprintf(\"%-8s: Tx:%10\" PRIu64 \" Rx:%10\" PRIu64\n \t\t\t\t       \" RttMin[us]:%10\" PRIu64\n \t\t\t\t       \" RttAvg[us]:%10lf RttMax[us]:%10\" PRIu64 \" Err:%8\" PRIu64\ndiff --git a/src/profinet.c b/src/profinet.c\ndeleted file mode 100644\nindex 0e61589..0000000\n--- a/src/profinet.c\n+++ /dev/null\n@@ -1,370 +0,0 @@\n-// SPDX-License-Identifier: BSD-2-Clause\n-/*\n- * Copyright (C) 2020-2026 Linutronix GmbH\n- */\n-\n-#include <pthread.h>\n-#include <string.h>\n-\n-#include <arpa/inet.h>\n-\n-#include \"log.h\"\n-#include \"net_def.h\"\n-#include \"ring_buffer.h\"\n-#include \"thread.h\"\n-#include \"utils.h\"\n-#include \"xdp.h\"\n-\n-static void initialize_secure_profinet_frame(enum security_mode mode, unsigned char *frame_data,\n-\t\t\t\t\t     size_t frame_length, const unsigned char *source,\n-\t\t\t\t\t     const unsigned char *destination,\n-\t\t\t\t\t     const char *payload_pattern,\n-\t\t\t\t\t     size_t payload_pattern_length, uint16_t vlan_tci,\n-\t\t\t\t\t     uint16_t frame_id)\n-{\n-\tstruct profinet_secure_header *rt;\n-\tstruct vlan_ethernet_header *eth;\n-\tuint16_t security_length;\n-\tsize_t payload_offset;\n-\n-\t/* Initialize to zero */\n-\tmemset(frame_data, '\\0', frame_length);\n-\n-\t/*\n-\t * Profinet Frame:\n-\t *   Destination\n-\t *   Source\n-\t *   VLAN tag: tpid 8100, id 0x00/0x101/102, dei 0, prio 6/5/4/3/2\n-\t *   Ether type: 8892\n-\t *   Frame id: TSN, RTC, RTA, DCP\n-\t *   SecurityHeader\n-\t *   Cycle counter\n-\t *   Payload\n-\t *   Padding to maxFrame - Checksum\n-\t *   security_checksum\n-\t */\n-\n-\teth = (struct vlan_ethernet_header *)frame_data;\n-\trt = (struct profinet_secure_header *)(frame_data + sizeof(*eth));\n-\n-\t/* Ethernet header */\n-\tmemcpy(eth->destination, destination, ETH_ALEN);\n-\tmemcpy(eth->source, source, ETH_ALEN);\n-\n-\t/* VLAN Header */\n-\teth->vlan_proto = htons(ETH_P_8021Q);\n-\teth->vlantci = htons(vlan_tci);\n-\teth->vlan_encapsulated_proto = htons(ETH_P_PROFINET_RT);\n-\n-\t/* Profinet Secure header */\n-\tsecurity_length = frame_length - sizeof(*eth) - sizeof(struct security_checksum);\n-\trt->frame_id = htons(frame_id);\n-\trt->security_meta_data.security_information = mode == SECURITY_MODE_AO ? 0x00 : 0x01;\n-\trt->security_meta_data.security_length = htobe16(security_length);\n-\trt->meta_data.frame_counter = 0;\n-\trt->meta_data.cycle_counter = 0;\n-\n-\t/* Payload */\n-\tpayload_offset = sizeof(*eth) + sizeof(*rt);\n-\tmemcpy(frame_data + payload_offset, payload_pattern, payload_pattern_length);\n-\n-\t/* security_checksum is set to zero and calculated for each frame on Tx */\n-}\n-\n-static void initialize_rt_profinet_frame(unsigned char *frame_data, size_t frame_length,\n-\t\t\t\t\t const unsigned char *source,\n-\t\t\t\t\t const unsigned char *destination,\n-\t\t\t\t\t const char *payload_pattern, size_t payload_pattern_length,\n-\t\t\t\t\t uint16_t vlan_tci, uint16_t frame_id)\n-{\n-\tstruct vlan_ethernet_header *eth;\n-\tstruct profinet_rt_header *rt;\n-\tsize_t payload_offset;\n-\n-\t/* Initialize to zero */\n-\tmemset(frame_data, '\\0', frame_length);\n-\n-\t/*\n-\t * Profinet Frame:\n-\t *   Destination\n-\t *   Source\n-\t *   VLAN tag: tpid 8100, id 0x00/0x101/102, dei 0, prio 6/5/4/3/2\n-\t *   Ether type: 8892\n-\t *   Frame id: TSN, RTC, RTA, DCP\n-\t *   Cycle counter\n-\t *   Payload\n-\t *   Padding to maxFrame\n-\t */\n-\n-\teth = (struct vlan_ethernet_header *)frame_data;\n-\trt = (struct profinet_rt_header *)(frame_data + sizeof(*eth));\n-\n-\t/* Ethernet header */\n-\tmemcpy(eth->destination, destination, ETH_ALEN);\n-\tmemcpy(eth->source, source, ETH_ALEN);\n-\n-\t/* VLAN Header */\n-\teth->vlan_proto = htons(ETH_P_8021Q);\n-\teth->vlantci = htons(vlan_tci);\n-\teth->vlan_encapsulated_proto = htons(ETH_P_PROFINET_RT);\n-\n-\t/* Profinet RT header */\n-\trt->frame_id = htons(frame_id);\n-\trt->meta_data.frame_counter = 0;\n-\trt->meta_data.cycle_counter = 0;\n-\n-\t/* Payload */\n-\tpayload_offset = sizeof(*eth) + sizeof(*rt);\n-\tmemcpy(frame_data + payload_offset, payload_pattern, payload_pattern_length);\n-}\n-\n-void initialize_profinet_frame(enum security_mode mode, unsigned char *frame_data,\n-\t\t\t       size_t frame_length, const unsigned char *source,\n-\t\t\t       const unsigned char *destination, const char *payload_pattern,\n-\t\t\t       size_t payload_pattern_length, uint16_t vlan_tci, uint16_t frame_id)\n-{\n-\tswitch (mode) {\n-\tcase SECURITY_MODE_NONE:\n-\t\tinitialize_rt_profinet_frame(frame_data, frame_length, source, destination,\n-\t\t\t\t\t     payload_pattern, payload_pattern_length, vlan_tci,\n-\t\t\t\t\t     frame_id);\n-\t\tbreak;\n-\tcase SECURITY_MODE_AE:\n-\tcase SECURITY_MODE_AO:\n-\t\tinitialize_secure_profinet_frame(mode, frame_data, frame_length, source,\n-\t\t\t\t\t\t destination, payload_pattern,\n-\t\t\t\t\t\t payload_pattern_length, vlan_tci, frame_id);\n-\t\tbreak;\n-\t}\n-}\n-\n-int receive_profinet_frame(void *data, unsigned char *frame_data, size_t len)\n-{\n-\tstruct thread_context *thread_context = data;\n-\tconst struct traffic_class_config *class_config = thread_context->conf;\n-\tconst unsigned char *expected_pattern =\n-\t\t(const unsigned char *)class_config->payload_pattern;\n-\tstruct security_context *security_context = thread_context->rx_security_context;\n-\tconst size_t expected_pattern_length = class_config->payload_pattern_length;\n-\tconst bool mirror_enabled = class_config->rx_mirror_enabled;\n-\tconst bool ignore_rx_errors = class_config->ignore_rx_errors;\n-\tsize_t expected_frame_length = class_config->frame_length;\n-\tuint64_t tx_timestamp, rx_hw_timestamp, rx_sw_timestamp;\n-\tbool out_of_order, payload_mismatch, frame_id_mismatch;\n-\tunsigned char plaintext[MAX_FRAME_SIZE];\n-\tunsigned char new_frame[MAX_FRAME_SIZE];\n-\tstruct profinet_secure_header *srt;\n-\tstruct profinet_rt_header *rt;\n-\tuint64_t sequence_counter;\n-\tbool vlan_tag_missing;\n-\tvoid *p = frame_data;\n-\tstruct ethhdr *eth;\n-\tuint16_t frame_id;\n-\tuint16_t proto;\n-\n-\tif (len < sizeof(struct vlan_ethernet_header)) {\n-\t\tlog_message(LOG_LEVEL_WARNING, \"%sRx: Too small frame received!\\n\",\n-\t\t\t    thread_context->traffic_class);\n-\t\treturn -EINVAL;\n-\t}\n-\n-\teth = p;\n-\tif (eth->h_proto == htons(ETH_P_8021Q)) {\n-\t\tstruct vlan_ethernet_header *veth = p;\n-\n-\t\tproto = veth->vlan_encapsulated_proto;\n-\t\tp += sizeof(*veth);\n-\t\tvlan_tag_missing = false;\n-\t} else {\n-\t\tproto = eth->h_proto;\n-\t\tp += sizeof(*eth);\n-\t\texpected_frame_length -= sizeof(struct vlan_header);\n-\t\tvlan_tag_missing = true;\n-\t}\n-\n-\tif (proto != htons(ETH_P_PROFINET_RT)) {\n-\t\tlog_message(LOG_LEVEL_WARNING, \"%sRx: Not a Profinet frame received!\\n\",\n-\t\t\t    thread_context->traffic_class);\n-\t\treturn -EINVAL;\n-\t}\n-\n-\t/* Check frame length: VLAN tag might be stripped or not. Check it. */\n-\tif (len != expected_frame_length) {\n-\t\tlog_message(LOG_LEVEL_WARNING, \"%sRx: Frame with wrong length received!\\n\",\n-\t\t\t    thread_context->traffic_class);\n-\t\treturn -EINVAL;\n-\t}\n-\n-\t/* Check cycle counter, frame id range and payload. */\n-\tif (class_config->security_mode == SECURITY_MODE_NONE) {\n-\t\trt = p;\n-\t\tp += sizeof(*rt);\n-\n-\t\tframe_id = be16toh(rt->frame_id);\n-\t\tsequence_counter = meta_data_to_sequence_counter(\n-\t\t\t&rt->meta_data, class_config->num_frames_per_cycle);\n-\n-\t\ttx_timestamp = meta_data_to_tx_timestamp(&rt->meta_data);\n-\t\tset_mirror_tx_timestamp_est(&rt->meta_data);\n-\n-\t} else if (class_config->security_mode == SECURITY_MODE_AO) {\n-\n-\t\tunsigned char *begin_of_security_checksum;\n-\t\tunsigned char *begin_of_aad_data;\n-\t\tsize_t size_of_eth_header;\n-\t\tsize_t size_of_aad_data;\n-\t\tstruct security_iv iv;\n-\t\tint ret;\n-\n-\t\tsrt = p;\n-\t\tp += sizeof(*srt);\n-\n-\t\tframe_id = be16toh(srt->frame_id);\n-\t\tsequence_counter = meta_data_to_sequence_counter(\n-\t\t\t&srt->meta_data, class_config->num_frames_per_cycle);\n-\n-\t\ttx_timestamp = meta_data_to_tx_timestamp(&srt->meta_data);\n-\n-\t\t/* Authenticate received Profinet Frame */\n-\t\tsize_of_eth_header = vlan_tag_missing ? sizeof(struct ethhdr)\n-\t\t\t\t\t\t      : sizeof(struct vlan_ethernet_header);\n-\n-\t\tbegin_of_aad_data = frame_data + size_of_eth_header;\n-\t\tsize_of_aad_data = len - size_of_eth_header - sizeof(struct security_checksum);\n-\t\tbegin_of_security_checksum = frame_data + (len - sizeof(struct security_checksum));\n-\n-\t\tprepare_iv((const unsigned char *)class_config->security_iv_prefix,\n-\t\t\t   sequence_counter, &iv);\n-\n-\t\tret = security_decrypt(security_context, NULL, 0, begin_of_aad_data,\n-\t\t\t\t       size_of_aad_data, begin_of_security_checksum,\n-\t\t\t\t       (unsigned char *)&iv, NULL);\n-\t\tif (ret)\n-\t\t\tlog_message(LOG_LEVEL_WARNING,\n-\t\t\t\t    \"%sRx: frame[%\" PRIu64 \"] Not authentificated\\n\",\n-\t\t\t\t    thread_context->traffic_class, sequence_counter);\n-\n-\t\tset_mirror_tx_timestamp_est(&srt->meta_data);\n-\n-\t\tsecurity_encrypt(security_context, NULL, 0, begin_of_aad_data, size_of_aad_data,\n-\t\t\t\t (unsigned char *)&iv, NULL, begin_of_security_checksum);\n-\n-\t} else {\n-\t\tunsigned char *begin_of_security_checksum;\n-\t\tunsigned char *begin_of_ciphertext;\n-\t\tunsigned char *begin_of_aad_data;\n-\t\tsize_t size_of_ciphertext;\n-\t\tsize_t size_of_eth_header;\n-\t\tsize_t size_of_aad_data;\n-\t\tstruct security_iv iv;\n-\t\tint ret;\n-\n-\t\tsrt = p;\n-\n-\t\tframe_id = be16toh(srt->frame_id);\n-\t\tsequence_counter = meta_data_to_sequence_counter(\n-\t\t\t&srt->meta_data, class_config->num_frames_per_cycle);\n-\n-\t\ttx_timestamp = meta_data_to_tx_timestamp(&srt->meta_data);\n-\n-\t\t/* Authenticate received Profinet Frame */\n-\t\tsize_of_eth_header = vlan_tag_missing ? sizeof(struct ethhdr)\n-\t\t\t\t\t\t      : sizeof(struct vlan_ethernet_header);\n-\n-\t\tbegin_of_aad_data = frame_data + size_of_eth_header;\n-\t\tsize_of_aad_data = sizeof(*srt);\n-\t\tbegin_of_security_checksum = frame_data + (len - sizeof(struct security_checksum));\n-\t\tbegin_of_ciphertext = frame_data + size_of_eth_header + sizeof(*srt);\n-\t\tsize_of_ciphertext = len - sizeof(struct vlan_ethernet_header) -\n-\t\t\t\t     sizeof(struct profinet_secure_header) -\n-\t\t\t\t     sizeof(struct security_checksum);\n-\n-\t\tprepare_iv((const unsigned char *)class_config->security_iv_prefix,\n-\t\t\t   sequence_counter, &iv);\n-\n-\t\tret = security_decrypt(security_context, begin_of_ciphertext, size_of_ciphertext,\n-\t\t\t\t       begin_of_aad_data, size_of_aad_data,\n-\t\t\t\t       begin_of_security_checksum, (unsigned char *)&iv, plaintext);\n-\t\tif (ret)\n-\t\t\tlog_message(LOG_LEVEL_WARNING,\n-\t\t\t\t    \"%sRx: frame[%\" PRIu64 \"] Not authentificated and decrypted\\n\",\n-\t\t\t\t    thread_context->traffic_class, sequence_counter);\n-\n-\t\t/* plaintext points to the decrypted payload */\n-\t\tp = plaintext;\n-\n-\t\tset_mirror_tx_timestamp_est(&srt->meta_data);\n-\n-\t\tsecurity_encrypt(security_context, thread_context->payload_pattern,\n-\t\t\t\t thread_context->payload_pattern_length, begin_of_aad_data,\n-\t\t\t\t size_of_aad_data, (unsigned char *)&iv, begin_of_ciphertext,\n-\t\t\t\t begin_of_security_checksum);\n-\t}\n-\n-\txdp_get_timestamp_metadata(frame_data, &rx_hw_timestamp, &rx_sw_timestamp);\n-\tout_of_order = sequence_counter != thread_context->rx_sequence_counter;\n-\tpayload_mismatch = memcmp(p, expected_pattern, expected_pattern_length);\n-\tframe_id_mismatch = frame_id != thread_context->frame_id;\n-\n-\tstat_frame_received(thread_context->frame_type, sequence_counter, out_of_order,\n-\t\t\t    payload_mismatch, frame_id_mismatch, tx_timestamp, rx_hw_timestamp,\n-\t\t\t    rx_sw_timestamp);\n-\n-\tif (frame_id_mismatch)\n-\t\tlog_message(\n-\t\t\tLOG_LEVEL_WARNING, \"%sRx: frame[%\" PRIu64 \"] FrameId mismatch: 0x%4x!\\n\",\n-\t\t\tthread_context->traffic_class, sequence_counter, thread_context->frame_id);\n-\n-\tif (out_of_order) {\n-\t\tif (!ignore_rx_errors)\n-\t\t\tlog_message(LOG_LEVEL_WARNING,\n-\t\t\t\t    \"%sRx: frame[%\" PRIu64 \"] SequenceCounter mismatch: %\" PRIu64\n-\t\t\t\t    \"!\\n\",\n-\t\t\t\t    thread_context->traffic_class, sequence_counter,\n-\t\t\t\t    thread_context->rx_sequence_counter);\n-\t\tthread_context->rx_sequence_counter++;\n-\t}\n-\n-\tif (payload_mismatch)\n-\t\tlog_message(LOG_LEVEL_WARNING,\n-\t\t\t    \"%sRx: frame[%\" PRIu64 \"] Payload Pattern mismatch!\\n\",\n-\t\t\t    thread_context->traffic_class, sequence_counter);\n-\n-\tthread_context->rx_sequence_counter++;\n-\n-\t/*\n-\t * If mirror enabled, assemble and store the frame for Tx later.\n-\t *\n-\t * In case of XDP the Rx umem area will be reused for Tx.\n-\t */\n-\tif (!mirror_enabled)\n-\t\treturn 0;\n-\n-\tif (class_config->xdp_enabled) {\n-\t\t/* Re-add vlan tag */\n-\t\tif (vlan_tag_missing)\n-\t\t\tinsert_vlan_tag(frame_data, len, ETH_P_PROFINET_RT,\n-\t\t\t\t\tclass_config->vid | class_config->pcp << VLAN_PCP_SHIFT);\n-\n-\t\t/* Swap mac addresses inline */\n-\t\tswap_mac_addresses(frame_data, len);\n-\t} else {\n-\t\t/* Build new frame for Tx with VLAN info. */\n-\t\tbuild_vlan_frame_from_rx(frame_data, len, new_frame, sizeof(new_frame),\n-\t\t\t\t\t ETH_P_PROFINET_RT,\n-\t\t\t\t\t class_config->vid | class_config->pcp << VLAN_PCP_SHIFT);\n-\n-\t\t/* Store the new frame. */\n-\t\tring_buffer_add(thread_context->mirror_buffer, new_frame,\n-\t\t\t\tlen + sizeof(struct vlan_header));\n-\t}\n-\n-\t/* RTA is a burst traffic class and needs to update num_frames_available */\n-\tif (thread_context->frame_type == RTA_FRAME_TYPE) {\n-\t\tpthread_mutex_lock(&thread_context->data_mutex);\n-\t\tthread_context->num_frames_available++;\n-\t\tpthread_mutex_unlock(&thread_context->data_mutex);\n-\t}\n-\n-\treturn 0;\n-}\ndiff --git a/src/profinet.h b/src/profinet.h\ndeleted file mode 100644\nindex e53c623..0000000\n--- a/src/profinet.h\n+++ /dev/null\n@@ -1,25 +0,0 @@\n-/* SPDX-License-Identifier: BSD-2-Clause */\n-/*\n- * Copyright (C) 2020-2026 Linutronix GmbH\n- */\n-\n-#include \"security.h\"\n-\n-/*\n- * This function initializes an PROFINET Ethernet frame. The Ethernet header, PROFINET header and\n- * payload is initialized. The sequenceCounter is set to zero.\n- *\n- * In case the SecurityMode is AE or AO, the PROFINET Ethernet frames will contain the\n- * SecurityHeader after the FrameID.\n- */\n-void initialize_profinet_frame(enum security_mode mode, unsigned char *frame_data,\n-\t\t\t       size_t frame_length, const unsigned char *source,\n-\t\t\t       const unsigned char *destination, const char *payload_pattern,\n-\t\t\t       size_t payload_pattern_length, uint16_t vlan_tci, uint16_t frame_id);\n-\n-/*\n- * This function receives a Profinet frame. It performs all required tests such as checking sequence\n- * counters, payload, checksums, etc. This is used for TSN, RTC and RTA as well as by packet and xdp\n- * code.\n- */\n-int receive_profinet_frame(void *data, unsigned char *frame_data, size_t len);\ndiff --git a/src/reference.c b/src/reference.c\nindex 8721d52..40e7291 100644\n--- a/src/reference.c\n+++ b/src/reference.c\n@@ -1,24 +1,305 @@\n // SPDX-License-Identifier: BSD-2-Clause\n /*\n- * Copyright (C) 2020-2026 Linutronix GmbH\n+ * Copyright (C) 2020-2025 Linutronix GmbH\n  * Author Kurt Kanzenbach <kurt@linutronix.de>\n  */\n \n-#include <stdbool.h>\n+#include <getopt.h>\n+#include <signal.h>\n+#include <stdio.h>\n #include <stdlib.h>\n \n-#include \"tb.h\"\n+#include <sys/mman.h>\n+\n+#include \"app_config.h\"\n+#include \"config.h\"\n+#include \"dcp_thread.h\"\n+#include \"hist.h\"\n+#include \"layer2_thread.h\"\n+#include \"lldp_thread.h\"\n+#include \"log.h\"\n+#include \"log_json.h\"\n+#include \"log_mqtt.h\"\n+#include \"print.h\"\n+#include \"rta_thread.h\"\n+#include \"rtc_thread.h\"\n+#include \"stat.h\"\n+#include \"thread.h\"\n+#include \"tsn_thread.h\"\n+#include \"udp_thread.h\"\n+#include \"utils.h\"\n+\n+static struct option long_options[] = {\n+\t{\"config\", optional_argument, NULL, 'c'},\n+\t{\"help\", no_argument, NULL, 'h'},\n+\t{\"version\", no_argument, NULL, 'V'},\n+\t{NULL},\n+};\n+\n+static struct log_mqtt_thread_context *log_mqtt_thread;\n+static struct log_json_thread_context *log_json_thread;\n+static struct log_thread_context *log_thread;\n+static struct thread_context *g2_threads;\n+static struct thread_context *threads;\n+\n+static void term_handler(int sig)\n+{\n+\tint i;\n+\n+\tprintf(\"Stopping all application threads...\\n\");\n+\n+\tprint_stop = 1;\n+\n+\tif (log_mqtt_thread)\n+\t\tlog_mqtt_thread->stop = 1;\n+\n+\tif (log_json_thread)\n+\t\tlog_json_thread->stop = 1;\n+\n+\tif (log_thread)\n+\t\tlog_thread->stop = 1;\n+\n+\tif (g2_threads)\n+\t\tg2_threads->stop = 1;\n+\n+\tif (threads)\n+\t\tfor (i = 0; i < NUM_PN_THREAD_TYPES; i++)\n+\t\t\tthreads[i].stop = 1;\n+}\n+\n+static void reset_stats_handler(int sig)\n+{\n+\treset_stats = 1;\n+}\n+\n+static void setup_signals(void)\n+{\n+\tstruct sigaction sa1, sa2;\n+\n+\tsigemptyset(&sa1.sa_mask);\n+\tsa1.sa_handler = term_handler;\n+\tsa1.sa_flags = 0;\n+\n+\tsigemptyset(&sa2.sa_mask);\n+\tsa2.sa_handler = reset_stats_handler;\n+\tsa2.sa_flags = 0;\n+\n+\tif (sigaction(SIGTERM, &sa1, NULL))\n+\t\tperror(\"sigaction() failed\");\n+\tif (sigaction(SIGINT, &sa1, NULL))\n+\t\tperror(\"sigaction() failed\");\n+\tif (sigaction(SIGUSR1, &sa2, NULL))\n+\t\tperror(\"sigaction() failed\");\n+}\n+\n+static void print_usage_and_die(void)\n+{\n+\tfprintf(stderr, \"usage: reference [options]\\n\");\n+\tfprintf(stderr, \"  options:\\n\");\n+\tfprintf(stderr, \"    -h, --help:    Print this help text\\n\");\n+\tfprintf(stderr, \"    -V, --version: Print version\\n\");\n+\tfprintf(stderr, \"    -c, --config:  Path to config file\\n\");\n+\n+\texit(EXIT_SUCCESS);\n+}\n+\n+static void print_version_and_die(void)\n+{\n+\tprintf(\"reference: version \\\"%s\\\"\\n\", VERSION);\n+\texit(EXIT_SUCCESS);\n+}\n \n int main(int argc, char *argv[])\n {\n-\tstruct tb_startup_mode mode = {\n-\t\t.binary_name = \"reference\",\n-\t\t.stat_mode = LOG_REFERENCE,\n-\t\t.use_histogram = true,\n-\t\t.is_mirror = false,\n-\t};\n-\n-\ttb_startup(argc, argv, &mode);\n+\tconst char *config_file = NULL;\n+\tint c, ret;\n+\n+\twhile ((c = getopt_long(argc, argv, \"c:hV\", long_options, NULL)) != -1) {\n+\t\tswitch (c) {\n+\t\tcase 'V':\n+\t\t\tprint_version_and_die();\n+\t\t\tbreak;\n+\t\tcase 'c':\n+\t\t\tconfig_file = optarg;\n+\t\t\tbreak;\n+\t\tcase 'h':\n+\t\tdefault:\n+\t\t\tprint_usage_and_die();\n+\t\t}\n+\t}\n+\n+\tret = config_set_defaults(false);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to set default config values!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tif (!config_file) {\n+\t\tfprintf(stderr, \"Specifying an configuration file is mandatory. See tests/ \"\n+\t\t\t\t\"directory for examples!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tret = config_read_from_file(config_file);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to parse configuration file!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tconfig_print_values();\n+\n+\tif (!config_sanity_check()) {\n+\t\tfprintf(stderr, \"Configuration failed sanity checks!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tif (mlockall(MCL_CURRENT | MCL_FUTURE)) {\n+\t\tperror(\"mlockall() failed\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tconfigure_cpu_latency();\n+\n+\tsetup_signals();\n+\n+\tret = log_init();\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to initialize logging!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tret = histogram_init();\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to initialize histogram code!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tret = stat_init(LOG_REFERENCE);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to initialize statistics!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tlog_thread = log_thread_create();\n+\tif (!log_thread) {\n+\t\tfprintf(stderr, \"Failed to create and start Log Thread!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tlog_mqtt_thread = log_mqtt_thread_create();\n+\tif (!log_mqtt_thread && app_config.log_mqtt) {\n+\t\tfprintf(stderr, \"Failed to create and start Log via MQTT Thread!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tlog_json_thread = log_json_thread_create();\n+\tif (!log_json_thread && app_config.log_json) {\n+\t\tfprintf(stderr, \"Failed to create and start Log via JSON Thread!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tg2_threads = generic_l2_threads_create();\n+\tif (!g2_threads) {\n+\t\tfprintf(stderr, \"Failed to create and start Generic L2 Threads!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tthreads = calloc(NUM_PN_THREAD_TYPES, sizeof(struct thread_context));\n+\tif (!threads) {\n+\t\tfprintf(stderr, \"Failed to allocate PN threads!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tif (link_pn_threads(threads)) {\n+\t\tfprintf(stderr, \"Failed to determine PN traffic classes order!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tret = udp_low_threads_create(&threads[UDP_LOW_THREAD]);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to create and start UDP Low Threads!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tret = udp_high_threads_create(&threads[UDP_HIGH_THREAD]);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to create and start UDP High Threads!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tret = lldp_threads_create(&threads[LLDP_THREAD]);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to create and start LLDP Threads!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tret = dcp_threads_create(&threads[DCP_THREAD]);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to create and start DCP Threads!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tret = rta_threads_create(&threads[RTA_THREAD]);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to create and start RTA Threads!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tret = rtc_threads_create(&threads[RTC_THREAD]);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to create and start RTC Threads!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tret = tsn_low_threads_create(&threads[TSN_LOW_THREAD]);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to create and start TSN Low Threads!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tret = tsn_high_threads_create(&threads[TSN_HIGH_THREAD]);\n+\tif (ret) {\n+\t\tfprintf(stderr, \"Failed to create and start TSN High Threads!\\n\");\n+\t\texit(EXIT_FAILURE);\n+\t}\n+\n+\tprint_stats();\n+\n+\ttsn_high_threads_wait_for_finish(&threads[TSN_HIGH_THREAD]);\n+\ttsn_low_threads_wait_for_finish(&threads[TSN_LOW_THREAD]);\n+\trtc_threads_wait_for_finish(&threads[RTC_THREAD]);\n+\trta_threads_wait_for_finish(&threads[RTA_THREAD]);\n+\tdcp_threads_wait_for_finish(&threads[DCP_THREAD]);\n+\tlldp_threads_wait_for_finish(&threads[LLDP_THREAD]);\n+\tudp_high_threads_wait_for_finish(&threads[UDP_HIGH_THREAD]);\n+\tudp_low_threads_wait_for_finish(&threads[UDP_LOW_THREAD]);\n+\tgeneric_l2_threads_wait_for_finish(g2_threads);\n+\tlog_mqtt_thread_wait_for_finish(log_mqtt_thread);\n+\tlog_json_thread_wait_for_finish(log_json_thread);\n+\tlog_thread_wait_for_finish(log_thread);\n+\n+\thistogram_write();\n+\n+\ttsn_high_threads_free(&threads[TSN_HIGH_THREAD]);\n+\ttsn_low_threads_free(&threads[TSN_LOW_THREAD]);\n+\trtc_threads_free(&threads[RTC_THREAD]);\n+\trta_threads_free(&threads[RTA_THREAD]);\n+\tdcp_threads_free(&threads[DCP_THREAD]);\n+\tlldp_threads_free(&threads[LLDP_THREAD]);\n+\tudp_high_threads_free(&threads[UDP_HIGH_THREAD]);\n+\tudp_low_threads_free(&threads[UDP_LOW_THREAD]);\n+\tgeneric_l2_threads_free(g2_threads);\n+\tlog_mqtt_thread_free(log_mqtt_thread);\n+\tlog_json_thread_free(log_json_thread);\n+\tlog_thread_free(log_thread);\n+\n+\thistogram_free();\n+\tstat_free();\n+\tlog_free();\n+\tconfig_free();\n+\tfree(threads);\n+\n+\trestore_cpu_latency();\n \n \treturn EXIT_SUCCESS;\n }\ndiff --git a/src/ring_buffer.c b/src/ring_buffer.c\nindex 2ea02b3..f4c5965 100644\n--- a/src/ring_buffer.c\n+++ b/src/ring_buffer.c\n@@ -1,6 +1,6 @@\n // SPDX-License-Identifier: BSD-2-Clause\n /*\n- * Copyright (C) 2020-2026 Linutronix GmbH\n+ * Copyright (C) 2020-2024 Linutronix GmbH\n  * Author Kurt Kanzenbach <kurt@linutronix.de>\n  */\n \n@@ -12,119 +12,126 @@\n #include \"ring_buffer.h\"\n #include \"thread.h\"\n \n-struct ring_buffer *ring_buffer_allocate(size_t size)\n+struct ring_buffer *ring_buffer_allocate(size_t buffer_size)\n {\n-\tstruct ring_buffer *rb;\n+\tstruct ring_buffer *ring_buffer;\n \n-\trb = calloc(1, sizeof(*rb));\n-\tif (!rb)\n+\tring_buffer = calloc(1, sizeof(*ring_buffer));\n+\tif (!ring_buffer)\n \t\treturn NULL;\n \n-\trb->data = calloc(size, sizeof(unsigned char));\n-\tif (!rb->data) {\n-\t\tfree(rb);\n+\tring_buffer->data = calloc(buffer_size, sizeof(char));\n+\tif (!ring_buffer->data) {\n+\t\tfree(ring_buffer);\n \t\treturn NULL;\n \t}\n \n-\trb->size = size;\n-\trb->count = 0;\n-\trb->wp = rb->data;\n-\trb->rp = rb->data;\n+\tring_buffer->buffer_size = buffer_size;\n+\tring_buffer->buffer_write_pointer = ring_buffer->data;\n+\tring_buffer->buffer_read_pointer = ring_buffer->data;\n \n-\tinit_mutex(&rb->mutex);\n+\tinit_mutex(&ring_buffer->data_mutex);\n \n-\treturn rb;\n+\treturn ring_buffer;\n }\n \n-void ring_buffer_free(struct ring_buffer *rb)\n+void ring_buffer_free(struct ring_buffer *ring_buffer)\n {\n-\tif (!rb)\n+\tif (!ring_buffer)\n \t\treturn;\n \n-\tfree(rb->data);\n-\tfree(rb);\n+\tfree(ring_buffer->data);\n+\tfree(ring_buffer);\n }\n \n-void ring_buffer_add(struct ring_buffer *rb, const unsigned char *data, size_t len)\n+void ring_buffer_add(struct ring_buffer *ring_buffer, const unsigned char *data, size_t len)\n {\n \tsize_t available;\n \n-\tif (!rb)\n+\tif (!ring_buffer)\n \t\treturn;\n \n-\tif (len > rb->size)\n+\tif (len > ring_buffer->buffer_size)\n \t\treturn;\n \n-\tpthread_mutex_lock(&rb->mutex);\n+\tpthread_mutex_lock(&ring_buffer->data_mutex);\n \n \t/* Wrap? */\n-\tavailable = (rb->data + rb->size) - rb->wp;\n+\tavailable =\n+\t\t(ring_buffer->data + ring_buffer->buffer_size) - ring_buffer->buffer_write_pointer;\n \tif (len <= available) {\n-\t\tmemcpy(rb->wp, data, len);\n-\t\trb->wp += len;\n+\t\tmemcpy(ring_buffer->buffer_write_pointer, data, len);\n+\t\tring_buffer->buffer_write_pointer += len;\n \t} else {\n-\t\tmemcpy(rb->wp, data, available);\n-\t\trb->wp = rb->data;\n-\t\tmemcpy(rb->wp, data + available, len - available);\n-\t\trb->wp += len - available;\n+\t\tmemcpy(ring_buffer->buffer_write_pointer, data, available);\n+\t\tlen -= available;\n+\t\tdata += available;\n+\t\tring_buffer->buffer_write_pointer = ring_buffer->data;\n+\t\tmemcpy(ring_buffer->buffer_write_pointer, data, len);\n+\t\tring_buffer->buffer_write_pointer += len;\n \t}\n \n-\tif ((rb->data + rb->size) == rb->wp)\n-\t\trb->wp = rb->data;\n+\tif ((ring_buffer->data + ring_buffer->buffer_size) == ring_buffer->buffer_write_pointer)\n+\t\tring_buffer->buffer_write_pointer = ring_buffer->data;\n \n-\t/* Advance read pointer in case of overflow. Oldest data get's dropped first. */\n-\tif (rb->count + len > rb->size) {\n-\t\trb->rp = rb->wp;\n-\t\trb->count = rb->size;\n-\t} else {\n-\t\trb->count += len;\n-\t}\n-\n-\tpthread_mutex_unlock(&rb->mutex);\n+\tpthread_mutex_unlock(&ring_buffer->data_mutex);\n }\n \n-void ring_buffer_fetch(struct ring_buffer *rb, unsigned char *data, size_t len, size_t *out_len)\n+void ring_buffer_fetch(struct ring_buffer *ring_buffer, unsigned char *data, size_t len,\n+\t\t       size_t *out_len)\n {\n-\tsize_t available, real_len;\n-\n-\tif (!rb) {\n-\t\t*out_len = 0;\n-\t\treturn;\n-\t}\n+\tintptr_t available;\n+\tsize_t real_len;\n \n-\tif (len > rb->size) {\n-\t\t*out_len = 0;\n+\tif (!ring_buffer)\n \t\treturn;\n-\t}\n-\n-\tpthread_mutex_lock(&rb->mutex);\n \n-\tif (rb->count == 0) {\n-\t\t*out_len = 0;\n-\t\tpthread_mutex_unlock(&rb->mutex);\n+\tif (len > ring_buffer->buffer_size)\n \t\treturn;\n-\t}\n \n-\treal_len = len < rb->count ? len : rb->count;\n-\tavailable = (rb->data + rb->size) - rb->rp;\n-\n-\tif (real_len <= available) {\n-\t\t/* Simple case: Copy in direction towards end */\n-\t\tmemcpy(data, rb->rp, real_len);\n-\t\trb->rp += real_len;\n+\tpthread_mutex_lock(&ring_buffer->data_mutex);\n+\n+\tavailable = ring_buffer->buffer_write_pointer - ring_buffer->buffer_read_pointer;\n+\n+\t/* Simple case: Copy difference between read and write ptr. */\n+\tif (available > 0) {\n+\t\treal_len = available > len ? len : available;\n+\t\tmemcpy(data, ring_buffer->buffer_read_pointer, real_len);\n+\t\t*out_len = real_len;\n+\t\tring_buffer->buffer_read_pointer += real_len;\n+\t} else if (available < 0) {\n+\t\t/* Copy first part */\n+\t\tavailable = (ring_buffer->data + ring_buffer->buffer_size) -\n+\t\t\t    ring_buffer->buffer_read_pointer;\n+\t\treal_len = available > len ? len : available;\n+\t\tmemcpy(data, ring_buffer->buffer_read_pointer, real_len);\n+\n+\t\tlen -= real_len;\n+\t\tdata += real_len;\n+\t\t*out_len = real_len;\n+\t\tring_buffer->buffer_read_pointer += real_len;\n+\n+\t\tif (ring_buffer->buffer_read_pointer ==\n+\t\t    (ring_buffer->data + ring_buffer->buffer_size))\n+\t\t\tring_buffer->buffer_read_pointer = ring_buffer->data;\n+\n+\t\t/* Copy second part */\n+\t\tif (len > 0) {\n+\t\t\tavailable = ring_buffer->buffer_write_pointer -\n+\t\t\t\t    ring_buffer->buffer_read_pointer;\n+\t\t\treal_len = available > len ? len : available;\n+\n+\t\t\tmemcpy(data, ring_buffer->buffer_read_pointer, real_len);\n+\n+\t\t\tring_buffer->buffer_read_pointer += real_len;\n+\t\t\t*out_len += real_len;\n+\t\t}\n \t} else {\n-\t\t/* Wrap case: Copy first and second part */\n-\t\tmemcpy(data, rb->rp, available);\n-\t\tmemcpy(data + available, rb->data, real_len - available);\n-\n-\t\trb->rp = rb->data + (real_len - available);\n+\t\t*out_len = 0;\n \t}\n \n-\tif (rb->rp == (rb->data + rb->size))\n-\t\trb->rp = rb->data;\n-\n-\trb->count -= real_len;\n-\t*out_len = real_len;\n+\tif (ring_buffer->buffer_read_pointer == (ring_buffer->data + ring_buffer->buffer_size))\n+\t\tring_buffer->buffer_read_pointer = ring_buffer->data;\n \n-\tpthread_mutex_unlock(&rb->mutex);\n+\tpthread_mutex_unlock(&ring_buffer->data_mutex);\n }\ndiff --git a/src/ring_buffer.h b/src/ring_buffer.h\nindex bd057e6..bb38877 100644\n--- a/src/ring_buffer.h\n+++ b/src/ring_buffer.h\n@@ -1,6 +1,6 @@\n /* SPDX-License-Identifier: BSD-2-Clause */\n /*\n- * Copyright (C) 2020-2026 Linutronix GmbH\n+ * Copyright (C) 2020 Linutronix GmbH\n  * Author Kurt Kanzenbach <kurt@linutronix.de>\n  */\n \n@@ -15,27 +15,19 @@\n  *  b) Logging\n  *\n  * Introduce a \"generic\" ring buffer suitable for both scenarios.\n- *\n- * The policy is drop oldest data first and keep the newest. The functions will not block. Old data\n- * is overwritten without any warning.\n- *\n- * The locking is done with PI mutex. This is fine for the Ethernet frame data, because there's no\n- * lock contention (Tx and Rx run at different time slots). However, in case of logging lots of\n- * messages, there might be contention. FIXME: To get rid of this, implement per traffic class\n- * logging.\n  */\n struct ring_buffer {\n-\tsize_t size;\n-\tsize_t count;\n-\tunsigned char *wp;\n-\tunsigned char *rp;\n+\tsize_t buffer_size;\n+\tunsigned char *buffer_write_pointer;\n+\tunsigned char *buffer_read_pointer;\n \tunsigned char *data;\n-\tpthread_mutex_t mutex;\n+\tpthread_mutex_t data_mutex;\n };\n \n-struct ring_buffer *ring_buffer_allocate(size_t size);\n-void ring_buffer_add(struct ring_buffer *rb, const unsigned char *data, size_t len);\n-void ring_buffer_fetch(struct ring_buffer *rb, unsigned char *data, size_t len, size_t *out_len);\n-void ring_buffer_free(struct ring_buffer *rb);\n+struct ring_buffer *ring_buffer_allocate(size_t buffer_size);\n+void ring_buffer_add(struct ring_buffer *ring_buffer, const unsigned char *data, size_t len);\n+void ring_buffer_fetch(struct ring_buffer *ring_buffer, unsigned char *data, size_t len,\n+\t\t       size_t *out_len);\n+void ring_buffer_free(struct ring_buffer *ring_buffer);\n \n #endif /* _RING_BUFFER_H_ */\ndiff --git a/src/rta_thread.c b/src/rta_thread.c\nindex 179d86d..d2efabc 100644\n--- a/src/rta_thread.c\n+++ b/src/rta_thread.c\n@@ -1,6 +1,6 @@\n // SPDX-License-Identifier: BSD-2-Clause\n /*\n- * Copyright (C) 2020-2026 Linutronix GmbH\n+ * Copyright (C) 2020-2025 Linutronix GmbH\n  * Author Kurt Kanzenbach <kurt@linutronix.de>\n  */\n \n@@ -24,7 +24,6 @@\n #include \"net.h\"\n #include \"net_def.h\"\n #include \"packet.h\"\n-#include \"profinet.h\"\n #include \"rta_thread.h\"\n #include \"security.h\"\n #include \"stat.h\"\n@@ -89,10 +88,6 @@ static int rta_send_frames(struct thread_context *thread_context, unsigned char\n \tconst struct traffic_class_config *rta_config = thread_context->conf;\n \tint len, i;\n \n-\t/* Adjust meta data */\n-\tset_mirror_tx_timestamp(rta_config, frame_data, rta_config->frame_length, num_frames,\n-\t\t\t\tthread_context->meta_data_offset);\n-\n \t/* Send it */\n \tlen = rta_send_messages(thread_context, socket_fd, destination, frame_data, num_frames);\n \n@@ -116,7 +111,7 @@ static int rta_gen_and_send_frames(struct thread_context *thread_context, int so\n \tstruct timespec tx_time = {};\n \tint len, i;\n \n-\tapp_clock_get(&tx_time);\n+\tclock_gettime(app_config.application_clock_id, &tx_time);\n \n \tfor (i = 0; i < rta_config->num_frames_per_cycle; i++) {\n \t\tstruct prepare_frame_config frame_config;\n@@ -143,8 +138,8 @@ static int rta_gen_and_send_frames(struct thread_context *thread_context, int so\n \tlen = rta_send_messages(thread_context, socket_fd, destination,\n \t\t\t\tthread_context->tx_frame_data, rta_config->num_frames_per_cycle);\n \n-\tif (len > 0)\n-\t\tstat_frames_sent_batch(RTA_FRAME_TYPE, sequence_counter_begin, len);\n+\tfor (i = 0; i < len; i++)\n+\t\tstat_frame_sent(RTA_FRAME_TYPE, sequence_counter_begin + i);\n \n \treturn len;\n }\n@@ -212,8 +207,13 @@ static void *rta_tx_thread_routine(void *data)\n \t\t\t      rta_config->num_frames_per_cycle, source, rta_config->l2_destination);\n \n \tprepare_openssl(security_context);\n-\trta_initialize_frames(thread_context, thread_context->frame_copy, 1, source,\n+\trta_initialize_frames(thread_context, thread_context->payload_pattern, 1, source,\n \t\t\t      rta_config->l2_destination);\n+\tthread_context->payload_pattern +=\n+\t\tsizeof(struct vlan_ethernet_header) + sizeof(struct profinet_secure_header);\n+\tthread_context->payload_pattern_length =\n+\t\trta_config->frame_length - sizeof(struct vlan_ethernet_header) -\n+\t\tsizeof(struct profinet_secure_header) - sizeof(struct security_checksum);\n \n \twhile (!thread_context->stop) {\n \t\tstruct timespec timeout;\n@@ -301,7 +301,6 @@ static void *rta_xdp_tx_thread_routine(void *data)\n \txsk = thread_context->xsk;\n \txsk->tx_hwts.rtt = &round_trip_contexts[RTA_FRAME_TYPE];\n \txsk->tx_hwts.frames_per_cycle = rta_config->num_frames_per_cycle;\n-\txsk->tx_hwts.meta_data_offset = thread_context->meta_data_offset;\n \n \tret = get_interface_mac_address(rta_config->interface, source, ETH_ALEN);\n \tif (ret < 0) {\n@@ -318,8 +317,13 @@ static void *rta_xdp_tx_thread_routine(void *data)\n \t\t\t      rta_config->l2_destination);\n \n \tprepare_openssl(security_context);\n-\trta_initialize_frames(thread_context, thread_context->frame_copy, 1, source,\n+\trta_initialize_frames(thread_context, thread_context->payload_pattern, 1, source,\n \t\t\t      rta_config->l2_destination);\n+\tthread_context->payload_pattern +=\n+\t\tsizeof(struct vlan_ethernet_header) + sizeof(struct profinet_secure_header);\n+\tthread_context->payload_pattern_length =\n+\t\trta_config->frame_length - sizeof(struct vlan_ethernet_header) -\n+\t\tsizeof(struct profinet_secure_header) - sizeof(struct security_checksum);\n \n \twhile (!thread_context->stop) {\n \t\tstruct timespec timeout;\n@@ -354,6 +358,7 @@ static void *rta_xdp_tx_thread_routine(void *data)\n \t\t\t}\n \t\t} else {\n \t\t\tunsigned int received;\n+\t\t\tuint64_t i;\n \n \t\t\tpthread_mutex_lock(&thread_context->xdp_data_mutex);\n \n@@ -369,13 +374,37 @@ static void *rta_xdp_tx_thread_routine(void *data)\n \n \t\t\txsk_ring_prod__submit(&xsk->tx, received);\n \n-\t\t\tif (received > 0)\n-\t\t\t\tstat_frames_sent_batch(RTA_FRAME_TYPE, sequence_counter, received);\n+\t\t\tif (received > 0) {\n+\t\t\t\tif (rta_config->tx_hwtstamp_enabled) {\n+\t\t\t\t\t/*\n+\t\t\t\t\t * Once-per-cycle: record TX SW timestamp for the first\n+\t\t\t\t\t * packet in this cycle and count all frames\n+\t\t\t\t\t */\n+\t\t\t\t\tstat_frames_sent_batch(RTA_FRAME_TYPE, sequence_counter,\n+\t\t\t\t\t\t\t       received);\n+\t\t\t\t} else {\n+\t\t\t\t\tfor (i = sequence_counter; i < sequence_counter + received;\n+\t\t\t\t\t     ++i)\n+\t\t\t\t\t\tstat_frame_sent(RTA_FRAME_TYPE, i);\n+\t\t\t\t}\n+\t\t\t}\n \n \t\t\txsk->outstanding_tx += received;\n \t\t\tthread_context->received_frames = 0;\n \t\t\txdp_complete_tx(xsk);\n \n+#ifdef TX_TIMESTAMP\n+\t\t\tif (received > 0 && rta_config->tx_hwtstamp_enabled) {\n+\t\t\t\t/*\n+\t\t\t\t * TX HW timestamp becomes available in the next cycle\n+\t\t\t\t * after the packet is transmitted.\n+\t\t\t\t * This is one cycle later than SW timestamp tracked\n+\t\t\t\t * using sequence_counter\n+\t\t\t\t */\n+\t\t\t\txsk->tx_hwts.seq_lagged = sequence_counter;\n+\t\t\t}\n+#endif\n+\n \t\t\tpthread_mutex_unlock(&thread_context->xdp_data_mutex);\n \t\t}\n \n@@ -539,7 +568,7 @@ int rta_threads_create(struct thread_context *thread_context)\n \tstruct traffic_class_config *rta_config;\n \tint ret;\n \n-\tif (!config_is_traffic_class_active(RTA_FRAME_TYPE))\n+\tif (!config_is_traffic_class_active(\"Rta\"))\n \t\tgoto out;\n \n \tinit_mutex(&thread_context->data_mutex);\n@@ -578,20 +607,13 @@ int rta_threads_create(struct thread_context *thread_context)\n \t\t}\n \t}\n \n-\t/* Initialize data structures for AE */\n-\tthread_context->frame_copy = calloc(1, MAX_FRAME_SIZE);\n-\tif (!thread_context->frame_copy) {\n-\t\tfprintf(stderr, \"Failed to allocate RtaPayloadPattern for AE!\\n\");\n+\tthread_context->payload_pattern = calloc(1, MAX_FRAME_SIZE);\n+\tif (!thread_context->payload_pattern) {\n+\t\tfprintf(stderr, \"Failed to allocate RtaPayloadPattern!\\n\");\n \t\tret = -ENOMEM;\n \t\tgoto err_payload;\n \t}\n-\n-\tthread_context->payload_pattern = thread_context->frame_copy +\n-\t\t\t\t\t  sizeof(struct vlan_ethernet_header) +\n-\t\t\t\t\t  sizeof(struct profinet_secure_header);\n-\tthread_context->payload_pattern_length =\n-\t\trta_config->frame_length - sizeof(struct vlan_ethernet_header) -\n-\t\tsizeof(struct profinet_secure_header) - sizeof(struct security_checksum);\n+\tthread_context->payload_pattern_length = MAX_FRAME_SIZE;\n \n \t/* For XDP a AF_XDP socket is allocated. Otherwise a Linux raw socket is used. */\n \tif (rta_config->xdp_enabled) {\n@@ -649,40 +671,39 @@ int rta_threads_create(struct thread_context *thread_context)\n \t\tthread_context->rx_security_context = NULL;\n \t}\n \n-\tthread_context->meta_data_offset =\n-\t\tget_meta_data_offset(RTA_FRAME_TYPE, rta_config->security_mode);\n-\n-\tret = create_rt_thread(&thread_context->tx_task_id, rta_config->tx_thread_priority,\n-\t\t\t       rta_config->tx_thread_cpu,\n+\tret = create_rt_thread(&thread_context->tx_task_id, \"RtaTxThread\",\n+\t\t\t       rta_config->tx_thread_priority, rta_config->tx_thread_cpu,\n \t\t\t       rta_config->xdp_enabled ? rta_xdp_tx_thread_routine\n \t\t\t\t\t\t       : rta_tx_thread_routine,\n-\t\t\t       thread_context, \"RtaTxThread\");\n+\t\t\t       thread_context);\n \tif (ret) {\n \t\tfprintf(stderr, \"Failed to create Rta Tx Thread!\\n\");\n \t\tgoto err_thread;\n \t}\n \n \tif (!rta_config->rx_mirror_enabled) {\n-\t\tret = create_rt_thread(&thread_context->tx_gen_task_id,\n+\t\tret = create_rt_thread(&thread_context->tx_gen_task_id, \"RtaTxGenThread\",\n \t\t\t\t       rta_config->tx_thread_priority, rta_config->tx_thread_cpu,\n-\t\t\t\t       rta_tx_generation_thread_routine, thread_context,\n-\t\t\t\t       \"RtaTxGenThread\");\n+\t\t\t\t       rta_tx_generation_thread_routine, thread_context);\n \t\tif (ret) {\n \t\t\tfprintf(stderr, \"Failed to create Rta TxGen Thread!\\n\");\n \t\t\tgoto err_thread_txgen;\n \t\t}\n \t}\n \n-\tret = create_rt_thread(&thread_context->rx_task_id, rta_config->rx_thread_priority,\n-\t\t\t       rta_config->rx_thread_cpu,\n+\tret = create_rt_thread(&thread_context->rx_task_id, \"RtaRxThread\",\n+\t\t\t       rta_config->rx_thread_priority, rta_config->rx_thread_cpu,\n \t\t\t       rta_config->xdp_enabled ? rta_xdp_rx_thread_routine\n \t\t\t\t\t\t       : rta_rx_thread_routine,\n-\t\t\t       thread_context, \"RtaRxThread\");\n+\t\t\t       thread_context);\n \tif (ret) {\n \t\tfprintf(stderr, \"Failed to create Rta Rx Thread!\\n\");\n \t\tgoto err_thread_rx;\n \t}\n \n+\tthread_context->meta_data_offset =\n+\t\tget_meta_data_offset(RTA_FRAME_TYPE, rta_config->security_mode);\n+\n out:\n \treturn 0;\n \n@@ -706,7 +727,7 @@ err_buffer:\n \t\txdp_close_socket(thread_context->xsk, rta_config->interface,\n \t\t\t\t rta_config->xdp_skb_mode);\n err_socket:\n-\tfree(thread_context->frame_copy);\n+\tfree(thread_context->payload_pattern);\n err_payload:\n \tfree(thread_context->rx_frame_data);\n err_rx:\n@@ -726,7 +747,11 @@ void rta_threads_free(struct thread_context *thread_context)\n \n \trta_config = thread_context->conf;\n \n-\tfree(thread_context->frame_copy);\n+\tif (thread_context->payload_pattern) {\n+\t\tthread_context->payload_pattern -=\n+\t\t\tsizeof(struct vlan_ethernet_header) + sizeof(struct profinet_secure_header);\n+\t\tfree(thread_context->payload_pattern);\n+\t}\n \n \tsecurity_exit(thread_context->tx_security_context);\n \tsecurity_exit(thread_context->rx_security_context);\ndiff --git a/src/rtc_thread.c b/src/rtc_thread.c\nindex e03bae0..f635b23 100644\n--- a/src/rtc_thread.c\n+++ b/src/rtc_thread.c\n@@ -1,6 +1,6 @@\n // SPDX-License-Identifier: BSD-2-Clause\n /*\n- * Copyright (C) 2020-2026 Linutronix GmbH\n+ * Copyright (C) 2020-2025 Linutronix GmbH\n  * Author Kurt Kanzenbach <kurt@linutronix.de>\n  */\n \n@@ -24,13 +24,11 @@\n #include \"log.h\"\n #include \"net.h\"\n #include \"packet.h\"\n-#include \"profinet.h\"\n #include \"rtc_thread.h\"\n #include \"security.h\"\n #include \"stat.h\"\n #include \"thread.h\"\n #include \"utils.h\"\n-#include \"workload.h\"\n #include \"xdp.h\"\n \n static void rtc_initialize_frames(struct thread_context *thread_context, unsigned char *frame_data,\n@@ -91,10 +89,6 @@ static int rtc_send_frames(struct thread_context *thread_context, unsigned char\n \tconst struct traffic_class_config *rtc_config = thread_context->conf;\n \tint len, i;\n \n-\t/* Adjust meta data */\n-\tset_mirror_tx_timestamp(rtc_config, frame_data, rtc_config->frame_length, num_frames,\n-\t\t\t\tthread_context->meta_data_offset);\n-\n \t/* Send it */\n \tlen = rtc_send_messages(thread_context, socket_fd, destination, frame_data, num_frames);\n \n@@ -118,7 +112,7 @@ static int rtc_gen_and_send_frames(struct thread_context *thread_context, int so\n \tstruct timespec tx_time = {};\n \tint len, i;\n \n-\tapp_clock_get(&tx_time);\n+\tclock_gettime(app_config.application_clock_id, &tx_time);\n \n \tfor (i = 0; i < rtc_config->num_frames_per_cycle; i++) {\n \t\tstruct prepare_frame_config frame_config;\n@@ -145,8 +139,8 @@ static int rtc_gen_and_send_frames(struct thread_context *thread_context, int so\n \tlen = rtc_send_messages(thread_context, socket_fd, destination,\n \t\t\t\tthread_context->tx_frame_data, rtc_config->num_frames_per_cycle);\n \n-\tif (len > 0)\n-\t\tstat_frames_sent_batch(RTC_FRAME_TYPE, sequence_counter_begin, len);\n+\tfor (i = 0; i < len; i++)\n+\t\tstat_frame_sent(RTC_FRAME_TYPE, sequence_counter_begin + i);\n \n \treturn len;\n }\n@@ -214,8 +208,13 @@ static void *rtc_tx_thread_routine(void *data)\n \t\t\t      rtc_config->num_frames_per_cycle, source, rtc_config->l2_destination);\n \n \tprepare_openssl(security_context);\n-\trtc_initialize_frames(thread_context, thread_context->frame_copy, 1, source,\n+\trtc_initialize_frames(thread_context, thread_context->payload_pattern, 1, source,\n \t\t\t      rtc_config->l2_destination);\n+\tthread_context->payload_pattern +=\n+\t\tsizeof(struct vlan_ethernet_header) + sizeof(struct profinet_secure_header);\n+\tthread_context->payload_pattern_length =\n+\t\trtc_config->frame_length - sizeof(struct vlan_ethernet_header) -\n+\t\tsizeof(struct profinet_secure_header) - sizeof(struct security_checksum);\n \n \tret = get_thread_start_time(app_config.application_tx_base_offset_ns, &wakeup_time);\n \tif (ret) {\n@@ -259,8 +258,6 @@ static void *rtc_tx_thread_routine(void *data)\n \t\t\t}\n \t\t}\n \n-\t\tworkload_check_finished(thread_context);\n-\n \t\t/*\n \t\t * Send RtcFrames, two possibilites:\n \t\t *  a) Generate it, or\n@@ -320,7 +317,6 @@ static void *rtc_xdp_tx_thread_routine(void *data)\n \txsk = thread_context->xsk;\n \txsk->tx_hwts.rtt = &round_trip_contexts[RTC_FRAME_TYPE];\n \txsk->tx_hwts.frames_per_cycle = rtc_config->num_frames_per_cycle;\n-\txsk->tx_hwts.meta_data_offset = thread_context->meta_data_offset;\n \n \tret = get_interface_mac_address(rtc_config->interface, source, ETH_ALEN);\n \tif (ret < 0) {\n@@ -337,8 +333,13 @@ static void *rtc_xdp_tx_thread_routine(void *data)\n \t\t\t      rtc_config->l2_destination);\n \n \tprepare_openssl(security_context);\n-\trtc_initialize_frames(thread_context, thread_context->frame_copy, 1, source,\n+\trtc_initialize_frames(thread_context, thread_context->payload_pattern, 1, source,\n \t\t\t      rtc_config->l2_destination);\n+\tthread_context->payload_pattern +=\n+\t\tsizeof(struct vlan_ethernet_header) + sizeof(struct profinet_secure_header);\n+\tthread_context->payload_pattern_length =\n+\t\trtc_config->frame_length - sizeof(struct vlan_ethernet_header) -\n+\t\tsizeof(struct profinet_secure_header) - sizeof(struct security_checksum);\n \n \tret = get_thread_start_time(app_config.application_tx_base_offset_ns, &wakeup_time);\n \tif (ret) {\n@@ -382,8 +383,6 @@ static void *rtc_xdp_tx_thread_routine(void *data)\n \t\t\t}\n \t\t}\n \n-\t\tworkload_check_finished(thread_context);\n-\n \t\t/*\n \t\t * Send RtcFrames, two possibilites:\n \t\t *  a) Generate it, or\n@@ -395,6 +394,7 @@ static void *rtc_xdp_tx_thread_routine(void *data)\n \t\t\tsequence_counter += num_frames;\n \t\t} else {\n \t\t\tunsigned int received;\n+\t\t\tuint64_t i;\n \n \t\t\tpthread_mutex_lock(&thread_context->xdp_data_mutex);\n \n@@ -410,13 +410,37 @@ static void *rtc_xdp_tx_thread_routine(void *data)\n \n \t\t\txsk_ring_prod__submit(&xsk->tx, received);\n \n-\t\t\tif (received > 0)\n-\t\t\t\tstat_frames_sent_batch(RTC_FRAME_TYPE, sequence_counter, received);\n+\t\t\tif (received > 0) {\n+\t\t\t\tif (rtc_config->tx_hwtstamp_enabled) {\n+\t\t\t\t\t/*\n+\t\t\t\t\t * Once-per-cycle: record TX SW timestamp for the first\n+\t\t\t\t\t * packet in this cycle and count all frames\n+\t\t\t\t\t */\n+\t\t\t\t\tstat_frames_sent_batch(RTC_FRAME_TYPE, sequence_counter,\n+\t\t\t\t\t\t\t       received);\n+\t\t\t\t} else {\n+\t\t\t\t\tfor (i = sequence_counter; i < sequence_counter + received;\n+\t\t\t\t\t     ++i)\n+\t\t\t\t\t\tstat_frame_sent(RTC_FRAME_TYPE, i);\n+\t\t\t\t}\n+\t\t\t}\n \n \t\t\txsk->outstanding_tx += received;\n \t\t\tthread_context->received_frames = 0;\n \t\t\txdp_complete_tx(xsk);\n \n+#ifdef TX_TIMESTAMP\n+\t\t\tif (received > 0 && rtc_config->tx_hwtstamp_enabled) {\n+\t\t\t\t/*\n+\t\t\t\t * TX HW timestamp becomes available in the next cycle\n+\t\t\t\t * after the packet is transmitted.\n+\t\t\t\t * This is one cycle later than SW timestamp tracked\n+\t\t\t\t * using sequence_counter\n+\t\t\t\t */\n+\t\t\t\txsk->tx_hwts.seq_lagged = sequence_counter;\n+\t\t\t}\n+#endif\n+\n \t\t\tpthread_mutex_unlock(&thread_context->xdp_data_mutex);\n \t\t}\n \n@@ -438,8 +462,8 @@ static void *rtc_rx_thread_routine(void *data)\n {\n \tstruct thread_context *thread_context = data;\n \tconst uint64_t cycle_time_ns = app_config.application_base_cycle_time_ns;\n-\tint socket_fd, ret, received;\n \tstruct timespec wakeup_time;\n+\tint socket_fd, ret;\n \n \tsocket_fd = thread_context->socket_fd;\n \n@@ -475,9 +499,7 @@ static void *rtc_rx_thread_routine(void *data)\n \t\t}\n \n \t\t/* Receive Rtc frames. */\n-\t\treceived = packet_receive_messages(thread_context->packet_context, &recv_req);\n-\n-\t\tworkload_signal(thread_context, received);\n+\t\tpacket_receive_messages(thread_context->packet_context, &recv_req);\n \t}\n \n \treturn NULL;\n@@ -525,8 +547,6 @@ static void *rtc_xdp_rx_thread_routine(void *data)\n \t\t\t\t\t      receive_profinet_frame, thread_context, NULL);\n \t\tthread_context->received_frames = received;\n \t\tpthread_mutex_unlock(&thread_context->xdp_data_mutex);\n-\n-\t\tworkload_signal(thread_context, received);\n \t}\n \n \treturn NULL;\n@@ -537,7 +557,7 @@ int rtc_threads_create(struct thread_context *thread_context)\n \tstruct traffic_class_config *rtc_config;\n \tint ret;\n \n-\tif (!config_is_traffic_class_active(RTC_FRAME_TYPE))\n+\tif (!config_is_traffic_class_active(\"Rtc\"))\n \t\tgoto out;\n \n \tinit_mutex(&thread_context->data_mutex);\n@@ -576,20 +596,13 @@ int rtc_threads_create(struct thread_context *thread_context)\n \t\t}\n \t}\n \n-\t/* Initialize data structures for AE */\n-\tthread_context->frame_copy = calloc(1, MAX_FRAME_SIZE);\n-\tif (!thread_context->frame_copy) {\n-\t\tfprintf(stderr, \"Failed to allocate RtcPayloadPattern for AE!\\n\");\n+\tthread_context->payload_pattern = calloc(1, MAX_FRAME_SIZE);\n+\tif (!thread_context->payload_pattern) {\n+\t\tfprintf(stderr, \"Failed to allocate RtcPayloadPattern!\\n\");\n \t\tret = -ENOMEM;\n \t\tgoto err_payload;\n \t}\n-\n-\tthread_context->payload_pattern = thread_context->frame_copy +\n-\t\t\t\t\t  sizeof(struct vlan_ethernet_header) +\n-\t\t\t\t\t  sizeof(struct profinet_secure_header);\n-\tthread_context->payload_pattern_length =\n-\t\trtc_config->frame_length - sizeof(struct vlan_ethernet_header) -\n-\t\tsizeof(struct profinet_secure_header) - sizeof(struct security_checksum);\n+\tthread_context->payload_pattern_length = MAX_FRAME_SIZE;\n \n \t/* For XDP a AF_XDP socket is allocated. Otherwise a Linux raw socket is used. */\n \tif (rtc_config->xdp_enabled) {\n@@ -647,42 +660,32 @@ int rtc_threads_create(struct thread_context *thread_context)\n \t\tthread_context->rx_security_context = NULL;\n \t}\n \n-\tthread_context->meta_data_offset =\n-\t\tget_meta_data_offset(RTC_FRAME_TYPE, rtc_config->security_mode);\n-\n-\tret = create_rt_thread(&thread_context->tx_task_id, rtc_config->tx_thread_priority,\n-\t\t\t       rtc_config->tx_thread_cpu,\n+\tret = create_rt_thread(&thread_context->tx_task_id, \"RtcTxThread\",\n+\t\t\t       rtc_config->tx_thread_priority, rtc_config->tx_thread_cpu,\n \t\t\t       rtc_config->xdp_enabled ? rtc_xdp_tx_thread_routine\n \t\t\t\t\t\t       : rtc_tx_thread_routine,\n-\t\t\t       thread_context, \"RtcTxThread\");\n+\t\t\t       thread_context);\n \tif (ret) {\n \t\tfprintf(stderr, \"Failed to create Rtc Tx thread!\\n\");\n \t\tgoto err_thread_create1;\n \t}\n \n-\tret = create_rt_thread(&thread_context->rx_task_id, rtc_config->rx_thread_priority,\n-\t\t\t       rtc_config->rx_thread_cpu,\n+\tret = create_rt_thread(&thread_context->rx_task_id, \"RtcRxThread\",\n+\t\t\t       rtc_config->rx_thread_priority, rtc_config->rx_thread_cpu,\n \t\t\t       rtc_config->xdp_enabled ? rtc_xdp_rx_thread_routine\n \t\t\t\t\t\t       : rtc_rx_thread_routine,\n-\t\t\t       thread_context, \"RtcRxThread\");\n+\t\t\t       thread_context);\n \tif (ret) {\n \t\tfprintf(stderr, \"Failed to create Rtc Rx thread!\\n\");\n \t\tgoto err_thread_create2;\n \t}\n \n-\t/* Create workload thread for execution after network RX */\n-\tret = workload_context_init(thread_context);\n-\tif (ret) {\n-\t\tfprintf(stderr, \"Failed to create Rtc Workload context!\\n\");\n-\t\tgoto err_thread_wl;\n-\t}\n+\tthread_context->meta_data_offset =\n+\t\tget_meta_data_offset(RTC_FRAME_TYPE, rtc_config->security_mode);\n \n out:\n \treturn 0;\n \n-err_thread_wl:\n-\tthread_context->stop = 1;\n-\tpthread_join(thread_context->rx_task_id, NULL);\n err_thread_create2:\n \tthread_context->stop = 1;\n \tpthread_join(thread_context->tx_task_id, NULL);\n@@ -699,7 +702,7 @@ err_thread:\n \t\txdp_close_socket(thread_context->xsk, rtc_config->interface,\n \t\t\t\t rtc_config->xdp_skb_mode);\n err_socket:\n-\tfree(thread_context->frame_copy);\n+\tfree(thread_context->payload_pattern);\n err_payload:\n \tfree(thread_context->rx_frame_data);\n err_rx:\n@@ -719,7 +722,11 @@ void rtc_threads_free(struct thread_context *thread_context)\n \n \trtc_config = thread_context->conf;\n \n-\tfree(thread_context->frame_copy);\n+\tif (thread_context->payload_pattern) {\n+\t\tthread_context->payload_pattern -=\n+\t\t\tsizeof(struct vlan_ethernet_header) + sizeof(struct profinet_secure_header);\n+\t\tfree(thread_context->payload_pattern);\n+\t}\n \n \tsecurity_exit(thread_context->tx_security_context);\n \tsecurity_exit(thread_context->rx_security_context);\n@@ -736,8 +743,6 @@ void rtc_threads_free(struct thread_context *thread_context)\n \tif (thread_context->xsk)\n \t\txdp_close_socket(thread_context->xsk, rtc_config->interface,\n \t\t\t\t rtc_config->xdp_skb_mode);\n-\n-\tworkload_thread_free(thread_context);\n }\n \n void rtc_threads_wait_for_finish(struct thread_context *thread_context)\n@@ -745,8 +750,6 @@ void rtc_threads_wait_for_finish(struct thread_context *thread_context)\n \tif (!thread_context)\n \t\treturn;\n \n-\tworkload_thread_wait_for_finish(thread_context);\n-\n \tif (thread_context->rx_task_id)\n \t\tpthread_join(thread_context->rx_task_id, NULL);\n \tif (thread_context->tx_task_id)\ndiff --git a/src/security.c b/src/security.c\nindex 5625eb0..c1f77d2 100644\n--- a/src/security.c\n+++ b/src/security.c\n@@ -37,12 +37,10 @@ struct security_context *security_init(enum security_algorithm algorithm, const\n \n \t/* Set key and cipher */\n \tif (1 != EVP_EncryptInit_ex2(context->ctx, context->cipher, key, NULL, NULL))\n-\t\tgoto err_init;\n+\t\tgoto err_ctx;\n \n \treturn context;\n \n-err_init:\n-\tEVP_CIPHER_CTX_free(context->ctx);\n err_ctx:\n \tEVP_CIPHER_free(context->cipher);\n err_cipher:\ndiff --git a/src/stat.c b/src/stat.c\nindex ff1f1af..b11e683 100644\n--- a/src/stat.c\n+++ b/src/stat.c\n@@ -1,6 +1,6 @@\n // SPDX-License-Identifier: BSD-2-Clause\n /*\n- * Copyright (C) 2021-2026 Linutronix GmbH\n+ * Copyright (C) 2021-2025 Linutronix GmbH\n  * Author Kurt Kanzenbach <kurt@linutronix.de>\n  */\n \n@@ -66,13 +66,11 @@ static void stat_reset(struct statistics *stats)\n {\n \tmemset(stats, 0, sizeof(struct statistics));\n \tstats->round_trip_min = UINT64_MAX;\n-\tstats->oneway_min = INT64_MAX;\n-\tstats->oneway_max = INT64_MIN;\n+\tstats->oneway_min = UINT64_MAX;\n \tstats->rx_min = UINT64_MAX;\n \tstats->rx_hw2xdp_min = UINT64_MAX;\n \tstats->rx_xdp2app_min = UINT64_MAX;\n-\tfor (int i = 0; i < WORKLOAD_MAX; i++)\n-\t\tstats->workload[i].rx_workload_min = UINT64_MAX;\n+\tstats->rx_workload_min = UINT64_MAX;\n \tstats->tx_min = UINT64_MAX;\n \tstats->proc_first_min = UINT64_MAX;\n \tstats->proc_batch_min = UINT64_MAX;\n@@ -110,7 +108,7 @@ int stat_init(enum log_stat_options log_selection)\n \t\t}\n \n \t\tif (allocation_error)\n-\t\t\tgoto err_alloc;\n+\t\t\treturn -ENOMEM;\n \t}\n \n \tfor (i = 0; i < NUM_FRAME_TYPES; i++) {\n@@ -119,16 +117,11 @@ int stat_init(enum log_stat_options log_selection)\n \t}\n \n \tif (app_config.debug_stop_trace_on_outlier) {\n-\t\tfile_tracing_on = fopen(\"/sys/kernel/tracing/tracing_on\", \"w\");\n-\t\tif (!file_tracing_on) {\n-\t\t\tfprintf(stderr,\n-\t\t\t\t\"Failed to open 'tracing_on' file! Try mounting tracefs.\\n\");\n+\t\tfile_tracing_on = fopen(\"/sys/kernel/debug/tracing/tracing_on\", \"w\");\n+\t\tif (!file_tracing_on)\n \t\t\treturn -errno;\n-\t\t}\n-\t\tfile_trace_marker = fopen(\"/sys/kernel/tracing/trace_marker\", \"w\");\n+\t\tfile_trace_marker = fopen(\"/sys/kernel/debug/tracing/trace_marker\", \"w\");\n \t\tif (!file_trace_marker) {\n-\t\t\tfprintf(stderr,\n-\t\t\t\t\"Failed to open 'tracing_marker' file! Try mounting tracefs.\\n\");\n \t\t\tfclose(file_tracing_on);\n \t\t\treturn -errno;\n \t\t}\n@@ -143,15 +136,7 @@ int stat_init(enum log_stat_options log_selection)\n \n \tlog_stat_user_selected = log_selection;\n \n-\t/* L2 can be anything. Update name accordingly. */\n-\tstat_frame_type_names[GENERICL2_FRAME_TYPE] = app_config.classes[GENERICL2_FRAME_TYPE].name;\n-\n \treturn 0;\n-\n-err_alloc:\n-\tfor (i = 0; i < NUM_FRAME_TYPES; i++)\n-\t\tfree(round_trip_contexts[i].backlog);\n-\treturn -ENOMEM;\n }\n \n void stat_free(void)\n@@ -179,7 +164,7 @@ void stat_update(void)\n \tbool proceed = false;\n \tstruct timespec now;\n \n-\tapp_clock_get(&now);\n+\tclock_gettime(app_config.application_clock_id, &now);\n \tcurr_time = ts_to_ns(&now);\n \n \tif (!last_ts)\n@@ -239,12 +224,6 @@ static inline void stat_update_min_max(uint64_t new_value, uint64_t *min, uint64\n \t*min = (new_value < *min) ? new_value : *min;\n }\n \n-static inline void stat_update_min_max_s(int64_t new_value, int64_t *min, int64_t *max)\n-{\n-\t*max = (new_value > *max) ? new_value : *max;\n-\t*min = (new_value < *min) ? new_value : *min;\n-}\n-\n static inline size_t get_first_frame_backlog_idx(uint64_t cycle_number,\n \t\t\t\t\t\t enum stat_frame_type frame_type,\n \t\t\t\t\t\t size_t backlog_len)\n@@ -256,7 +235,7 @@ static inline size_t get_first_frame_backlog_idx(uint64_t cycle_number,\n }\n \n static bool stat_frame_received_common(struct statistics *stat, enum stat_frame_type frame_type,\n-\t\t\t\t       uint64_t rt_time, int64_t oneway_time, bool out_of_order,\n+\t\t\t\t       uint64_t rt_time, uint64_t oneway_time, bool out_of_order,\n \t\t\t\t       bool payload_mismatch, bool frame_id_mismatch,\n \t\t\t\t       uint64_t rx_hw2app_time, uint64_t rx_hw2xdp_time,\n \t\t\t\t       uint64_t rx_xdp2app_time)\n@@ -276,7 +255,7 @@ static bool stat_frame_received_common(struct statistics *stat, enum stat_frame_\n \t\tstat->round_trip_avg = stat->round_trip_sum / (double)stat->round_trip_count;\n \t}\n \n-\tstat_update_min_max_s(oneway_time, &stat->oneway_min, &stat->oneway_max);\n+\tstat_update_min_max(oneway_time, &stat->oneway_min, &stat->oneway_max);\n \n \tif (stat_frame_type_is_real_time(frame_type) && oneway_time > rtt_expected_rt_limit / 2) {\n \t\tstat->oneway_outliers++;\n@@ -287,19 +266,17 @@ static bool stat_frame_received_common(struct statistics *stat, enum stat_frame_\n \tstat->oneway_avg = stat->oneway_sum / (double)stat->oneway_count;\n \n \tif (rx_hw2app_time != 0 && rx_hw2xdp_time != 0 && rx_xdp2app_time != 0) {\n-\t\tstat->rx_count++;\n-\n \t\tstat_update_min_max(rx_hw2app_time, &stat->rx_min, &stat->rx_max);\n \t\tstat->rx_sum += rx_hw2app_time;\n-\t\tstat->rx_avg = stat->rx_sum / (double)stat->rx_count;\n+\t\tstat->rx_avg = stat->rx_sum / (double)stat->oneway_count;\n \n \t\tstat_update_min_max(rx_hw2xdp_time, &stat->rx_hw2xdp_min, &stat->rx_hw2xdp_max);\n \t\tstat->rx_hw2xdp_sum += rx_hw2xdp_time;\n-\t\tstat->rx_hw2xdp_avg = stat->rx_hw2xdp_sum / (double)stat->rx_count;\n+\t\tstat->rx_hw2xdp_avg = stat->rx_hw2xdp_sum / (double)stat->oneway_count;\n \n \t\tstat_update_min_max(rx_xdp2app_time, &stat->rx_xdp2app_min, &stat->rx_xdp2app_max);\n \t\tstat->rx_xdp2app_sum += rx_xdp2app_time;\n-\t\tstat->rx_xdp2app_avg = stat->rx_xdp2app_sum / (double)stat->rx_count;\n+\t\tstat->rx_xdp2app_avg = stat->rx_xdp2app_sum / (double)stat->oneway_count;\n \t}\n \n \tstat->frames_received++;\n@@ -311,7 +288,8 @@ static bool stat_frame_received_common(struct statistics *stat, enum stat_frame_\n }\n \n #ifdef TX_TIMESTAMP\n-static void stat_frame_sent_latency_common(struct statistics *stat, uint64_t tx_latency_us)\n+static void stat_frame_sent_latency_common(struct statistics *stat, enum stat_frame_type frame_type,\n+\t\t\t\t\t   uint64_t tx_latency_us)\n {\n \tstat_update_min_max(tx_latency_us, &stat->tx_min, &stat->tx_max);\n \n@@ -320,24 +298,18 @@ static void stat_frame_sent_latency_common(struct statistics *stat, uint64_t tx_\n \tstat->tx_avg = stat->tx_sum / (double)stat->tx_count;\n }\n \n-static void stat_frame_proc_first_common(struct statistics *stat, uint64_t proc_first_us)\n+static void stat_frame_proc_first_common(struct statistics *stat, enum stat_frame_type frame_type,\n+\t\t\t\t\t uint64_t proc_first_us)\n {\n-\t/* Check for outliers - processing time exceeding cycle time */\n-\tif (proc_first_us > app_config.application_base_cycle_time_ns / 1000)\n-\t\tstat->proc_first_outliers++;\n-\n \tstat_update_min_max(proc_first_us, &stat->proc_first_min, &stat->proc_first_max);\n \tstat->proc_first_count++;\n \tstat->proc_first_sum += proc_first_us;\n \tstat->proc_first_avg = stat->proc_first_sum / (double)stat->proc_first_count;\n }\n \n-static void stat_frame_proc_batch_common(struct statistics *stat, uint64_t proc_batch_us)\n+static void stat_frame_proc_batch_common(struct statistics *stat, enum stat_frame_type frame_type,\n+\t\t\t\t\t uint64_t proc_batch_us)\n {\n-\t/* Check for outliers - processing time exceeding cycle time */\n-\tif (proc_batch_us > app_config.application_base_cycle_time_ns / 1000)\n-\t\tstat->proc_batch_outliers++;\n-\n \tstat_update_min_max(proc_batch_us, &stat->proc_batch_min, &stat->proc_batch_max);\n \tstat->proc_batch_count++;\n \tstat->proc_batch_sum += proc_batch_us;\n@@ -347,10 +319,10 @@ static void stat_frame_proc_batch_common(struct statistics *stat, uint64_t proc_\n \n #if defined(WITH_MQTT)\n static void stat_frame_received_per_period(enum stat_frame_type frame_type, uint64_t curr_time,\n-\t\t\t\t\t   uint64_t rt_time, int64_t oneway_time, bool out_of_order,\n-\t\t\t\t\t   bool payload_mismatch, bool frame_id_mismatch,\n-\t\t\t\t\t   uint64_t rx_hw2app_time, uint64_t rx_hw2xdp_time,\n-\t\t\t\t\t   uint64_t rx_xdp2app_time)\n+\t\t\t\t\t   uint64_t rt_time, uint64_t oneway_time,\n+\t\t\t\t\t   bool out_of_order, bool payload_mismatch,\n+\t\t\t\t\t   bool frame_id_mismatch, uint64_t rx_hw2app_time,\n+\t\t\t\t\t   uint64_t rx_hw2xdp_time, uint64_t rx_xdp2app_time)\n {\n \tstruct statistics *stat_per_period = &statistics_per_period[frame_type];\n \n@@ -368,11 +340,9 @@ static void stat_frame_sent_per_period(enum stat_frame_type frame_type)\n \tstat_per_period->frames_sent++;\n }\n \n-static void stat_frame_workload_per_period(int id, enum stat_frame_type frame_type,\n-\t\t\t\t\t   uint64_t workload_time)\n+static void stat_frame_workload_per_period(enum stat_frame_type frame_type, uint64_t workload_time)\n {\n-\tstruct workload_statistics *stat_per_period =\n-\t\t&statistics_per_period[frame_type].workload[id];\n+\tstruct statistics *stat_per_period = &statistics_per_period[frame_type];\n \n \tif (stat_per_period->rx_workload_count >\n \t    app_config.classes[frame_type].rx_workload_skip_count) {\n@@ -392,7 +362,7 @@ static void stat_frame_sent_latency_per_period(enum stat_frame_type frame_type,\n {\n \tstruct statistics *stat_per_period = &statistics_per_period[frame_type];\n \n-\tstat_frame_sent_latency_common(stat_per_period, tx_latency_us);\n+\tstat_frame_sent_latency_common(stat_per_period, frame_type, tx_latency_us);\n }\n \n static void stat_frame_proc_first_per_period(enum stat_frame_type frame_type,\n@@ -400,7 +370,7 @@ static void stat_frame_proc_first_per_period(enum stat_frame_type frame_type,\n {\n \tstruct statistics *stat_per_period = &statistics_per_period[frame_type];\n \n-\tstat_frame_proc_first_common(stat_per_period, proc_first_us);\n+\tstat_frame_proc_first_common(stat_per_period, frame_type, proc_first_us);\n }\n \n static void stat_frame_proc_batch_per_period(enum stat_frame_type frame_type,\n@@ -408,12 +378,10 @@ static void stat_frame_proc_batch_per_period(enum stat_frame_type frame_type,\n {\n \tstruct statistics *stat_per_period = &statistics_per_period[frame_type];\n \n-\tstat_frame_proc_batch_common(stat_per_period, proc_batch_us);\n+\tstat_frame_proc_batch_common(stat_per_period, frame_type, proc_batch_us);\n }\n #endif\n #else\n-#pragma GCC diagnostic push\n-#pragma GCC diagnostic ignored \"-Wunused-parameter\"\n static void stat_frame_received_per_period(enum stat_frame_type frame_type, uint64_t curr_time,\n \t\t\t\t\t   uint64_t rt_time, bool out_of_order,\n \t\t\t\t\t   bool payload_mismatch, bool frame_id_mismatch,\n@@ -426,8 +394,7 @@ static void stat_frame_sent_per_period(enum stat_frame_type frame_type)\n {\n }\n \n-static void stat_frame_workload_per_period(int id, enum stat_frame_type frame_type,\n-\t\t\t\t\t   uint64_t workload_time)\n+static void stat_frame_workload_per_period(enum stat_frame_type frame_type, uint64_t workload_time)\n {\n }\n #ifdef TX_TIMESTAMP\n@@ -445,7 +412,6 @@ static void stat_frame_proc_batch_per_period(enum stat_frame_type frame_type,\n \t\t\t\t\t     uint64_t proc_batch_us)\n {\n }\n-#pragma GCC diagnostic pop\n #endif\n #endif\n \n@@ -460,6 +426,7 @@ void stat_frames_sent_batch(enum stat_frame_type frame_type, uint64_t cycle_numb\n {\n \tstruct round_trip_context *rtt = &round_trip_contexts[frame_type];\n \tstruct statistics *stat = &global_statistics[frame_type];\n+\tsize_t idx = cycle_number % rtt->backlog_len;\n \tstruct timespec tx_time = {};\n \n \tif (frame_count == 1) {\n@@ -473,32 +440,9 @@ void stat_frames_sent_batch(enum stat_frame_type frame_type, uint64_t cycle_numb\n \tif ((log_stat_user_selected == LOG_REFERENCE ||\n \t     log_stat_user_selected == LOG_TX_TIMESTAMPS) &&\n \t    rtt->backlog) {\n-\t\tuint64_t end = cycle_number + frame_count;\n-\t\tuint64_t c;\n-\n-\t\tapp_clock_get(&tx_time);\n-\t\tif (app_config.classes[frame_type].tx_hwtstamp_enabled) {\n-\t\t\tuint64_t fpc = app_config.classes[frame_type].num_frames_per_cycle;\n-\t\t\tuint64_t base = (cycle_number / fpc) * fpc;\n-\n-\t\t\t/*\n-\t\t\t * Record Tx SW timestamp at the cycle-aligned first-frame slot of each\n-\t\t\t * cycle this batch covers. The completion path looks up sw_ts at the cycle\n-\t\t\t * boundary (seq rounded down to fpc), so writes must use the same\n-\t\t\t * alignment. Skip cycles whose first frame was sent in an earlier batch —\n-\t\t\t * that slot already holds the correct sw_ts and re-stamping it now would\n-\t\t\t * shrink/invert the measured latency.\n-\t\t\t */\n-\t\t\tfor (c = base; c < end; c += fpc) {\n-\t\t\t\tif (c < cycle_number)\n-\t\t\t\t\tcontinue;\n-\t\t\t\trtt->backlog[c % rtt->backlog_len].sw_ts = ts_to_ns(&tx_time);\n-\t\t\t}\n-\t\t} else {\n-\t\t\t/* If Tx HW TS is not enabled, each frame has its own sw_ts. */\n-\t\t\tfor (c = cycle_number; c < end; c++)\n-\t\t\t\trtt->backlog[c % rtt->backlog_len].sw_ts = ts_to_ns(&tx_time);\n-\t\t}\n+\t\t/* Record Tx SW timestamp for the first frame in the cycle */\n+\t\tclock_gettime(app_config.application_clock_id, &tx_time);\n+\t\trtt->backlog[idx].sw_ts = ts_to_ns(&tx_time);\n \t}\n \n \t/* Increment stats by frame_count */\n@@ -546,7 +490,7 @@ void stat_frame_sent_latency(enum stat_frame_type frame_type, uint64_t seq)\n \t\t\t    (unsigned long long)hw_ts, (long long)latency, idx);\n \n \t\t/* Update global stats */\n-\t\tstat_frame_sent_latency_common(stat, latency);\n+\t\tstat_frame_sent_latency_common(stat, frame_type, latency);\n \n \t\t/* Update stats per collection interval */\n \t\tstat_frame_sent_latency_per_period(frame_type, latency);\n@@ -572,19 +516,19 @@ void stat_frame_received(enum stat_frame_type frame_type, uint64_t cycle_number,\n \t\t\t bool payload_mismatch, bool frame_id_mismatch, uint64_t tx_timestamp,\n \t\t\t uint64_t rx_hw_timestamp, uint64_t rx_sw_timestamp)\n {\n-\tuint64_t rt_time = 0, curr_time, rx_hw2app_time, rx_hw2xdp_time, rx_xdp2app_time;\n \tstruct round_trip_context *rtt = &round_trip_contexts[frame_type];\n \tconst bool histogram = app_config.stats_histogram_enabled;\n \tstruct statistics *stat = &global_statistics[frame_type];\n+\tuint64_t rt_time = 0, curr_time, oneway_time, rx_hw2app_time, rx_hw2xdp_time,\n+\t\t rx_xdp2app_time;\n \tstruct timespec rx_time = {};\n \tbool outlier = false;\n-\tint64_t oneway_time;\n \n \tlog_message(LOG_LEVEL_DEBUG, \"%s: frame[%\" PRIu64 \"] received\\n\",\n \t\t    stat_frame_type_to_string(frame_type), cycle_number);\n \n \t/* Record Rx timestamp in us */\n-\tapp_clock_get(&rx_time);\n+\tclock_gettime(app_config.application_clock_id, &rx_time);\n \tcurr_time = ts_to_ns(&rx_time);\n \n \t/* Store RX HW timestamp for ProFirst and ProBatch latency measurement at Mirror */\n@@ -640,7 +584,7 @@ void stat_frame_received(enum stat_frame_type frame_type, uint64_t cycle_number,\n \t}\n \n \t/* Calc Oneway Time */\n-\toneway_time = (int64_t)curr_time - (int64_t)tx_timestamp;\n+\toneway_time = curr_time - tx_timestamp;\n \toneway_time /= 1000;\n \n \tif (rx_hw_timestamp != 0 && rx_sw_timestamp != 0) {\n@@ -725,7 +669,7 @@ void stat_proc_first_latency(enum stat_frame_type frame_type, uint64_t cycle_num\n \t\t\t    rx_hw_ts, tx_hw_timestamp);\n \n \t\t/* Update global stats */\n-\t\tstat_frame_proc_first_common(stat, proc_first_latency);\n+\t\tstat_frame_proc_first_common(stat, frame_type, proc_first_latency);\n \n \t\t/* Update stats per collection interval */\n \t\tstat_frame_proc_first_per_period(frame_type, proc_first_latency);\n@@ -782,7 +726,7 @@ void stat_proc_batch_latency(enum stat_frame_type frame_type, uint64_t cycle_num\n \t\t\t    rx_hw_ts, last_tx_hw_timestamp);\n \n \t\t/* Update global stats */\n-\t\tstat_frame_proc_batch_common(stat, proc_batch_latency);\n+\t\tstat_frame_proc_batch_common(stat, frame_type, proc_batch_latency);\n \n \t\t/* Update stats per collection interval */\n \t\tstat_frame_proc_batch_per_period(frame_type, proc_batch_latency);\n@@ -796,17 +740,17 @@ void stat_proc_batch_latency(enum stat_frame_type frame_type, uint64_t cycle_num\n }\n #endif\n \n-void stat_frame_workload(int id, enum stat_frame_type frame_type, uint64_t cycle_number,\n+void stat_frame_workload(enum stat_frame_type frame_type, uint64_t cycle_number,\n \t\t\t struct timespec start_ts)\n {\n-\tstruct workload_statistics *stat = &global_statistics[frame_type].workload[id];\n+\tstruct statistics *stat = &global_statistics[frame_type];\n \tuint64_t workload_time = 0, curr_time, start_time;\n \tstruct timespec clk_time = {};\n \n \tlog_message(LOG_LEVEL_DEBUG, \"%s: frame[%\" PRIu64 \"] workload_complete\\n\",\n \t\t    stat_frame_type_to_string(frame_type), cycle_number);\n \n-\tapp_clock_get(&clk_time);\n+\tclock_gettime(app_config.application_clock_id, &clk_time);\n \tcurr_time = ts_to_ns(&clk_time);\n \tstart_time = ts_to_ns(&start_ts);\n \n@@ -822,33 +766,40 @@ void stat_frame_workload(int id, enum stat_frame_type frame_type, uint64_t cycle\n \tstat->rx_workload_avg = stat->rx_workload_sum / (double)stat->rx_workload_count;\n \n \t/* Update stats per collection interval */\n-\tstat_frame_workload_per_period(id, frame_type, workload_time);\n+\tstat_frame_workload_per_period(frame_type, workload_time);\n }\n \n-void stat_inc_workload_outlier(int id, enum stat_frame_type frame_type)\n+void stat_inc_workload_outlier(enum stat_frame_type frame_type)\n {\n-\tstruct workload_statistics *stat = &global_statistics[frame_type].workload[id];\n+\tstruct statistics *stat = &global_statistics[frame_type];\n \n-\tif (stat->rx_workload_count > app_config.classes[frame_type].rx_workload_skip_count)\n-\t\tstat->rx_workload_outliers++;\n+\tstat->rx_workload_outliers++;\n }\n \n-static int append_jlog_u64(char **buffer, size_t *len, const char *stat, uint64_t value)\n+static int json_err_handling(char **buffer, size_t *len, int ret)\n {\n-\tint ret;\n+\t/* Error? */\n+\tif (ret < 0)\n+\t\treturn -EINVAL;\n \n-\tret = snprintf(*buffer, *len, \"\\\"%s\\\": %\" PRIu64 \",\\n\", stat, value);\n+\t/* Buffer too small? */\n+\tif (ret >= *len)\n+\t\treturn -EINVAL;\n \n-\treturn snprintf_err_handling(buffer, len, ret);\n+\t/* All good */\n+\t*buffer += ret;\n+\t*len -= ret;\n+\n+\treturn 0;\n }\n \n-static int append_jlog_i64(char **buffer, size_t *len, const char *stat, int64_t value)\n+static int append_jlog_u64(char **buffer, size_t *len, const char *stat, uint64_t value)\n {\n \tint ret;\n \n-\tret = snprintf(*buffer, *len, \"\\\"%s\\\": %\" PRIi64 \",\\n\", stat, value);\n+\tret = snprintf(*buffer, *len, \"\\\"%s\\\": %\" PRIu64 \",\\n\", stat, value);\n \n-\treturn snprintf_err_handling(buffer, len, ret);\n+\treturn json_err_handling(buffer, len, ret);\n }\n \n static int last_jlog_u64(char **buffer, size_t *len, const char *stat, uint64_t value)\n@@ -857,91 +808,33 @@ static int last_jlog_u64(char **buffer, size_t *len, const char *stat, uint64_t\n \n \tret = snprintf(*buffer, *len, \"\\\"%s\\\": %\" PRIu64 \"\\n\", stat, value);\n \n-\treturn snprintf_err_handling(buffer, len, ret);\n+\treturn json_err_handling(buffer, len, ret);\n }\n \n-static int append_jlog_float(char **buffer, size_t *len, const char *stat, double value)\n+static int append_jlog_float(char **buffer, size_t *len, const char *stat, float value)\n {\n \tint ret;\n \n \tret = snprintf(*buffer, *len, \"\\\"%s\\\": %lf,\\n\", stat, value);\n \n-\treturn snprintf_err_handling(buffer, len, ret);\n+\treturn json_err_handling(buffer, len, ret);\n }\n \n-static int append_jlog_workload_stats(char **buffer, size_t *len, enum stat_frame_type frame_type,\n-\t\t\t\t      const struct statistics *stat)\n-{\n-\tint ret, num;\n-\n-\tnum = app_config.classes[frame_type].workload_thread_cpus_num;\n-\n-\tif (num == 1) {\n-\t\tret = append_jlog_u64(buffer, len, \"RxWorkloadMin\",\n-\t\t\t\t      stat->workload[0].rx_workload_min);\n-\t\tif (ret)\n-\t\t\treturn ret;\n-\n-\t\tret = append_jlog_u64(buffer, len, \"RxWorkloadMax\",\n-\t\t\t\t      stat->workload[0].rx_workload_max);\n-\t\tif (ret)\n-\t\t\treturn ret;\n-\n-\t\tret = append_jlog_float(buffer, len, \"RxWorkloadAv\",\n-\t\t\t\t\tstat->workload[0].rx_workload_avg);\n-\t\tif (ret)\n-\t\t\treturn ret;\n-\n-\t\treturn append_jlog_u64(buffer, len, \"RxWorkloadOutliers\",\n-\t\t\t\t       stat->workload[0].rx_workload_outliers);\n-\t}\n-\n-\tfor (int i = 0; i < num; i++) {\n-\t\tconst struct workload_statistics *wl = &stat->workload[i];\n-\n-\t\t/* Encode workload id into statistics */\n-\t\tret = snprintf(*buffer, *len, \"\\\"RxWorkload%dMin\\\": %\" PRIu64 \",\\n\", i,\n-\t\t\t       wl->rx_workload_min);\n-\t\tret = snprintf_err_handling(buffer, len, ret);\n-\t\tif (ret)\n-\t\t\treturn ret;\n-\n-\t\tret = snprintf(*buffer, *len, \"\\\"RxWorkload%dMax\\\": %\" PRIu64 \",\\n\", i,\n-\t\t\t       wl->rx_workload_max);\n-\t\tret = snprintf_err_handling(buffer, len, ret);\n-\t\tif (ret)\n-\t\t\treturn ret;\n-\n-\t\tret = snprintf(*buffer, *len, \"\\\"RxWorkload%dAv\\\": %lf,\\n\", i, wl->rx_workload_avg);\n-\t\tret = snprintf_err_handling(buffer, len, ret);\n-\t\tif (ret)\n-\t\t\treturn ret;\n-\n-\t\tret = snprintf(*buffer, *len, \"\\\"RxWorkload%dOutliers\\\": %\" PRIu64 \",\\n\", i,\n-\t\t\t       wl->rx_workload_outliers);\n-\t\tret = snprintf_err_handling(buffer, len, ret);\n-\t\tif (ret)\n-\t\t\treturn ret;\n-\t}\n-\n-\treturn 0;\n-}\n-\n-int stat_to_json(char *json, size_t len, enum stat_frame_type frame_type,\n-\t\t const struct statistics *stat, const char *tc, const char *measurement)\n+int stat_to_json(char *json, size_t len, const struct statistics *stat, const char *tc,\n+\t\t const char *measurement)\n {\n \tint ret;\n \n \t/* JSON header */\n \tret = snprintf(json, len,\n-\t\t       \"{\\\"testbench\\\" :\\n\"\n+\t\t       \"{\\\"reference\\\" :\\n\"\n \t\t       \"{\\\"Timestamp\\\" : %\" PRIu64 \",\\n\"\n \t\t       \"\\\"MeasurementName\\\" : \\\"%s\\\",\\n\"\n \t\t       \"\\\"stats\\\" : \\n\"\n \t\t       \"{\\n\"\n \t\t       \"\\\"TCName\\\" : \\\"%s\\\",\\n\",\n \t\t       stat->time_stamp, measurement, tc);\n-\tret = snprintf_err_handling(&json, &len, ret);\n+\tret = json_err_handling(&json, &len, ret);\n \tif (ret)\n \t\treturn ret;\n \n@@ -966,11 +859,11 @@ int stat_to_json(char *json, size_t len, enum stat_frame_type frame_type,\n \tif (ret)\n \t\treturn ret;\n \n-\tret = append_jlog_i64(&json, &len, \"OnewayMin\", stat->oneway_min);\n+\tret = append_jlog_u64(&json, &len, \"OnewayMin\", stat->oneway_min);\n \tif (ret)\n \t\treturn ret;\n \n-\tret = append_jlog_i64(&json, &len, \"OnewayMax\", stat->oneway_max);\n+\tret = append_jlog_u64(&json, &len, \"OnewayMax\", stat->oneway_max);\n \tif (ret)\n \t\treturn ret;\n \n@@ -990,10 +883,6 @@ int stat_to_json(char *json, size_t len, enum stat_frame_type frame_type,\n \tif (ret)\n \t\treturn ret;\n \n-\tret = append_jlog_u64(&json, &len, \"ProcFirstOutliers\", stat->proc_first_outliers);\n-\tif (ret)\n-\t\treturn ret;\n-\n \tret = append_jlog_u64(&json, &len, \"ProcBatchMin\", stat->proc_batch_min);\n \tif (ret)\n \t\treturn ret;\n@@ -1006,10 +895,6 @@ int stat_to_json(char *json, size_t len, enum stat_frame_type frame_type,\n \tif (ret)\n \t\treturn ret;\n \n-\tret = append_jlog_u64(&json, &len, \"ProcBatchOutliers\", stat->proc_batch_outliers);\n-\tif (ret)\n-\t\treturn ret;\n-\n \tret = append_jlog_u64(&json, &len, \"RxMin\", stat->rx_min);\n \tif (ret)\n \t\treturn ret;\n@@ -1046,7 +931,15 @@ int stat_to_json(char *json, size_t len, enum stat_frame_type frame_type,\n \tif (ret)\n \t\treturn ret;\n \n-\tret = append_jlog_workload_stats(&json, &len, frame_type, stat);\n+\tret = append_jlog_u64(&json, &len, \"RxWorkloadMin\", stat->rx_workload_min);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = append_jlog_u64(&json, &len, \"RxWorkloadMax\", stat->rx_workload_max);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = append_jlog_float(&json, &len, \"RxWorkloadAv\", stat->rx_workload_avg);\n \tif (ret)\n \t\treturn ret;\n \n@@ -1089,5 +982,5 @@ int stat_to_json(char *json, size_t len, enum stat_frame_type frame_type,\n \t/* JSON footer */\n \tret = snprintf(json, len, \"}\\n}\\n}\\n\");\n \n-\treturn snprintf_err_handling(&json, &len, ret);\n+\treturn json_err_handling(&json, &len, ret);\n }\ndiff --git a/src/stat.h b/src/stat.h\nindex aa5bad9..80de03c 100644\n--- a/src/stat.h\n+++ b/src/stat.h\n@@ -1,6 +1,6 @@\n /* SPDX-License-Identifier: BSD-2-Clause */\n /*\n- * Copyright (C) 2021-2026 Linutronix GmbH\n+ * Copyright (C) 2021-2025 Linutronix GmbH\n  * Author Kurt Kanzenbach <kurt@linutronix.de>\n  */\n \n@@ -12,9 +12,6 @@\n #include <stddef.h>\n #include <stdint.h>\n \n-/* Maximum workload threads per traffic class */\n-#define WORKLOAD_MAX 32\n-\n enum log_stat_options {\n \tLOG_REFERENCE = 0,\n \tLOG_MIRROR,\n@@ -56,15 +53,6 @@ static inline const char *stat_frame_type_to_string(enum stat_frame_type frame_t\n \treturn stat_frame_type_names[frame_type];\n }\n \n-struct workload_statistics {\n-\tuint64_t rx_workload_min;\n-\tuint64_t rx_workload_max;\n-\tuint64_t rx_workload_count;\n-\tdouble rx_workload_sum;\n-\tdouble rx_workload_avg;\n-\tuint64_t rx_workload_outliers;\n-};\n-\n struct statistics {\n \tuint64_t time_stamp;\n \tuint64_t frames_sent;\n@@ -78,8 +66,8 @@ struct statistics {\n \tuint64_t round_trip_outliers;\n \tdouble round_trip_sum;\n \tdouble round_trip_avg;\n-\tint64_t oneway_min;\n-\tint64_t oneway_max;\n+\tuint64_t oneway_min;\n+\tuint64_t oneway_max;\n \tuint64_t oneway_count;\n \tuint64_t oneway_outliers;\n \tdouble oneway_sum;\n@@ -88,20 +76,17 @@ struct statistics {\n \tuint64_t proc_first_min;\n \tuint64_t proc_first_max;\n \tuint64_t proc_first_count;\n-\tuint64_t proc_first_outliers;\n \tdouble proc_first_sum;\n \tdouble proc_first_avg;\n \t/* Batch processing latency at Mirror (1st Rx HW to Last Tx HW timestamp) */\n \tuint64_t proc_batch_min;\n \tuint64_t proc_batch_max;\n \tuint64_t proc_batch_count;\n-\tuint64_t proc_batch_outliers;\n \tdouble proc_batch_sum;\n \tdouble proc_batch_avg;\n \t/* Rx latency from NIC Rx Hw timestamp to user space timestamp */\n \tuint64_t rx_min;\n \tuint64_t rx_max;\n-\tuint64_t rx_count;\n \tdouble rx_sum;\n \tdouble rx_avg;\n \t/* Rx latency from NIC Rx Hw timestamp to Xdp prog timestamp */\n@@ -115,7 +100,12 @@ struct statistics {\n \tdouble rx_xdp2app_sum;\n \tdouble rx_xdp2app_avg;\n \t/* Workload statistics */\n-\tstruct workload_statistics workload[WORKLOAD_MAX];\n+\tuint64_t rx_workload_min;\n+\tuint64_t rx_workload_max;\n+\tuint64_t rx_workload_count;\n+\tdouble rx_workload_sum;\n+\tdouble rx_workload_avg;\n+\tuint64_t rx_workload_outliers;\n \t/* Tx latency from user space (at send) to NIC Tx HW timestamp */\n \tuint64_t tx_min;\n \tuint64_t tx_max;\n@@ -148,16 +138,15 @@ void stat_frame_received(enum stat_frame_type frame_type, uint64_t cycle_number,\n void stat_update(void);\n void stat_get_global_stats(struct statistics *stats, size_t len);\n void stat_get_stats_per_period(struct statistics *stats, size_t len);\n-void stat_frame_workload(int id, enum stat_frame_type, uint64_t cycle_number,\n-\t\t\t struct timespec start_ts);\n-void stat_inc_workload_outlier(int id, enum stat_frame_type frame_type);\n+void stat_frame_workload(enum stat_frame_type, uint64_t cycle_number, struct timespec start_ts);\n+void stat_inc_workload_outlier(enum stat_frame_type frame_type);\n void stat_frame_sent_latency(enum stat_frame_type frame_type, uint64_t seq);\n void stat_proc_first_latency(enum stat_frame_type frame_type, uint64_t cycle_number,\n \t\t\t     uint64_t tx_hw_timestamp);\n void stat_proc_batch_latency(enum stat_frame_type frame_type, uint64_t cycle_number,\n \t\t\t     uint64_t last_tx_hw_timestamp);\n-int stat_to_json(char *json, size_t len, enum stat_frame_type frame_type,\n-\t\t const struct statistics *stat, const char *tc, const char *measurement);\n+int stat_to_json(char *json, size_t len, const struct statistics *stat, const char *tc,\n+\t\t const char *measurement);\n \n extern volatile sig_atomic_t reset_stats;\n extern struct round_trip_context round_trip_contexts[NUM_FRAME_TYPES];\ndiff --git a/src/tb.c b/src/tb.c\ndeleted file mode 100644\nindex 6a57fd2..0000000\n--- a/src/tb.c\n+++ /dev/null\n@@ -1,320 +0,0 @@\n-// SPDX-License-Identifier: BSD-2-Clause\n-/*\n- * Copyright (C) 2026 Linutronix GmbH\n- */\n-\n-#include <getopt.h>\n-#include <signal.h>\n-#include <stdio.h>\n-#include <stdlib.h>\n-\n-#include <sys/mman.h>\n-\n-#include \"app_config.h\"\n-#include \"config.h\"\n-#include \"dcp_thread.h\"\n-#include \"hist.h\"\n-#include \"layer2_thread.h\"\n-#include \"lldp_thread.h\"\n-#include \"log.h\"\n-#include \"log_json.h\"\n-#include \"log_mqtt.h\"\n-#include \"print.h\"\n-#include \"rta_thread.h\"\n-#include \"rtc_thread.h\"\n-#include \"stat.h\"\n-#include \"tb.h\"\n-#include \"thread.h\"\n-#include \"tsn_thread.h\"\n-#include \"udp_thread.h\"\n-#include \"utils.h\"\n-\n-static struct option long_options[] = {\n-\t{\"config\", optional_argument, NULL, 'c'},\n-\t{\"help\", no_argument, NULL, 'h'},\n-\t{\"version\", no_argument, NULL, 'V'},\n-\t{},\n-};\n-\n-static struct log_mqtt_thread_context *log_mqtt_thread;\n-static struct log_json_thread_context *log_json_thread;\n-static struct log_thread_context *log_thread;\n-static struct thread_context *g2_threads;\n-static struct thread_context *threads;\n-\n-static void term_handler(int __unused sig)\n-{\n-\tint i;\n-\n-\tprintf(\"Stopping all application threads...\\n\");\n-\n-\tprint_stop = 1;\n-\n-\tif (log_mqtt_thread)\n-\t\tlog_mqtt_thread->stop = 1;\n-\n-\tif (log_json_thread)\n-\t\tlog_json_thread->stop = 1;\n-\n-\tif (log_thread)\n-\t\tlog_thread->stop = 1;\n-\n-\tif (g2_threads)\n-\t\tg2_threads->stop = 1;\n-\n-\tif (threads)\n-\t\tfor (i = 0; i < NUM_PN_THREAD_TYPES; i++)\n-\t\t\tthreads[i].stop = 1;\n-}\n-\n-static void reset_stats_handler(int __unused sig)\n-{\n-\treset_stats = 1;\n-}\n-\n-static void setup_signals(void)\n-{\n-\tstruct sigaction sa1, sa2;\n-\n-\tsigemptyset(&sa1.sa_mask);\n-\tsa1.sa_handler = term_handler;\n-\tsa1.sa_flags = 0;\n-\n-\tsigemptyset(&sa2.sa_mask);\n-\tsa2.sa_handler = reset_stats_handler;\n-\tsa2.sa_flags = 0;\n-\n-\tif (sigaction(SIGTERM, &sa1, NULL))\n-\t\tperror(\"sigaction() failed\");\n-\tif (sigaction(SIGINT, &sa1, NULL))\n-\t\tperror(\"sigaction() failed\");\n-\tif (sigaction(SIGUSR1, &sa2, NULL))\n-\t\tperror(\"sigaction() failed\");\n-}\n-\n-static void print_usage_and_die(const char *name)\n-{\n-\tfprintf(stderr, \"usage: %s [options]\\n\", name);\n-\tfprintf(stderr, \"  options:\\n\");\n-\tfprintf(stderr, \"    -h, --help:    Print this help text\\n\");\n-\tfprintf(stderr, \"    -V, --version: Print version\\n\");\n-\tfprintf(stderr, \"    -c, --config:  Path to config file\\n\");\n-\n-\texit(EXIT_SUCCESS);\n-}\n-\n-static void print_version_and_die(const char *name)\n-{\n-\tprintf(\"%s: version \\\"%s\\\"\\n\", name, VERSION);\n-\texit(EXIT_SUCCESS);\n-}\n-\n-void tb_startup(int argc, char *argv[], struct tb_startup_mode *mode)\n-{\n-\tbool enable_tx_latency_logging = false;\n-\tconst char *config_file = NULL;\n-\tint c, ret;\n-\n-\twhile ((c = getopt_long(argc, argv, \"c:hV\", long_options, NULL)) != -1) {\n-\t\tswitch (c) {\n-\t\tcase 'V':\n-\t\t\tprint_version_and_die(mode->binary_name);\n-\t\t\tbreak;\n-\t\tcase 'c':\n-\t\t\tconfig_file = optarg;\n-\t\t\tbreak;\n-\t\tcase 'h':\n-\t\tdefault:\n-\t\t\tprint_usage_and_die(mode->binary_name);\n-\t\t}\n-\t}\n-\n-\tret = config_set_defaults(mode->is_mirror);\n-\tif (ret) {\n-\t\tfprintf(stderr, \"Failed to set default config values!\\n\");\n-\t\texit(EXIT_FAILURE);\n-\t}\n-\n-\tif (!config_file) {\n-\t\tfprintf(stderr, \"Specifying an configuration file is mandatory. See tests/ \"\n-\t\t\t\t\"directory for examples!\\n\");\n-\t\texit(EXIT_FAILURE);\n-\t}\n-\n-\tret = config_read_from_file(config_file);\n-\tif (ret) {\n-\t\tfprintf(stderr, \"Failed to parse configuration file!\\n\");\n-\t\texit(EXIT_FAILURE);\n-\t}\n-\n-\tconfig_print_values();\n-\n-\tif (!config_sanity_check()) {\n-\t\tfprintf(stderr, \"Configuration failed sanity checks!\\n\");\n-\t\texit(EXIT_FAILURE);\n-\t}\n-\n-\tif (mlockall(MCL_CURRENT | MCL_FUTURE)) {\n-\t\tperror(\"mlockall() failed\");\n-\t\texit(EXIT_FAILURE);\n-\t}\n-\n-\tconfigure_cpu_latency();\n-\n-\tsetup_signals();\n-\n-\tret = log_init();\n-\tif (ret) {\n-\t\tfprintf(stderr, \"Failed to initialize logging!\\n\");\n-\t\texit(EXIT_FAILURE);\n-\t}\n-\n-\tif (mode->is_mirror) {\n-\t\tfor (int i = 0; i < NUM_FRAME_TYPES; i++) {\n-\t\t\tif (app_config.classes[i].tx_hwtstamp_enabled) {\n-\t\t\t\tenable_tx_latency_logging = true;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t}\n-\n-\t\tif (enable_tx_latency_logging)\n-\t\t\tmode->stat_mode = LOG_TX_TIMESTAMPS;\n-\t}\n-\n-\tif (mode->use_histogram) {\n-\t\tret = histogram_init();\n-\t\tif (ret) {\n-\t\t\tfprintf(stderr, \"Failed to initialize histogram code!\\n\");\n-\t\t\texit(EXIT_FAILURE);\n-\t\t}\n-\t}\n-\n-\tret = stat_init(mode->stat_mode);\n-\tif (ret) {\n-\t\tfprintf(stderr, \"Failed to initialize statistics!\\n\");\n-\t\texit(EXIT_FAILURE);\n-\t}\n-\n-\tlog_thread = log_thread_create();\n-\tif (!log_thread) {\n-\t\tfprintf(stderr, \"Failed to create and start Log Thread!\\n\");\n-\t\texit(EXIT_FAILURE);\n-\t}\n-\n-\tlog_mqtt_thread = log_mqtt_thread_create();\n-\tif (!log_mqtt_thread && app_config.log_mqtt) {\n-\t\tfprintf(stderr, \"Failed to create and start Log via MQTT Thread!\\n\");\n-\t\texit(EXIT_FAILURE);\n-\t}\n-\n-\tlog_json_thread = log_json_thread_create();\n-\tif (!log_json_thread && app_config.log_json) {\n-\t\tfprintf(stderr, \"Failed to create and start Log via JSON Thread!\\n\");\n-\t\texit(EXIT_FAILURE);\n-\t}\n-\n-\tg2_threads = generic_l2_threads_create();\n-\tif (!g2_threads) {\n-\t\tfprintf(stderr, \"Failed to create and start Generic L2 Threads!\\n\");\n-\t\texit(EXIT_FAILURE);\n-\t}\n-\n-\tthreads = calloc(NUM_PN_THREAD_TYPES, sizeof(struct thread_context));\n-\tif (!threads) {\n-\t\tfprintf(stderr, \"Failed to allocate PN threads!\\n\");\n-\t\texit(EXIT_FAILURE);\n-\t}\n-\n-\tif (link_pn_threads(threads)) {\n-\t\tfprintf(stderr, \"Failed to determine PN traffic classes order!\\n\");\n-\t\texit(EXIT_FAILURE);\n-\t}\n-\n-\tret = udp_low_threads_create(&threads[UDP_LOW_THREAD]);\n-\tif (ret) {\n-\t\tfprintf(stderr, \"Failed to create and start UDP Low Threads!\\n\");\n-\t\texit(EXIT_FAILURE);\n-\t}\n-\n-\tret = udp_high_threads_create(&threads[UDP_HIGH_THREAD]);\n-\tif (ret) {\n-\t\tfprintf(stderr, \"Failed to create and start UDP High Threads!\\n\");\n-\t\texit(EXIT_FAILURE);\n-\t}\n-\n-\tret = lldp_threads_create(&threads[LLDP_THREAD]);\n-\tif (ret) {\n-\t\tfprintf(stderr, \"Failed to create and start LLDP Threads!\\n\");\n-\t\texit(EXIT_FAILURE);\n-\t}\n-\n-\tret = dcp_threads_create(&threads[DCP_THREAD]);\n-\tif (ret) {\n-\t\tfprintf(stderr, \"Failed to create and start DCP Threads!\\n\");\n-\t\texit(EXIT_FAILURE);\n-\t}\n-\n-\tret = rta_threads_create(&threads[RTA_THREAD]);\n-\tif (ret) {\n-\t\tfprintf(stderr, \"Failed to create and start RTA Threads!\\n\");\n-\t\texit(EXIT_FAILURE);\n-\t}\n-\n-\tret = rtc_threads_create(&threads[RTC_THREAD]);\n-\tif (ret) {\n-\t\tfprintf(stderr, \"Failed to create and start RTC Threads!\\n\");\n-\t\texit(EXIT_FAILURE);\n-\t}\n-\n-\tret = tsn_low_threads_create(&threads[TSN_LOW_THREAD]);\n-\tif (ret) {\n-\t\tfprintf(stderr, \"Failed to create and start TSN Low Threads!\\n\");\n-\t\texit(EXIT_FAILURE);\n-\t}\n-\n-\tret = tsn_high_threads_create(&threads[TSN_HIGH_THREAD]);\n-\tif (ret) {\n-\t\tfprintf(stderr, \"Failed to create and start TSN High Threads!\\n\");\n-\t\texit(EXIT_FAILURE);\n-\t}\n-\n-\tprint_stats();\n-\n-\ttsn_high_threads_wait_for_finish(&threads[TSN_HIGH_THREAD]);\n-\ttsn_low_threads_wait_for_finish(&threads[TSN_LOW_THREAD]);\n-\trtc_threads_wait_for_finish(&threads[RTC_THREAD]);\n-\trta_threads_wait_for_finish(&threads[RTA_THREAD]);\n-\tdcp_threads_wait_for_finish(&threads[DCP_THREAD]);\n-\tlldp_threads_wait_for_finish(&threads[LLDP_THREAD]);\n-\tudp_high_threads_wait_for_finish(&threads[UDP_HIGH_THREAD]);\n-\tudp_low_threads_wait_for_finish(&threads[UDP_LOW_THREAD]);\n-\tgeneric_l2_threads_wait_for_finish(g2_threads);\n-\tlog_mqtt_thread_wait_for_finish(log_mqtt_thread);\n-\tlog_json_thread_wait_for_finish(log_json_thread);\n-\tlog_thread_wait_for_finish(log_thread);\n-\n-\tif (mode->use_histogram)\n-\t\thistogram_write();\n-\n-\ttsn_high_threads_free(&threads[TSN_HIGH_THREAD]);\n-\ttsn_low_threads_free(&threads[TSN_LOW_THREAD]);\n-\trtc_threads_free(&threads[RTC_THREAD]);\n-\trta_threads_free(&threads[RTA_THREAD]);\n-\tdcp_threads_free(&threads[DCP_THREAD]);\n-\tlldp_threads_free(&threads[LLDP_THREAD]);\n-\tudp_high_threads_free(&threads[UDP_HIGH_THREAD]);\n-\tudp_low_threads_free(&threads[UDP_LOW_THREAD]);\n-\tgeneric_l2_threads_free(g2_threads);\n-\tlog_mqtt_thread_free(log_mqtt_thread);\n-\tlog_json_thread_free(log_json_thread);\n-\tlog_thread_free(log_thread);\n-\n-\tif (mode->use_histogram)\n-\t\thistogram_free();\n-\tstat_free();\n-\tlog_free();\n-\tconfig_free();\n-\tfree(threads);\n-\n-\trestore_cpu_latency();\n-}\ndiff --git a/src/tb.h b/src/tb.h\ndeleted file mode 100644\nindex 2912285..0000000\n--- a/src/tb.h\n+++ /dev/null\n@@ -1,22 +0,0 @@\n-/* SPDX-License-Identifier: BSD-2-Clause */\n-/*\n- * Copyright (C) 2026 Linutronix GmbH\n- */\n-\n-#ifndef TB_H\n-#define TB_H\n-\n-#include <stdbool.h>\n-\n-#include \"stat.h\"\n-\n-struct tb_startup_mode {\n-\tconst char *binary_name;\n-\tenum log_stat_options stat_mode;\n-\tbool use_histogram;\n-\tbool is_mirror;\n-};\n-\n-void tb_startup(int argc, char *argv[], struct tb_startup_mode *mode);\n-\n-#endif /* TB_H */\ndiff --git a/src/thread.c b/src/thread.c\nindex a679164..5188fe8 100644\n--- a/src/thread.c\n+++ b/src/thread.c\n@@ -1,12 +1,11 @@\n // SPDX-License-Identifier: BSD-2-Clause\n /*\n- * Copyright (C) 2020-2026 Linutronix GmbH\n+ * Copyright (C) 2020,2022 Linutronix GmbH\n  * Author Kurt Kanzenbach <kurt@linutronix.de>\n  */\n \n #include <errno.h>\n #include <pthread.h>\n-#include <stdarg.h>\n #include <stdio.h>\n #include <stdlib.h>\n #include <string.h>\n@@ -16,20 +15,14 @@\n #include \"thread.h\"\n #include \"utils.h\"\n \n-int create_rt_thread(pthread_t *task_id, int thread_priority, int cpu_core,\n-\t\t     void *(*thread_routine)(void *), void *data, const char *format, ...)\n+int create_rt_thread(pthread_t *task_id, const char *thread_name, int thread_priority, int cpu_core,\n+\t\t     void *(*thread_routine)(void *), void *data)\n {\n \tstruct sched_param param;\n-\tchar thread_name[16]; /* TASK_COMM_LEN */\n \tpthread_attr_t attr;\n \tcpu_set_t cpus;\n-\tva_list args;\n \tint ret;\n \n-\tva_start(args, format);\n-\tvsnprintf(thread_name, sizeof(thread_name), format, args);\n-\tva_end(args);\n-\n \tret = pthread_attr_init(&attr);\n \tif (ret) {\n \t\tpthread_error(ret, \"pthread_attr_init() failed\");\n@@ -76,11 +69,7 @@ int create_rt_thread(pthread_t *task_id, int thread_priority, int cpu_core,\n \t\tgoto err;\n \t}\n \n-\tret = pthread_setname_np(*task_id, thread_name);\n-\tif (ret) {\n-\t\tpthread_error(ret, \"pthread_setname_np() failed\");\n-\t\tgoto err;\n-\t}\n+\tpthread_setname_np(*task_id, thread_name);\n \n \treturn 0;\n \n@@ -111,33 +100,26 @@ static struct thread_context *find_next_pn_thread(struct thread_context *pn_thre\n {\n \tswitch (start) {\n \tcase TSN_HIGH_THREAD:\n-\t\tif (config_is_traffic_class_active(TSN_LOW_FRAME_TYPE))\n+\t\tif (config_is_traffic_class_active(\"TsnLow\"))\n \t\t\treturn &pn_threads[TSN_LOW_THREAD];\n-\t\t/* fall through */\n \tcase TSN_LOW_THREAD:\n-\t\tif (config_is_traffic_class_active(RTC_FRAME_TYPE))\n+\t\tif (config_is_traffic_class_active(\"Rtc\"))\n \t\t\treturn &pn_threads[RTC_THREAD];\n-\t\t/* fall through */\n \tcase RTC_THREAD:\n-\t\tif (config_is_traffic_class_active(RTA_FRAME_TYPE))\n+\t\tif (config_is_traffic_class_active(\"Rta\"))\n \t\t\treturn &pn_threads[RTA_THREAD];\n-\t\t/* fall through */\n \tcase RTA_THREAD:\n-\t\tif (config_is_traffic_class_active(DCP_FRAME_TYPE))\n+\t\tif (config_is_traffic_class_active(\"Dcp\"))\n \t\t\treturn &pn_threads[DCP_THREAD];\n-\t\t/* fall through */\n \tcase DCP_THREAD:\n-\t\tif (config_is_traffic_class_active(LLDP_FRAME_TYPE))\n+\t\tif (config_is_traffic_class_active(\"Lldp\"))\n \t\t\treturn &pn_threads[LLDP_THREAD];\n-\t\t/* fall through */\n \tcase LLDP_THREAD:\n-\t\tif (config_is_traffic_class_active(UDP_HIGH_FRAME_TYPE))\n+\t\tif (config_is_traffic_class_active(\"UdpHigh\"))\n \t\t\treturn &pn_threads[UDP_HIGH_THREAD];\n-\t\t/* fall through */\n \tcase UDP_HIGH_THREAD:\n-\t\tif (config_is_traffic_class_active(UDP_LOW_FRAME_TYPE))\n+\t\tif (config_is_traffic_class_active(\"UdpLow\"))\n \t\t\treturn &pn_threads[UDP_LOW_THREAD];\n-\t\t/* fall through */\n \tcase UDP_LOW_THREAD:\n \t\treturn NULL;\n \t}\n@@ -169,13 +151,13 @@ int link_pn_threads(struct thread_context *pn_threads)\n \t *\n \t * GenericL2 has nothing todo with Profinet.\n \t */\n-\tif (config_is_traffic_class_active(TSN_HIGH_FRAME_TYPE)) {\n+\tif (config_is_traffic_class_active(\"TsnHigh\")) {\n \t\tpn_threads[TSN_HIGH_THREAD].is_first = true;\n \t\tt = &pn_threads[TSN_HIGH_THREAD];\n-\t} else if (config_is_traffic_class_active(RTC_FRAME_TYPE)) {\n+\t} else if (config_is_traffic_class_active(\"Rtc\")) {\n \t\tpn_threads[RTC_THREAD].is_first = true;\n \t\tt = &pn_threads[RTC_THREAD];\n-\t} else if (config_is_traffic_class_active(GENERICL2_FRAME_TYPE)) {\n+\t} else if (config_is_traffic_class_active(\"GenericL2\")) {\n \t\treturn 0;\n \t} else {\n \t\treturn -EINVAL;\ndiff --git a/src/thread.h b/src/thread.h\nindex 4e1b9e2..1e31c7c 100644\n--- a/src/thread.h\n+++ b/src/thread.h\n@@ -1,6 +1,6 @@\n /* SPDX-License-Identifier: BSD-2-Clause */\n /*\n- * Copyright (C) 2020-2026 Linutronix GmbH\n+ * Copyright (C) 2020-2024 Linutronix GmbH\n  * Author Kurt Kanzenbach <kurt@linutronix.de>\n  */\n \n@@ -18,7 +18,6 @@\n #include <linux/if_ether.h>\n \n #include \"config.h\"\n-#include \"stat.h\"\n #include \"xdp.h\"\n \n #define MAX_FRAME_SIZE XDP_FRAME_SIZE\n@@ -32,7 +31,6 @@ struct ring_buffer;\n struct xdp_socket;\n struct security_context;\n struct packet_context;\n-struct workload_config;\n \n struct thread_context {\n \t/* Task related */\n@@ -69,7 +67,6 @@ struct thread_context {\n \tstruct security_context *rx_security_context; /* Rx context for Auth and Crypt */\n \tunsigned char *payload_pattern;               /* Frame payload pattern used for AE */\n \tsize_t payload_pattern_length;                /* Length of payload pattern */\n-\tunsigned char *frame_copy;                    /* Frame copy for AE */\n \n \t/* Traffic class related */\n \tstruct traffic_class_config *conf; /* Pointer to traffic class configuration */\n@@ -95,8 +92,8 @@ enum pn_thread_type {\n \tNUM_PN_THREAD_TYPES,\n };\n \n-int create_rt_thread(pthread_t *task_id, int priority, int cpu_core,\n-\t\t     void *(*thread_routine)(void *), void *data, const char *format, ...);\n+int create_rt_thread(pthread_t *task_id, const char *thread_name, int priority, int cpu_core,\n+\t\t     void *(*thread_routine)(void *), void *data);\n void init_mutex(pthread_mutex_t *mutex);\n void init_condition_variable(pthread_cond_t *cond_var);\n int link_pn_threads(struct thread_context *pn_threads);\ndiff --git a/src/tsn_thread.c b/src/tsn_thread.c\nindex 273c530..e23ac33 100644\n--- a/src/tsn_thread.c\n+++ b/src/tsn_thread.c\n@@ -1,6 +1,6 @@\n // SPDX-License-Identifier: BSD-2-Clause\n /*\n- * Copyright (C) 2021-2026 Linutronix GmbH\n+ * Copyright (C) 2021-2025 Linutronix GmbH\n  * Author Kurt Kanzenbach <kurt@linutronix.de>\n  */\n \n@@ -28,7 +28,6 @@\n #include \"net.h\"\n #include \"net_def.h\"\n #include \"packet.h\"\n-#include \"profinet.h\"\n #include \"security.h\"\n #include \"stat.h\"\n #include \"thread.h\"\n@@ -98,10 +97,6 @@ static int tsn_send_frames(struct thread_context *thread_context, unsigned char\n \tconst struct traffic_class_config *tsn_config = thread_context->conf;\n \tint len, i;\n \n-\t/* Adjust meta data */\n-\tset_mirror_tx_timestamp(tsn_config, frame_data, tsn_config->frame_length, num_frames,\n-\t\t\t\tthread_context->meta_data_offset);\n-\n \t/* Send it */\n \tlen = tsn_send_messages(thread_context, socket_fd, destination, frame_data, num_frames,\n \t\t\t\tduration);\n@@ -120,14 +115,14 @@ static int tsn_send_frames(struct thread_context *thread_context, unsigned char\n }\n \n static int tsn_gen_and_send_frames(struct thread_context *thread_context, int socket_fd,\n-\t\t\t\t   struct sockaddr_ll *destination, uint64_t sequence_counter_begin,\n-\t\t\t\t   uint64_t duration)\n+\t\t\t\t   struct sockaddr_ll *destination, uint64_t wakeup_time,\n+\t\t\t\t   uint64_t sequence_counter_begin, uint64_t duration)\n {\n \tconst struct traffic_class_config *tsn_config = thread_context->conf;\n \tstruct timespec tx_time = {};\n \tint len, i;\n \n-\tapp_clock_get(&tx_time);\n+\tclock_gettime(app_config.application_clock_id, &tx_time);\n \n \tfor (i = 0; i < tsn_config->num_frames_per_cycle; i++) {\n \t\tstruct prepare_frame_config frame_config;\n@@ -156,15 +151,16 @@ static int tsn_gen_and_send_frames(struct thread_context *thread_context, int so\n \t\t\t\tthread_context->tx_frame_data, tsn_config->num_frames_per_cycle,\n \t\t\t\tduration);\n \n-\tif (len > 0)\n-\t\tstat_frames_sent_batch(thread_context->frame_type, sequence_counter_begin, len);\n+\tfor (i = 0; i < len; i++)\n+\t\tstat_frame_sent(thread_context->frame_type, sequence_counter_begin + i);\n \n \treturn len;\n }\n \n static void tsn_gen_and_send_xdp_frames(struct thread_context *thread_context,\n \t\t\t\t\tstruct xdp_socket *xsk, uint64_t sequence_counter,\n-\t\t\t\t\tuint64_t duration, uint32_t *frame_number)\n+\t\t\t\t\tuint64_t wakeup_time, uint64_t duration,\n+\t\t\t\t\tuint32_t *frame_number)\n {\n \tconst struct traffic_class_config *tsn_config = thread_context->conf;\n \tstruct xdp_tx_time tx_time = {\n@@ -245,8 +241,13 @@ static void *tsn_tx_thread_routine(void *data)\n \t\t\t      tsn_config->num_frames_per_cycle, source, tsn_config->l2_destination);\n \n \tprepare_openssl(security_context);\n-\ttsn_initialize_frames(thread_context, thread_context->frame_copy, 1, source,\n+\ttsn_initialize_frames(thread_context, thread_context->payload_pattern, 1, source,\n \t\t\t      tsn_config->l2_destination);\n+\tthread_context->payload_pattern +=\n+\t\tsizeof(struct vlan_ethernet_header) + sizeof(struct profinet_secure_header);\n+\tthread_context->payload_pattern_length =\n+\t\ttsn_config->frame_length - sizeof(struct vlan_ethernet_header) -\n+\t\tsizeof(struct profinet_secure_header) - sizeof(struct security_checksum);\n \n \tret = get_thread_start_time(app_config.application_tx_base_offset_ns, &wakeup_time);\n \tif (ret) {\n@@ -286,8 +287,6 @@ static void *tsn_tx_thread_routine(void *data)\n \t\t\t}\n \t\t}\n \n-\t\tworkload_check_finished(thread_context);\n-\n \t\t/*\n \t\t * Send TsnFrames, two possibilites:\n \t\t *  a) Generate it, or\n@@ -295,7 +294,7 @@ static void *tsn_tx_thread_routine(void *data)\n \t\t */\n \t\tif (!mirror_enabled) {\n \t\t\ttsn_gen_and_send_frames(thread_context, socket_fd, &destination,\n-\t\t\t\t\t\tsequence_counter, duration);\n+\t\t\t\t\t\tts_to_ns(&wakeup_time), sequence_counter, duration);\n \n \t\t\tsequence_counter += tsn_config->num_frames_per_cycle;\n \t\t} else {\n@@ -349,7 +348,6 @@ static void *tsn_xdp_tx_thread_routine(void *data)\n \txsk = thread_context->xsk;\n \txsk->tx_hwts.rtt = &round_trip_contexts[thread_context->frame_type];\n \txsk->tx_hwts.frames_per_cycle = tsn_config->num_frames_per_cycle;\n-\txsk->tx_hwts.meta_data_offset = thread_context->meta_data_offset;\n \n \tret = get_interface_mac_address(tsn_config->interface, source, ETH_ALEN);\n \tif (ret < 0) {\n@@ -376,8 +374,13 @@ static void *tsn_xdp_tx_thread_routine(void *data)\n \t\t\t      tsn_config->l2_destination);\n \n \tprepare_openssl(security_context);\n-\ttsn_initialize_frames(thread_context, thread_context->frame_copy, 1, source,\n+\ttsn_initialize_frames(thread_context, thread_context->payload_pattern, 1, source,\n \t\t\t      tsn_config->l2_destination);\n+\tthread_context->payload_pattern +=\n+\t\tsizeof(struct vlan_ethernet_header) + sizeof(struct profinet_secure_header);\n+\tthread_context->payload_pattern_length =\n+\t\ttsn_config->frame_length - sizeof(struct vlan_ethernet_header) -\n+\t\tsizeof(struct profinet_secure_header) - sizeof(struct security_checksum);\n \n \tret = get_thread_start_time(app_config.application_tx_base_offset_ns, &wakeup_time);\n \tif (ret) {\n@@ -417,19 +420,19 @@ static void *tsn_xdp_tx_thread_routine(void *data)\n \t\t\t}\n \t\t}\n \n-\t\tworkload_check_finished(thread_context);\n-\n \t\t/*\n \t\t * Send TsnFrames, two possibilites:\n \t\t *  a) Generate it, or\n \t\t *  b) Use received ones if mirror enabled\n \t\t */\n \t\tif (!mirror_enabled) {\n-\t\t\ttsn_gen_and_send_xdp_frames(thread_context, xsk, sequence_counter, duration,\n+\t\t\ttsn_gen_and_send_xdp_frames(thread_context, xsk, sequence_counter,\n+\t\t\t\t\t\t    ts_to_ns(&wakeup_time), duration,\n \t\t\t\t\t\t    &frame_number);\n \t\t\tsequence_counter += num_frames;\n \t\t} else {\n \t\t\tunsigned int received;\n+\t\t\tuint64_t i;\n \n \t\t\tpthread_mutex_lock(&thread_context->xdp_data_mutex);\n \n@@ -437,6 +440,16 @@ static void *tsn_xdp_tx_thread_routine(void *data)\n \n \t\t\tsequence_counter = thread_context->rx_sequence_counter - received;\n \n+\t\t\t/* Increment workload outlier count if workload did not finish. */\n+\t\t\tif (tsn_config->rx_workload_enabled) {\n+\t\t\t\tif (thread_context->workload->workload_done == 0 &&\n+\t\t\t\t    sequence_counter) {\n+\t\t\t\t\tstat_inc_workload_outlier(thread_context->frame_type);\n+\t\t\t\t\tlog_message(LOG_LEVEL_DEBUG, \"Workload did not finish!\\n\");\n+\t\t\t\t} else\n+\t\t\t\t\tthread_context->workload->workload_done = 0;\n+\t\t\t}\n+\n \t\t\t/*\n \t\t\t * The XDP receiver stored the frames within the umem area and populated the\n \t\t\t * Tx ring. Now, the Tx ring can be committed to the kernel. Furthermore,\n@@ -445,15 +458,38 @@ static void *tsn_xdp_tx_thread_routine(void *data)\n \n \t\t\txsk_ring_prod__submit(&xsk->tx, received);\n \n-\t\t\tif (received > 0)\n-\t\t\t\tstat_frames_sent_batch(thread_context->frame_type, sequence_counter,\n-\t\t\t\t\t\t       received);\n+\t\t\tif (received > 0) {\n+\t\t\t\tif (tsn_config->tx_hwtstamp_enabled) {\n+\t\t\t\t\t/*\n+\t\t\t\t\t * Once-per-cycle: record TX SW timestamp for the first\n+\t\t\t\t\t * packet in this cycle and count all frames\n+\t\t\t\t\t */\n+\t\t\t\t\tstat_frames_sent_batch(thread_context->frame_type,\n+\t\t\t\t\t\t\t       sequence_counter, received);\n+\t\t\t\t} else {\n+\t\t\t\t\tfor (i = sequence_counter; i < sequence_counter + received;\n+\t\t\t\t\t     ++i)\n+\t\t\t\t\t\tstat_frame_sent(thread_context->frame_type, i);\n+\t\t\t\t}\n+\t\t\t}\n \n \t\t\txsk->outstanding_tx += received;\n \t\t\tthread_context->received_frames = 0;\n \n \t\t\txdp_complete_tx(xsk);\n \n+#ifdef TX_TIMESTAMP\n+\t\t\tif (received > 0 && tsn_config->tx_hwtstamp_enabled) {\n+\t\t\t\t/*\n+\t\t\t\t * TX HW timestamp becomes available in the next cycle\n+\t\t\t\t * after the packet is transmitted.\n+\t\t\t\t * This is one cycle later than SW timestamp tracked\n+\t\t\t\t * using sequence_counter\n+\t\t\t\t */\n+\t\t\t\txsk->tx_hwts.seq_lagged = sequence_counter;\n+\t\t\t}\n+#endif\n+\n \t\t\tpthread_mutex_unlock(&thread_context->xdp_data_mutex);\n \t\t}\n \n@@ -475,8 +511,8 @@ static void *tsn_rx_thread_routine(void *data)\n {\n \tstruct thread_context *thread_context = data;\n \tconst uint64_t cycle_time_ns = app_config.application_base_cycle_time_ns;\n-\tint socket_fd, ret, received;\n \tstruct timespec wakeup_time;\n+\tint socket_fd, ret;\n \n \tsocket_fd = thread_context->socket_fd;\n \n@@ -512,9 +548,7 @@ static void *tsn_rx_thread_routine(void *data)\n \t\t}\n \n \t\t/* Receive Tsn frames. */\n-\t\treceived = packet_receive_messages(thread_context->packet_context, &recv_req);\n-\n-\t\tworkload_signal(thread_context, received);\n+\t\tpacket_receive_messages(thread_context->packet_context, &recv_req);\n \t}\n \n \treturn NULL;\n@@ -581,7 +615,13 @@ static void *tsn_xdp_rx_thread_routine(void *data)\n \t\tthread_context->received_frames = received;\n \t\tpthread_mutex_unlock(&thread_context->xdp_data_mutex);\n \n-\t\tworkload_signal(thread_context, received);\n+\t\tif (thread_context->conf->rx_workload_enabled && mirror_enabled) {\n+\t\t\t/* Run workload if we received frames or prewarm is enabled */\n+\t\t\tif (received || thread_context->conf->rx_workload_prewarm) {\n+\t\t\t\tthread_context->workload->workload_run = 1;\n+\t\t\t\tpthread_cond_signal(&(thread_context->workload->workload_cond));\n+\t\t\t}\n+\t\t}\n \t}\n \n \treturn NULL;\n@@ -590,9 +630,16 @@ static void *tsn_xdp_rx_thread_routine(void *data)\n int tsn_threads_create(struct thread_context *thread_context)\n {\n \tconst struct traffic_class_config *tsn_config = thread_context->conf;\n+\tchar thread_name[128];\n \tint ret;\n \n-\tif (!config_is_traffic_class_active(thread_context->frame_type)) {\n+\tif (thread_context->frame_type == TSN_HIGH_FRAME_TYPE &&\n+\t    !config_is_traffic_class_active(\"TsnHigh\")) {\n+\t\tret = 0;\n+\t\tgoto out;\n+\t}\n+\tif (thread_context->frame_type == TSN_LOW_FRAME_TYPE &&\n+\t    !config_is_traffic_class_active(\"TsnLow\")) {\n \t\tret = 0;\n \t\tgoto out;\n \t}\n@@ -623,20 +670,13 @@ int tsn_threads_create(struct thread_context *thread_context)\n \t\t}\n \t}\n \n-\t/* Initialize data structures for AE */\n-\tthread_context->frame_copy = calloc(1, MAX_FRAME_SIZE);\n-\tif (!thread_context->frame_copy) {\n-\t\tfprintf(stderr, \"Failed to allocate TsnPayloadPattern for AE!\\n\");\n+\tthread_context->payload_pattern = calloc(1, MAX_FRAME_SIZE);\n+\tif (!thread_context->payload_pattern) {\n+\t\tfprintf(stderr, \"Failed to allocate TsnPayloadPattern!\\n\");\n \t\tret = -ENOMEM;\n \t\tgoto err_payload;\n \t}\n-\n-\tthread_context->payload_pattern = thread_context->frame_copy +\n-\t\t\t\t\t  sizeof(struct vlan_ethernet_header) +\n-\t\t\t\t\t  sizeof(struct profinet_secure_header);\n-\tthread_context->payload_pattern_length =\n-\t\ttsn_config->frame_length - sizeof(struct vlan_ethernet_header) -\n-\t\tsizeof(struct profinet_secure_header) - sizeof(struct security_checksum);\n+\tthread_context->payload_pattern_length = MAX_FRAME_SIZE;\n \n \t/* For XDP a AF_XDP socket is allocated. Otherwise a Linux raw socket is used. */\n \tif (tsn_config->xdp_enabled) {\n@@ -698,40 +738,62 @@ int tsn_threads_create(struct thread_context *thread_context)\n \t\tthread_context->rx_security_context = NULL;\n \t}\n \n-\tthread_context->meta_data_offset =\n-\t\tget_meta_data_offset(thread_context->frame_type, tsn_config->security_mode);\n+\tsnprintf(thread_name, sizeof(thread_name), \"%sTxThread\", thread_context->traffic_class);\n \n-\tret = create_rt_thread(&thread_context->tx_task_id, tsn_config->tx_thread_priority,\n-\t\t\t       tsn_config->tx_thread_cpu,\n+\tret = create_rt_thread(&thread_context->tx_task_id, thread_name,\n+\t\t\t       tsn_config->tx_thread_priority, tsn_config->tx_thread_cpu,\n \t\t\t       tsn_config->xdp_enabled ? tsn_xdp_tx_thread_routine\n \t\t\t\t\t\t       : tsn_tx_thread_routine,\n-\t\t\t       thread_context, \"%sTxThread\", thread_context->traffic_class);\n+\t\t\t       thread_context);\n \tif (ret) {\n \t\tfprintf(stderr, \"Failed to create Tsn Tx Thread!\\n\");\n \t\tgoto err_thread;\n \t}\n \n-\tret = create_rt_thread(&thread_context->rx_task_id, tsn_config->rx_thread_priority,\n-\t\t\t       tsn_config->rx_thread_cpu,\n+\tsnprintf(thread_name, sizeof(thread_name), \"%sRxThread\", thread_context->traffic_class);\n+\n+\tret = create_rt_thread(&thread_context->rx_task_id, thread_name,\n+\t\t\t       tsn_config->rx_thread_priority, tsn_config->rx_thread_cpu,\n \t\t\t       tsn_config->xdp_enabled ? tsn_xdp_rx_thread_routine\n \t\t\t\t\t\t       : tsn_rx_thread_routine,\n-\t\t\t       thread_context, \"%sRxThread\", thread_context->traffic_class);\n+\t\t\t       thread_context);\n \tif (ret) {\n \t\tfprintf(stderr, \"Failed to create Tsn Rx Thread!\\n\");\n \t\tgoto err_thread_rx;\n \t}\n \n \t/* Create workload thread for execution after network RX */\n-\tret = workload_context_init(thread_context);\n-\tif (ret) {\n-\t\tfprintf(stderr, \"Failed to create Tsn Workload context!\\n\");\n-\t\tgoto err_thread_wl;\n+\tif (tsn_config->rx_workload_enabled) {\n+\t\tthread_context->workload = calloc(1, sizeof(struct workload_config));\n+\n+\t\tif (!thread_context->workload) {\n+\t\t\tfprintf(stderr, \"Failed to allocate workload!\\n\");\n+\t\t\tret = -ENOMEM;\n+\t\t\tgoto err_thread_wl;\n+\t\t}\n+\n+\t\tworkload_context_init(\n+\t\t\tthread_context, tsn_config->workload_file, tsn_config->workload_function,\n+\t\t\ttsn_config->workload_arguments, tsn_config->workload_setup_function,\n+\t\t\ttsn_config->workload_setup_arguments, thread_context->frame_type);\n+\t\tret = create_rt_thread(\n+\t\t\t&thread_context->workload->workload_task_id, tsn_config->workload_function,\n+\t\t\ttsn_config->workload_thread_priority, tsn_config->workload_thread_cpu,\n+\t\t\t&workload_thread_routine, thread_context);\n+\t\tif (ret) {\n+\t\t\tfprintf(stderr, \"Failed to create Tsn Workload Thread!\\n\");\n+\t\t\tgoto err_thread_wl;\n+\t\t}\n \t}\n \n+\tthread_context->meta_data_offset =\n+\t\tget_meta_data_offset(thread_context->frame_type, tsn_config->security_mode);\n+\n \treturn 0;\n \n err_thread_wl:\n \tthread_context->stop = 1;\n+\tfree(thread_context->workload);\n \tpthread_join(thread_context->rx_task_id, NULL);\n err_thread_rx:\n \tthread_context->stop = 1;\n@@ -749,7 +811,7 @@ err_buffer:\n \t\txdp_close_socket(thread_context->xsk, tsn_config->interface,\n \t\t\t\t tsn_config->xdp_skb_mode);\n err_socket:\n-\tfree(thread_context->frame_copy);\n+\tfree(thread_context->payload_pattern);\n err_payload:\n \tfree(thread_context->rx_frame_data);\n err_rx:\n@@ -770,7 +832,11 @@ static void tsn_threads_free(struct thread_context *thread_context)\n \n \ttsn_config = thread_context->conf;\n \n-\tfree(thread_context->frame_copy);\n+\tif (thread_context->payload_pattern) {\n+\t\tthread_context->payload_pattern -=\n+\t\t\tsizeof(struct vlan_ethernet_header) + sizeof(struct profinet_secure_header);\n+\t\tfree(thread_context->payload_pattern);\n+\t}\n \n \tsecurity_exit(thread_context->tx_security_context);\n \tsecurity_exit(thread_context->rx_security_context);\n@@ -788,7 +854,8 @@ static void tsn_threads_free(struct thread_context *thread_context)\n \t\txdp_close_socket(thread_context->xsk, tsn_config->interface,\n \t\t\t\t tsn_config->xdp_skb_mode);\n \n-\tworkload_thread_free(thread_context);\n+\tif (thread_context->workload != NULL && thread_context->workload->workload_task_id)\n+\t\tworkload_thread_free(thread_context);\n }\n \n static void tsn_threads_wait_for_finish(struct thread_context *thread_context)\n@@ -796,8 +863,8 @@ static void tsn_threads_wait_for_finish(struct thread_context *thread_context)\n \tif (!thread_context)\n \t\treturn;\n \n-\tworkload_thread_wait_for_finish(thread_context);\n-\n+\tif (thread_context->workload != NULL && thread_context->workload->workload_task_id)\n+\t\tpthread_join(thread_context->workload->workload_task_id, NULL);\n \tif (thread_context->rx_task_id)\n \t\tpthread_join(thread_context->rx_task_id, NULL);\n \tif (thread_context->tx_task_id)\ndiff --git a/src/tx_time.c b/src/tx_time.c\nindex 0bb0849..7385bbd 100644\n--- a/src/tx_time.c\n+++ b/src/tx_time.c\n@@ -41,7 +41,7 @@ uint64_t tx_time_get_frame_tx_time(uint64_t sequence_counter, uint64_t duration,\n \t * All calculations are performed in nanoseconds.\n \t */\n \n-\tapp_clock_get(&now);\n+\tclock_gettime(app_config.application_clock_id, &now);\n \tnow_ns = ts_to_ns(&now);\n \tpast_cycles = (now_ns - app_config.application_base_start_time_ns) / cycle_time;\n \tbase_time = (past_cycles + 1) * cycle_time + app_config.application_base_start_time_ns;\ndiff --git a/src/udp_thread.c b/src/udp_thread.c\nindex bcdd000..7d092c3 100644\n--- a/src/udp_thread.c\n+++ b/src/udp_thread.c\n@@ -1,6 +1,6 @@\n // SPDX-License-Identifier: BSD-2-Clause\n /*\n- * Copyright (C) 2020-2026 Linutronix GmbH\n+ * Copyright (C) 2020-2024 Linutronix GmbH\n  * Author Kurt Kanzenbach <kurt@linutronix.de>\n  */\n \n@@ -56,7 +56,7 @@ static void udp_send_frame(struct thread_context *thread_context, const unsigned\n \tuint64_t sequence_counter;\n \tssize_t ret = -1;\n \n-\tapp_clock_get(&tx_time);\n+\tclock_gettime(app_config.application_clock_id, &tx_time);\n \n \t/* Fetch meta data */\n \tmeta = (struct reference_meta_data *)frame_data;\n@@ -74,8 +74,6 @@ static void udp_send_frame(struct thread_context *thread_context, const unsigned\n \t\tret = sendto(socket_fd, frame_data, frame_length, 0,\n \t\t\t     (struct sockaddr_in6 *)destination, sizeof(struct sockaddr_in6));\n \t\tbreak;\n-\tdefault:\n-\t\tret = -EINVAL;\n \t}\n \tif (ret < 0) {\n \t\tlog_message(LOG_LEVEL_ERROR, \"%sTx: send() for %\" PRIu64 \" failed: %s\\n\",\n@@ -95,7 +93,7 @@ static void udp_gen_and_send_frame(struct thread_context *thread_context, unsign\n \tstruct timespec tx_time = {};\n \tssize_t ret = -1;\n \n-\tapp_clock_get(&tx_time);\n+\tclock_gettime(app_config.application_clock_id, &tx_time);\n \n \t/* Adjust meta data */\n \tmeta = (struct reference_meta_data *)frame_data;\n@@ -262,12 +260,12 @@ static void *udp_rx_thread_routine(void *data)\n \t\t\tif (len == -1 && errno != EAGAIN && errno != EWOULDBLOCK) {\n \t\t\t\tlog_message(LOG_LEVEL_ERROR, \"%sRx: recv() failed: %s\\n\",\n \t\t\t\t\t    thread_context->traffic_class, strerror(errno));\n-\t\t\t\tbreak;\n+\t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (len == 0)\n \t\t\t\tcontinue;\n \n-\t\t\t/* No more frames. Come back within next period. */\n+\t\t\t/* No more frames. Comeback within next period. */\n \t\t\tif (len == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))\n \t\t\t\tbreak;\n \n@@ -288,7 +286,7 @@ static void *udp_rx_thread_routine(void *data)\n \t\t\t\tmeta_data_to_sequence_counter(meta, num_frames_per_cycle);\n \n \t\t\ttx_timestamp = meta_data_to_tx_timestamp(meta);\n-\t\t\tset_mirror_tx_timestamp_est(meta);\n+\t\t\tset_mirror_tx_timestamp(meta);\n \n \t\t\tout_of_order = sequence_counter != rx_sequence_counter;\n \t\t\tpayload_mismatch = memcmp(frame + sizeof(struct reference_meta_data),\n@@ -382,9 +380,16 @@ static void *udp_tx_generation_thread_routine(void *data)\n static int udp_threads_create(struct thread_context *thread_context)\n {\n \tconst struct traffic_class_config *udp_config = thread_context->conf;\n+\tchar thread_name[128];\n \tint ret;\n \n-\tif (!config_is_traffic_class_active(thread_context->frame_type)) {\n+\tif (thread_context->frame_type == UDP_HIGH_FRAME_TYPE &&\n+\t    !config_is_traffic_class_active(\"UdpHigh\")) {\n+\t\tret = 0;\n+\t\tgoto out;\n+\t}\n+\tif (thread_context->frame_type == UDP_LOW_FRAME_TYPE &&\n+\t    !config_is_traffic_class_active(\"UdpLow\")) {\n \t\tret = 0;\n \t\tgoto out;\n \t}\n@@ -426,28 +431,33 @@ static int udp_threads_create(struct thread_context *thread_context)\n \t\t}\n \t}\n \n-\tret = create_rt_thread(&thread_context->tx_task_id, udp_config->tx_thread_priority,\n-\t\t\t       udp_config->tx_thread_cpu, udp_tx_thread_routine, thread_context,\n-\t\t\t       \"%sTxThread\", thread_context->traffic_class);\n+\tsnprintf(thread_name, sizeof(thread_name), \"%sTxThread\", thread_context->traffic_class);\n+\n+\tret = create_rt_thread(&thread_context->tx_task_id, thread_name,\n+\t\t\t       udp_config->tx_thread_priority, udp_config->tx_thread_cpu,\n+\t\t\t       udp_tx_thread_routine, thread_context);\n \tif (ret) {\n \t\tfprintf(stderr, \"Failed to create Udp Tx Thread!\\n\");\n \t\tgoto err_thread;\n \t}\n \n+\tsnprintf(thread_name, sizeof(thread_name), \"%sTxGenThread\", thread_context->traffic_class);\n+\n \tif (!udp_config->rx_mirror_enabled) {\n-\t\tret = create_rt_thread(&thread_context->tx_gen_task_id,\n+\t\tret = create_rt_thread(&thread_context->tx_gen_task_id, \"UdpLowTxGenThread\",\n \t\t\t\t       udp_config->tx_thread_priority, udp_config->tx_thread_cpu,\n-\t\t\t\t       udp_tx_generation_thread_routine, thread_context,\n-\t\t\t\t       \"%sTxGenThread\", thread_context->traffic_class);\n+\t\t\t\t       udp_tx_generation_thread_routine, thread_context);\n \t\tif (ret) {\n \t\t\tfprintf(stderr, \"Failed to create Udp TxGen Thread!\\n\");\n \t\t\tgoto err_thread_txgen;\n \t\t}\n \t}\n \n-\tret = create_rt_thread(&thread_context->rx_task_id, udp_config->rx_thread_priority,\n-\t\t\t       udp_config->rx_thread_cpu, udp_rx_thread_routine, thread_context,\n-\t\t\t       \"%sRxThread\", thread_context->traffic_class);\n+\tsnprintf(thread_name, sizeof(thread_name), \"%sRxThread\", thread_context->traffic_class);\n+\n+\tret = create_rt_thread(&thread_context->rx_task_id, thread_name,\n+\t\t\t       udp_config->rx_thread_priority, udp_config->rx_thread_cpu,\n+\t\t\t       udp_rx_thread_routine, thread_context);\n \tif (ret) {\n \t\tfprintf(stderr, \"Failed to create Udp Rx Thread!\\n\");\n \t\tgoto err_thread_rx;\ndiff --git a/src/utils.c b/src/utils.c\nindex 686224c..dc2ce8c 100644\n--- a/src/utils.c\n+++ b/src/utils.c\n@@ -1,6 +1,6 @@\n // SPDX-License-Identifier: BSD-2-Clause\n /*\n- * Copyright (C) 2020-2026 Linutronix GmbH\n+ * Copyright (C) 2020-2024 Linutronix GmbH\n  * Author Kurt Kanzenbach <kurt@linutronix.de>\n  */\n \n@@ -58,7 +58,7 @@ void swap_mac_addresses(void *buffer, size_t len)\n \tmemcpy(eth->h_dest, tmp, sizeof(tmp));\n }\n \n-void insert_vlan_tag(void *buffer, size_t len, uint16_t ether_type, uint16_t vlan_tci)\n+void insert_vlan_tag(void *buffer, size_t len, uint16_t vlan_tci)\n {\n \tstruct vlan_ethernet_header *veth;\n \n@@ -70,7 +70,7 @@ void insert_vlan_tag(void *buffer, size_t len, uint16_t ether_type, uint16_t vla\n \n \tveth = buffer;\n \n-\tveth->vlan_encapsulated_proto = htons(ether_type);\n+\tveth->vlan_encapsulated_proto = htons(ETH_P_PROFINET_RT);\n \tveth->vlan_proto = htons(ETH_P_8021Q);\n \tveth->vlantci = htons(vlan_tci);\n }\n@@ -101,6 +101,360 @@ void build_vlan_frame_from_rx(const unsigned char *old_frame, size_t old_frame_l\n \teth_new->vlan_encapsulated_proto = htons(ether_type);\n }\n \n+static void initialize_secure_profinet_frame(enum security_mode mode, unsigned char *frame_data,\n+\t\t\t\t\t     size_t frame_length, const unsigned char *source,\n+\t\t\t\t\t     const unsigned char *destination,\n+\t\t\t\t\t     const char *payload_pattern,\n+\t\t\t\t\t     size_t payload_pattern_length, uint16_t vlan_tci,\n+\t\t\t\t\t     uint16_t frame_id)\n+{\n+\tstruct profinet_secure_header *rt;\n+\tstruct vlan_ethernet_header *eth;\n+\tuint16_t security_length;\n+\tsize_t payload_offset;\n+\n+\t/* Initialize to zero */\n+\tmemset(frame_data, '\\0', frame_length);\n+\n+\t/*\n+\t * Profinet Frame:\n+\t *   Destination\n+\t *   Source\n+\t *   VLAN tag: tpid 8100, id 0x00/0x101/102, dei 0, prio 6/5/4/3/2\n+\t *   Ether type: 8892\n+\t *   Frame id: TSN, RTC, RTA, DCP\n+\t *   SecurityHeader\n+\t *   Cycle counter\n+\t *   Payload\n+\t *   Padding to maxFrame - Checksum\n+\t *   security_checksum\n+\t */\n+\n+\teth = (struct vlan_ethernet_header *)frame_data;\n+\trt = (struct profinet_secure_header *)(frame_data + sizeof(*eth));\n+\n+\t/* Ethernet header */\n+\tmemcpy(eth->destination, destination, ETH_ALEN);\n+\tmemcpy(eth->source, source, ETH_ALEN);\n+\n+\t/* VLAN Header */\n+\teth->vlan_proto = htons(ETH_P_8021Q);\n+\teth->vlantci = htons(vlan_tci);\n+\teth->vlan_encapsulated_proto = htons(ETH_P_PROFINET_RT);\n+\n+\t/* Profinet Secure header */\n+\tsecurity_length = frame_length - sizeof(*eth) - sizeof(struct security_checksum);\n+\trt->frame_id = htons(frame_id);\n+\trt->security_meta_data.security_information = mode == SECURITY_MODE_AO ? 0x00 : 0x01;\n+\trt->security_meta_data.security_length = htobe16(security_length);\n+\trt->meta_data.frame_counter = 0;\n+\trt->meta_data.cycle_counter = 0;\n+\n+\t/* Payload */\n+\tpayload_offset = sizeof(*eth) + sizeof(*rt);\n+\tmemcpy(frame_data + payload_offset, payload_pattern, payload_pattern_length);\n+\n+\t/* security_checksum is set to zero and calculated for each frame on Tx */\n+}\n+\n+static void initialize_rt_profinet_frame(unsigned char *frame_data, size_t frame_length,\n+\t\t\t\t\t const unsigned char *source,\n+\t\t\t\t\t const unsigned char *destination,\n+\t\t\t\t\t const char *payload_pattern, size_t payload_pattern_length,\n+\t\t\t\t\t uint16_t vlan_tci, uint16_t frame_id)\n+{\n+\tstruct vlan_ethernet_header *eth;\n+\tstruct profinet_rt_header *rt;\n+\tsize_t payload_offset;\n+\n+\t/* Initialize to zero */\n+\tmemset(frame_data, '\\0', frame_length);\n+\n+\t/*\n+\t * Profinet Frame:\n+\t *   Destination\n+\t *   Source\n+\t *   VLAN tag: tpid 8100, id 0x00/0x101/102, dei 0, prio 6/5/4/3/2\n+\t *   Ether type: 8892\n+\t *   Frame id: TSN, RTC, RTA, DCP\n+\t *   Cycle counter\n+\t *   Payload\n+\t *   Padding to maxFrame\n+\t */\n+\n+\teth = (struct vlan_ethernet_header *)frame_data;\n+\trt = (struct profinet_rt_header *)(frame_data + sizeof(*eth));\n+\n+\t/* Ethernet header */\n+\tmemcpy(eth->destination, destination, ETH_ALEN);\n+\tmemcpy(eth->source, source, ETH_ALEN);\n+\n+\t/* VLAN Header */\n+\teth->vlan_proto = htons(ETH_P_8021Q);\n+\teth->vlantci = htons(vlan_tci);\n+\teth->vlan_encapsulated_proto = htons(ETH_P_PROFINET_RT);\n+\n+\t/* Profinet RT header */\n+\trt->frame_id = htons(frame_id);\n+\trt->meta_data.frame_counter = 0;\n+\trt->meta_data.cycle_counter = 0;\n+\n+\t/* Payload */\n+\tpayload_offset = sizeof(*eth) + sizeof(*rt);\n+\tmemcpy(frame_data + payload_offset, payload_pattern, payload_pattern_length);\n+}\n+\n+void initialize_profinet_frame(enum security_mode mode, unsigned char *frame_data,\n+\t\t\t       size_t frame_length, const unsigned char *source,\n+\t\t\t       const unsigned char *destination, const char *payload_pattern,\n+\t\t\t       size_t payload_pattern_length, uint16_t vlan_tci, uint16_t frame_id)\n+{\n+\tswitch (mode) {\n+\tcase SECURITY_MODE_NONE:\n+\t\tinitialize_rt_profinet_frame(frame_data, frame_length, source, destination,\n+\t\t\t\t\t     payload_pattern, payload_pattern_length, vlan_tci,\n+\t\t\t\t\t     frame_id);\n+\t\tbreak;\n+\tcase SECURITY_MODE_AE:\n+\tcase SECURITY_MODE_AO:\n+\t\tinitialize_secure_profinet_frame(mode, frame_data, frame_length, source,\n+\t\t\t\t\t\t destination, payload_pattern,\n+\t\t\t\t\t\t payload_pattern_length, vlan_tci, frame_id);\n+\t\tbreak;\n+\t}\n+}\n+\n+int receive_profinet_frame(void *data, unsigned char *frame_data, size_t len)\n+{\n+\tstruct thread_context *thread_context = data;\n+\tconst struct traffic_class_config *class_config = thread_context->conf;\n+\tconst unsigned char *expected_pattern =\n+\t\t(const unsigned char *)class_config->payload_pattern;\n+\tstruct security_context *security_context = thread_context->rx_security_context;\n+\tconst size_t expected_pattern_length = class_config->payload_pattern_length;\n+\tconst bool mirror_enabled = class_config->rx_mirror_enabled;\n+\tconst bool ignore_rx_errors = class_config->ignore_rx_errors;\n+\tsize_t expected_frame_length = class_config->frame_length;\n+\tuint64_t tx_timestamp, rx_hw_timestamp, rx_sw_timestamp;\n+\tbool out_of_order, payload_mismatch, frame_id_mismatch;\n+\tunsigned char plaintext[MAX_FRAME_SIZE];\n+\tunsigned char new_frame[MAX_FRAME_SIZE];\n+\tstruct profinet_secure_header *srt;\n+\tstruct profinet_rt_header *rt;\n+\tuint64_t sequence_counter;\n+\tbool vlan_tag_missing;\n+\tvoid *p = frame_data;\n+\tstruct ethhdr *eth;\n+\tuint16_t frame_id;\n+\tuint16_t proto;\n+\n+\tif (len < sizeof(struct vlan_ethernet_header)) {\n+\t\tlog_message(LOG_LEVEL_WARNING, \"%sRx: Too small frame received!\\n\",\n+\t\t\t    thread_context->traffic_class);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\teth = p;\n+\tif (eth->h_proto == htons(ETH_P_8021Q)) {\n+\t\tstruct vlan_ethernet_header *veth = p;\n+\n+\t\tproto = veth->vlan_encapsulated_proto;\n+\t\tp += sizeof(*veth);\n+\t\tvlan_tag_missing = false;\n+\t} else {\n+\t\tproto = eth->h_proto;\n+\t\tp += sizeof(*eth);\n+\t\texpected_frame_length -= sizeof(struct vlan_header);\n+\t\tvlan_tag_missing = true;\n+\t}\n+\n+\tif (proto != htons(ETH_P_PROFINET_RT)) {\n+\t\tlog_message(LOG_LEVEL_WARNING, \"%sRx: Not a Profinet frame received!\\n\",\n+\t\t\t    thread_context->traffic_class);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/* Check frame length: VLAN tag might be stripped or not. Check it. */\n+\tif (len != expected_frame_length) {\n+\t\tlog_message(LOG_LEVEL_WARNING, \"%sRx: Frame with wrong length received!\\n\",\n+\t\t\t    thread_context->traffic_class);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/* Check cycle counter, frame id range and payload. */\n+\tif (class_config->security_mode == SECURITY_MODE_NONE) {\n+\t\trt = p;\n+\t\tp += sizeof(*rt);\n+\n+\t\tframe_id = be16toh(rt->frame_id);\n+\t\tsequence_counter = meta_data_to_sequence_counter(\n+\t\t\t&rt->meta_data, class_config->num_frames_per_cycle);\n+\n+\t\ttx_timestamp = meta_data_to_tx_timestamp(&rt->meta_data);\n+\t\tset_mirror_tx_timestamp(&rt->meta_data);\n+\n+\t} else if (class_config->security_mode == SECURITY_MODE_AO) {\n+\n+\t\tunsigned char *begin_of_security_checksum;\n+\t\tunsigned char *begin_of_aad_data;\n+\t\tsize_t size_of_eth_header;\n+\t\tsize_t size_of_aad_data;\n+\t\tstruct security_iv iv;\n+\t\tint ret;\n+\n+\t\tsrt = p;\n+\t\tp += sizeof(*srt);\n+\n+\t\tframe_id = be16toh(srt->frame_id);\n+\t\tsequence_counter = meta_data_to_sequence_counter(\n+\t\t\t&srt->meta_data, class_config->num_frames_per_cycle);\n+\n+\t\ttx_timestamp = meta_data_to_tx_timestamp(&srt->meta_data);\n+\n+\t\t/* Authenticate received Profinet Frame */\n+\t\tsize_of_eth_header = vlan_tag_missing ? sizeof(struct ethhdr)\n+\t\t\t\t\t\t      : sizeof(struct vlan_ethernet_header);\n+\n+\t\tbegin_of_aad_data = frame_data + size_of_eth_header;\n+\t\tsize_of_aad_data = len - size_of_eth_header - sizeof(struct security_checksum);\n+\t\tbegin_of_security_checksum = frame_data + (len - sizeof(struct security_checksum));\n+\n+\t\tprepare_iv((const unsigned char *)class_config->security_iv_prefix,\n+\t\t\t   sequence_counter, &iv);\n+\n+\t\tret = security_decrypt(security_context, NULL, 0, begin_of_aad_data,\n+\t\t\t\t       size_of_aad_data, begin_of_security_checksum,\n+\t\t\t\t       (unsigned char *)&iv, NULL);\n+\t\tif (ret)\n+\t\t\tlog_message(LOG_LEVEL_WARNING,\n+\t\t\t\t    \"%sRx: frame[%\" PRIu64 \"] Not authentificated\\n\",\n+\t\t\t\t    thread_context->traffic_class, sequence_counter);\n+\n+\t\tset_mirror_tx_timestamp(&srt->meta_data);\n+\n+\t\tsecurity_encrypt(security_context, NULL, 0, begin_of_aad_data, size_of_aad_data,\n+\t\t\t\t (unsigned char *)&iv, NULL, begin_of_security_checksum);\n+\n+\t} else {\n+\t\tunsigned char *begin_of_security_checksum;\n+\t\tunsigned char *begin_of_ciphertext;\n+\t\tunsigned char *begin_of_aad_data;\n+\t\tsize_t size_of_ciphertext;\n+\t\tsize_t size_of_eth_header;\n+\t\tsize_t size_of_aad_data;\n+\t\tstruct security_iv iv;\n+\t\tint ret;\n+\n+\t\tsrt = p;\n+\n+\t\tframe_id = be16toh(srt->frame_id);\n+\t\tsequence_counter = meta_data_to_sequence_counter(\n+\t\t\t&srt->meta_data, class_config->num_frames_per_cycle);\n+\n+\t\ttx_timestamp = meta_data_to_tx_timestamp(&srt->meta_data);\n+\n+\t\t/* Authenticate received Profinet Frame */\n+\t\tsize_of_eth_header = vlan_tag_missing ? sizeof(struct ethhdr)\n+\t\t\t\t\t\t      : sizeof(struct vlan_ethernet_header);\n+\n+\t\tbegin_of_aad_data = frame_data + size_of_eth_header;\n+\t\tsize_of_aad_data = sizeof(*srt);\n+\t\tbegin_of_security_checksum = frame_data + (len - sizeof(struct security_checksum));\n+\t\tbegin_of_ciphertext = frame_data + size_of_eth_header + sizeof(*srt);\n+\t\tsize_of_ciphertext = len - sizeof(struct vlan_ethernet_header) -\n+\t\t\t\t     sizeof(struct profinet_secure_header) -\n+\t\t\t\t     sizeof(struct security_checksum);\n+\n+\t\tprepare_iv((const unsigned char *)class_config->security_iv_prefix,\n+\t\t\t   sequence_counter, &iv);\n+\n+\t\tret = security_decrypt(security_context, begin_of_ciphertext, size_of_ciphertext,\n+\t\t\t\t       begin_of_aad_data, size_of_aad_data,\n+\t\t\t\t       begin_of_security_checksum, (unsigned char *)&iv, plaintext);\n+\t\tif (ret)\n+\t\t\tlog_message(LOG_LEVEL_WARNING,\n+\t\t\t\t    \"%sRx: frame[%\" PRIu64 \"] Not authentificated and decrypted\\n\",\n+\t\t\t\t    thread_context->traffic_class, sequence_counter);\n+\n+\t\t/* plaintext points to the decrypted payload */\n+\t\tp = plaintext;\n+\n+\t\tset_mirror_tx_timestamp(&srt->meta_data);\n+\n+\t\tsecurity_encrypt(security_context, thread_context->payload_pattern,\n+\t\t\t\t thread_context->payload_pattern_length, begin_of_aad_data,\n+\t\t\t\t size_of_aad_data, (unsigned char *)&iv, begin_of_ciphertext,\n+\t\t\t\t begin_of_security_checksum);\n+\t}\n+\n+\txdp_get_timestamp_metadata(frame_data, &rx_hw_timestamp, &rx_sw_timestamp);\n+\tout_of_order = sequence_counter != thread_context->rx_sequence_counter;\n+\tpayload_mismatch = memcmp(p, expected_pattern, expected_pattern_length);\n+\tframe_id_mismatch = frame_id != thread_context->frame_id;\n+\n+\tstat_frame_received(thread_context->frame_type, sequence_counter, out_of_order,\n+\t\t\t    payload_mismatch, frame_id_mismatch, tx_timestamp, rx_hw_timestamp,\n+\t\t\t    rx_sw_timestamp);\n+\n+\tif (frame_id_mismatch)\n+\t\tlog_message(\n+\t\t\tLOG_LEVEL_WARNING, \"%sRx: frame[%\" PRIu64 \"] FrameId mismatch: 0x%4x!\\n\",\n+\t\t\tthread_context->traffic_class, sequence_counter, thread_context->frame_id);\n+\n+\tif (out_of_order) {\n+\t\tif (!ignore_rx_errors)\n+\t\t\tlog_message(LOG_LEVEL_WARNING,\n+\t\t\t\t    \"%sRx: frame[%\" PRIu64 \"] SequenceCounter mismatch: %\" PRIu64\n+\t\t\t\t    \"!\\n\",\n+\t\t\t\t    thread_context->traffic_class, sequence_counter,\n+\t\t\t\t    thread_context->rx_sequence_counter);\n+\t\tthread_context->rx_sequence_counter++;\n+\t}\n+\n+\tif (payload_mismatch)\n+\t\tlog_message(LOG_LEVEL_WARNING,\n+\t\t\t    \"%sRx: frame[%\" PRIu64 \"] Payload Pattern mismatch!\\n\",\n+\t\t\t    thread_context->traffic_class, sequence_counter);\n+\n+\tthread_context->rx_sequence_counter++;\n+\n+\t/*\n+\t * If mirror enabled, assemble and store the frame for Tx later.\n+\t *\n+\t * In case of XDP the Rx umem area will be reused for Tx.\n+\t */\n+\tif (!mirror_enabled)\n+\t\treturn 0;\n+\n+\tif (class_config->xdp_enabled) {\n+\t\t/* Re-add vlan tag */\n+\t\tif (vlan_tag_missing)\n+\t\t\tinsert_vlan_tag(frame_data, len,\n+\t\t\t\t\tclass_config->vid | class_config->pcp << VLAN_PCP_SHIFT);\n+\n+\t\t/* Swap mac addresses inline */\n+\t\tswap_mac_addresses(frame_data, len);\n+\t} else {\n+\t\t/* Build new frame for Tx with VLAN info. */\n+\t\tbuild_vlan_frame_from_rx(frame_data, len, new_frame, sizeof(new_frame),\n+\t\t\t\t\t ETH_P_PROFINET_RT,\n+\t\t\t\t\t class_config->vid | class_config->pcp << VLAN_PCP_SHIFT);\n+\n+\t\t/* Store the new frame. */\n+\t\tring_buffer_add(thread_context->mirror_buffer, new_frame,\n+\t\t\t\tlen + sizeof(struct vlan_header));\n+\t}\n+\n+\t/* RTA is a burst traffic class and needs to update num_frames_available */\n+\tif (thread_context->frame_type == RTA_FRAME_TYPE) {\n+\t\tpthread_mutex_lock(&thread_context->data_mutex);\n+\t\tthread_context->num_frames_available++;\n+\t\tpthread_mutex_unlock(&thread_context->data_mutex);\n+\t}\n+\n+\treturn 0;\n+}\n+\n int prepare_frame_for_tx(const struct prepare_frame_config *frame_config)\n {\n \t/* mode == NONE may be called from PROFINET or GenericL2 */\n@@ -200,7 +554,7 @@ void prepare_iv(const unsigned char *iv_prefix, uint64_t sequence_counter, struc\n \n void prepare_openssl(struct security_context *context)\n {\n-\tunsigned char iv[SECURITY_IV_LEN + 1] = \"012345678901\";\n+\tunsigned char iv[SECURITY_IV_LEN] = \"012345678901\";\n \tunsigned char dummy_frame[2048] = {5};\n \n \tif (!context)\n@@ -246,7 +600,7 @@ void configure_cpu_latency(void)\n \n \tret = write(fd, &lat, sizeof(lat));\n \tif (ret != sizeof(lat)) {\n-\t\tclose(fd);\n+\t\tclose(latency_fd);\n \t\treturn;\n \t}\n \n@@ -255,7 +609,7 @@ void configure_cpu_latency(void)\n \n void restore_cpu_latency(void)\n {\n-\tif (latency_fd >= 0)\n+\tif (latency_fd > 0)\n \t\tclose(latency_fd);\n }\n \n@@ -268,7 +622,6 @@ void print_mac_address(const unsigned char *mac_address)\n \t\tif (i != ETH_ALEN - 1)\n \t\t\tprintf(\"-\");\n \t}\n-\tprintf(\"\\n\");\n }\n \n void print_payload_pattern(const char *payload_pattern, size_t payload_pattern_length)\n@@ -277,44 +630,6 @@ void print_payload_pattern(const char *payload_pattern, size_t payload_pattern_l\n \n \tfor (i = 0; i < payload_pattern_length; ++i)\n \t\tprintf(\"0x%02x \", payload_pattern[i]);\n-\tprintf(\"\\n\");\n-}\n-\n-void print_cpu_list(const int *cpus, size_t cpus_len)\n-{\n-\tsize_t i;\n-\n-\tfor (i = 0; i < cpus_len; i++) {\n-\t\tprintf(\"%d\", cpus[i]);\n-\t\tif (i != cpus_len - 1)\n-\t\t\tprintf(\", \");\n-\t}\n-\tprintf(\"\\n\");\n-}\n-\n-void print_clockid(clockid_t clock)\n-{\n-\tchar buf[32];\n-\tint id;\n-\n-\tif (clock == CLOCK_TAI) {\n-\t\tprintf(\"CLOCK_TAI\\n\");\n-\t\treturn;\n-\t}\n-\n-\tif (clock == CLOCK_MONOTONIC) {\n-\t\tprintf(\"CLOCK_MONOTONIC\\n\");\n-\t\treturn;\n-\t}\n-\n-\tif (clock == CLOCK_REALTIME) {\n-\t\tprintf(\"CLOCK_REALTIME\\n\");\n-\t\treturn;\n-\t}\n-\n-\tid = clock - CLOCK_AUX;\n-\tsnprintf(buf, sizeof(buf), \"CLOCK_AUX%d\", id);\n-\tprintf(\"%s\\n\", buf);\n }\n \n uint32_t get_meta_data_offset(enum stat_frame_type frame_type, enum security_mode security_mode)\ndiff --git a/src/utils.h b/src/utils.h\nindex 5ef4a6a..069f8e3 100644\n--- a/src/utils.h\n+++ b/src/utils.h\n@@ -1,6 +1,6 @@\n /* SPDX-License-Identifier: BSD-2-Clause */\n /*\n- * Copyright (C) 2020-2026 Linutronix GmbH\n+ * Copyright (C) 2020-2024 Linutronix GmbH\n  * Author Kurt Kanzenbach <kurt@linutronix.de>\n  */\n \n@@ -16,27 +16,15 @@\n #include <time.h>\n \n #include \"config.h\"\n-#include \"log.h\"\n #include \"net_def.h\"\n #include \"security.h\"\n #include \"stat.h\"\n \n /* timing */\n-#ifndef MAX_CLOCKS\n-#define MAX_CLOCKS 16\n-#endif\n-\n-#ifndef CLOCK_AUX\n-#define CLOCK_AUX MAX_CLOCKS\n-#define MAX_AUX_CLOCKS 8\n-#endif\n-\n #define MSEC_PER_SEC 1000\n #define USEC_PER_SEC 1000000\n #define NSEC_PER_SEC 1000000000LL\n \n-#define __unused __attribute__((unused))\n-\n static inline void ns_to_ts(int64_t ns, struct timespec *ts)\n {\n \tts->tv_sec = ns / NSEC_PER_SEC;\n@@ -51,7 +39,7 @@ static inline int64_t ts_to_ns(const struct timespec *ts)\n void increment_period(struct timespec *time, int64_t period_ns);\n \n void swap_mac_addresses(void *buffer, size_t len);\n-void insert_vlan_tag(void *buffer, size_t len, uint16_t ether_type, uint16_t vlan_tci);\n+void insert_vlan_tag(void *buffer, size_t len, uint16_t vlan_tci);\n \n /*\n  * This function takes an received Ethernet frame by AF_PACKET sockets and performs two tasks:\n@@ -65,6 +53,25 @@ void build_vlan_frame_from_rx(const unsigned char *old_frame, size_t old_frame_l\n \t\t\t      unsigned char *new_frame, size_t new_frame_len, uint16_t ether_type,\n \t\t\t      uint16_t vlan_tci);\n \n+/*\n+ * This function initializes an PROFINET Ethernet frame. The Ethernet header, PROFINET header and\n+ * payload is initialized. The sequenceCounter is set to zero.\n+ *\n+ * In case the SecurityMode is AE or AO, the PROFINET Ethernet frames will contain the\n+ * SecurityHeader after the FrameID.\n+ */\n+void initialize_profinet_frame(enum security_mode mode, unsigned char *frame_data,\n+\t\t\t       size_t frame_length, const unsigned char *source,\n+\t\t\t       const unsigned char *destination, const char *payload_pattern,\n+\t\t\t       size_t payload_pattern_length, uint16_t vlan_tci, uint16_t frame_id);\n+\n+/*\n+ * This function receives a Profinet frame. It performs all required tests such as checking sequence\n+ * counters, payload, checksums, etc. This is used for TSN, RTC and RTA as well as by packet and xdp\n+ * code.\n+ */\n+int receive_profinet_frame(void *data, unsigned char *frame_data, size_t len);\n+\n /*\n  * The following function prepares an already initialized PROFINET Ethernet frame for final\n  * transmission. Depending on traffic class and security modes, different actions have to be taken\n@@ -102,8 +109,6 @@ void pthread_error(int ret, const char *message);\n /* Printing */\n void print_mac_address(const unsigned char *mac_address);\n void print_payload_pattern(const char *payload_pattern, size_t payload_pattern_length);\n-void print_cpu_list(const int *cpus, size_t cpus_len);\n-void print_clockid(clockid_t clock);\n \n #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))\n \n@@ -143,56 +148,17 @@ static inline uint64_t meta_data_to_tx_timestamp(const struct reference_meta_dat\n \treturn tx_timestamp;\n }\n \n-static inline void app_clock_get(struct timespec *time)\n-{\n-\tint ret;\n-\n-\t/* clock_gettime(AppClockId) can fail due to missing clocks e.g. CLOCK_AUX */\n-\tret = clock_gettime(app_config.application_clock_id, time);\n-\tif (ret) {\n-\t\tlog_message(LOG_LEVEL_ERROR, \"STAT: clock_gettime() failed: %s!\\n\",\n-\t\t\t    strerror(errno));\n-\t\tmemset(time, '\\0', sizeof(*time));\n-\t}\n-}\n-\n-static inline void set_mirror_tx_timestamp_est(struct reference_meta_data *meta)\n+static inline void set_mirror_tx_timestamp(struct reference_meta_data *meta)\n {\n \tstruct timespec now;\n \n-\tapp_clock_get(&now);\n+\tclock_gettime(app_config.application_clock_id, &now);\n \n-\t/*\n-\t * This is rather an estimation for the Tx timestamp. In case we do use PROFINET security,\n-\t * the Tx timestamp is embedded into the frame upon *receive*, because the Rx thread calls\n-\t * OpenSSL to authenticate and encrypt the frame afterwards. This means, we cannot update\n-\t * the Tx timestamp on Tx without breaking the checksums etc.\n-\t */\n \ttx_timestamp_to_meta_data(meta,\n \t\t\t\t  ts_to_ns(&now) + (app_config.application_tx_base_offset_ns -\n \t\t\t\t\t\t    app_config.application_rx_base_offset_ns));\n }\n \n-static inline void set_mirror_tx_timestamp(const struct traffic_class_config *conf,\n-\t\t\t\t\t   unsigned char *frame_data, size_t frame_size,\n-\t\t\t\t\t   size_t num_frames, uint32_t meta_data_offset)\n-{\n-\tstruct timespec tx_time;\n-\n-\t/* Only update for non-secure frames. See comment in set_mirror_tx_timestamp_est(). */\n-\tif (conf->security_mode != SECURITY_MODE_NONE)\n-\t\treturn;\n-\n-\tapp_clock_get(&tx_time);\n-\tfor (int i = 0; i < (int)num_frames; i++) {\n-\t\tunsigned char *data = frame_data + i * frame_size;\n-\t\tstruct reference_meta_data *meta_data;\n-\n-\t\tmeta_data = (struct reference_meta_data *)(data + meta_data_offset);\n-\t\ttx_timestamp_to_meta_data(meta_data, ts_to_ns(&tx_time));\n-\t}\n-}\n-\n static inline uint64_t get_sequence_counter(const unsigned char *frame_data,\n \t\t\t\t\t    uint32_t meta_data_offset, size_t num_frames_per_cycle)\n {\n@@ -215,22 +181,4 @@ static inline void set_sequence_counter(unsigned char *frame_data, uint32_t meta\n \n uint32_t get_meta_data_offset(enum stat_frame_type frame_type, enum security_mode security_mode);\n \n-/* snprintf */\n-static inline int snprintf_err_handling(char **buffer, size_t *len, int ret)\n-{\n-\t/* Error? */\n-\tif (ret < 0)\n-\t\treturn -EINVAL;\n-\n-\t/* Buffer too small? */\n-\tif (ret >= *len)\n-\t\treturn -EINVAL;\n-\n-\t/* All good */\n-\t*buffer += ret;\n-\t*len -= ret;\n-\n-\treturn 0;\n-}\n-\n #endif /* _UTILS_H_ */\ndiff --git a/src/workload.c b/src/workload.c\nindex cc271f5..ea1ce15 100644\n--- a/src/workload.c\n+++ b/src/workload.c\n@@ -1,36 +1,24 @@\n // SPDX-License-Identifier: BSD-2-Clause\n /*\n  * Copyright (c) 2025 Intel Corporation\n- * Copyright (c) 2025-2026 Linutronix GmbH\n  */\n-#include <dlfcn.h>\n-#include <errno.h>\n-#include <pthread.h>\n-#include <string.h>\n-\n-#include \"config.h\"\n-#include \"log.h\"\n-#include \"stat.h\"\n-#include \"thread.h\"\n-#include \"utils.h\"\n #include \"workload.h\"\n+#include \"stat.h\"\n+#include <dlfcn.h>\n \n /*\n  * Parse input for arguments store them in argc for count and argv for vector\n  * of arguments.\n  */\n-static int string_to_argc_argv(const char *input, int *argc, char ***argv)\n+void string_to_argc_argv(const char *input, int *argc, char ***argv)\n {\n-\tchar *temp_input = strdup(input); /* Duplicate to avoid modifying original */\n-\tint count = 1, i = 1, ret;\n \tchar *token;\n-\n-\tif (!temp_input)\n-\t\treturn -ENOMEM;\n+\tint count = 1;\n+\tchar *temp_input = strdup(input); /* Duplicate to avoid modifying original */\n \n \t/* First pass: count the number of arguments */\n \ttoken = strtok(temp_input, \" \");\n-\twhile (token) {\n+\twhile (token != NULL) {\n \t\tcount++;\n \t\ttoken = strtok(NULL, \" \");\n \t}\n@@ -38,319 +26,123 @@ static int string_to_argc_argv(const char *input, int *argc, char ***argv)\n \t/* Allocate memory for argv array */\n \t*argv = malloc(count * sizeof(char *));\n \tif (*argv == NULL) {\n-\t\tret = -ENOMEM;\n-\t\tgoto err_argv;\n+\t\tfprintf(stderr, \"Memory allocation failed\\n\");\n+\t\texit(1);\n \t}\n \n \t/* Second pass: populate argv */\n \tstrcpy(temp_input, input); /* Reset temp_input with original input */\n+\tint i = 1;\n \ttoken = strtok(temp_input, \" \");\n-\twhile (token) {\n+\twhile (token != NULL) {\n \t\t(*argv)[i++] = strdup(token); /* Duplicate token and assign */\n \t\ttoken = strtok(NULL, \" \");\n \t}\n \t(*argv)[0] = strdup(\"Workload\");\n-\tif ((*argv)[0] == NULL) {\n-\t\tret = -ENOMEM;\n-\t\tgoto err_workload;\n-\t}\n-\n-\t*argc = count; /* Set argc to the number of arguments */\n \n-\tfree(temp_input);\n-\treturn 0;\n-\n-err_workload:\n-\tfree(*argv);\n-err_argv:\n+\t*argc = count;    /* Set argc to the number of arguments */\n \tfree(temp_input); /* Free the temporary input string */\n-\treturn ret;\n-}\n-\n-/* Free argv array allocated by string_to_argc_argv() with strdup(). */\n-static void free_argv(int argc, char **argv)\n-{\n-\tfor (int i = 0; i < argc; i++)\n-\t\tfree(argv[i]);\n-\tfree(argv);\n }\n \n void *workload_thread_routine(void *data)\n {\n-\tstruct workload_thread_context *ctx;\n-\tstruct thread_context *thread_context;\n-\tstruct workload_config *wl_cfg;\n-\tstruct workload_thread *thread;\n-\n-\tctx = data;\n-\tthread_context = ctx->thread_context;\n-\twl_cfg = thread_context->workload;\n-\tthread = &wl_cfg->threads[ctx->id];\n+\tstruct thread_context *thread_context = data;\n+\tstruct workload_config *wl_cfg = thread_context->workload;\n+\tstruct timespec start_ts, timeout;\n \n-\t/* Run until we are ready to stop. */\n+\tpthread_mutex_lock(&wl_cfg->workload_mutex);\n+\t/* Run until we are ready to stop */\n \twhile (!thread_context->stop) {\n-\t\tstruct timespec start_ts, timeout;\n-\t\tint ret;\n-\n-\t\tclock_gettime(CLOCK_MONOTONIC, &timeout);\n+\t\tclock_gettime(app_config.application_clock_id, &start_ts);\n+\t\ttimeout = start_ts;\n \t\ttimeout.tv_sec++;\n-\n-\t\t/* Wait for workload to be run. Signaled by Rx threads. */\n-\t\tpthread_mutex_lock(&thread->workload_mutex);\n-\t\tret = pthread_cond_timedwait(&thread->workload_cond, &thread->workload_mutex,\n-\t\t\t\t\t     &timeout);\n-\t\tpthread_mutex_unlock(&thread->workload_mutex);\n-\n-\t\t/* In case of timeout, check !stop again. */\n-\t\tif (ret == ETIMEDOUT)\n-\t\t\tcontinue;\n-\n-\t\tapp_clock_get(&start_ts);\n-\t\tret = wl_cfg->workload_function(&thread->instance, wl_cfg->workload_argc,\n-\t\t\t\t\t\twl_cfg->workload_argv);\n-\t\tif (ret)\n-\t\t\tlog_message(LOG_LEVEL_WARNING,\n-\t\t\t\t    \"Workload: Workload function returned error %d\\n\", ret);\n-\n-\t\t/* workload_running is checked by Tx threads to indicate workload time overruns. */\n-\t\tpthread_mutex_lock(&thread->workload_mutex);\n-\t\tthread->workload_running = 0;\n-\t\tpthread_mutex_unlock(&thread->workload_mutex);\n-\n-\t\tstat_frame_workload(ctx->id, wl_cfg->associated_frame,\n-\t\t\t\t    thread->workload_sequence_counter, start_ts);\n-\t\tthread->workload_sequence_counter++;\n+\t\t/* Check for spurious wakeups */\n+\t\tif (wl_cfg->workload_run) {\n+\t\t\twl_cfg->workload_function(wl_cfg->workload_argc, wl_cfg->workload_argv);\n+\t\t\twl_cfg->workload_done = 1;\n+\t\t\twl_cfg->workload_run = 0;\n+\t\t\tstat_frame_workload(wl_cfg->associated_frame,\n+\t\t\t\t\t    wl_cfg->workload_sequence_counter, start_ts);\n+\t\t\twl_cfg->workload_sequence_counter++;\n+\t\t}\n+\t\tpthread_cond_timedwait(&wl_cfg->workload_cond, &wl_cfg->workload_mutex, &timeout);\n \t}\n-\n \treturn NULL;\n }\n \n-int workload_context_init(struct thread_context *thread_context)\n+void workload_context_init(struct thread_context *thread_context, char *workload_file,\n+\t\t\t   char *workload_function, char *workload_arguments,\n+\t\t\t   char *workload_setup_function, char *workload_setup_arguments,\n+\t\t\t   enum stat_frame_type frame_type)\n {\n-\tstruct traffic_class_config *conf = thread_context->conf;\n-\tstruct workload_config *wl_cfg;\n+\tstruct workload_config *wl_cfg = thread_context->workload;\n \tchar *error;\n-\tint ret, i;\n \n-\tif (!conf->rx_workload_enabled)\n-\t\treturn 0;\n+\twl_cfg->workload_handler = dlopen(workload_file, RTLD_NOW | RTLD_GLOBAL);\n \n-\tthread_context->workload = calloc(1, sizeof(struct workload_config));\n-\tif (!thread_context->workload) {\n-\t\tfprintf(stderr, \"Failed to allocate workload!\\n\");\n-\t\treturn -ENOMEM;\n-\t}\n-\twl_cfg = thread_context->workload;\n-\n-\twl_cfg->workload_handler = dlopen(conf->workload_file, RTLD_NOW | RTLD_GLOBAL);\n \tif (!wl_cfg->workload_handler) {\n \t\terror = dlerror();\n-\t\tfprintf(stderr, \"Error: Unable to open workload '%s': %s\\n\", conf->workload_file,\n-\t\t\terror);\n-\t\tret = -EINVAL;\n-\t\tgoto dlopen;\n+\t\tfprintf(stderr, \"Error: Unable to open workload: %s. %s\\n\", workload_file, error);\n+\t\texit(EXIT_FAILURE);\n \t}\n \n-\tif (conf->workload_setup_function) {\n+\tif (workload_setup_function != NULL) {\n \t\twl_cfg->workload_setup_function =\n-\t\t\tdlsym(wl_cfg->workload_handler, conf->workload_setup_function);\n+\t\t\tdlsym(wl_cfg->workload_handler, workload_setup_function);\n \t\tif (!wl_cfg->workload_setup_function) {\n \t\t\tfprintf(stderr, \"Error: Unable to find setup function: %s\\n\",\n-\t\t\t\tconf->workload_setup_function);\n-\t\t\tret = -EINVAL;\n-\t\t\tgoto dl;\n-\t\t}\n-\t}\n-\n-\tif (conf->workload_teardown_function) {\n-\t\twl_cfg->workload_teardown_function =\n-\t\t\tdlsym(wl_cfg->workload_handler, conf->workload_teardown_function);\n-\t\tif (!wl_cfg->workload_teardown_function) {\n-\t\t\tfprintf(stderr, \"Error: Unable to find teardown function: %s\\n\",\n-\t\t\t\tconf->workload_teardown_function);\n-\t\t\tret = -EINVAL;\n-\t\t\tgoto dl;\n+\t\t\t\tworkload_setup_function);\n+\t\t\texit(EXIT_FAILURE);\n \t\t}\n \t}\n \n-\twl_cfg->workload_function = dlsym(wl_cfg->workload_handler, conf->workload_function);\n+\twl_cfg->workload_function = dlsym(wl_cfg->workload_handler, workload_function);\n \tif (!wl_cfg->workload_function) {\n-\t\tfprintf(stderr, \"Error: Unable to find function: %s\\n\", conf->workload_function);\n-\t\tret = -EINVAL;\n-\t\tgoto dl;\n+\t\tfprintf(stderr, \"Error: Unable to find function: %s\\n\", workload_function);\n+\t\texit(EXIT_FAILURE);\n \t}\n \n-\twl_cfg->workload_argc = 0;\n-\twl_cfg->workload_argv = NULL;\n-\tif (conf->workload_arguments) {\n-\t\tret = string_to_argc_argv(conf->workload_arguments, &wl_cfg->workload_argc,\n-\t\t\t\t\t  &wl_cfg->workload_argv);\n-\t\tif (ret)\n-\t\t\tgoto dl;\n+\tif (workload_arguments != NULL) {\n+\t\tstring_to_argc_argv(workload_arguments, &(wl_cfg->workload_argc),\n+\t\t\t\t    &(wl_cfg->workload_argv));\n+\t} else {\n+\t\twl_cfg->workload_argc = 0;\n+\t\twl_cfg->workload_argv = NULL;\n \t}\n \n-\twl_cfg->workload_setup_argc = 0;\n-\twl_cfg->workload_setup_argv = NULL;\n-\tif (conf->workload_setup_arguments) {\n-\t\tret = string_to_argc_argv(conf->workload_setup_arguments,\n-\t\t\t\t\t  &wl_cfg->workload_setup_argc,\n-\t\t\t\t\t  &wl_cfg->workload_setup_argv);\n-\t\tif (ret)\n-\t\t\tgoto argv;\n+\tif (workload_setup_arguments != NULL) {\n+\t\tstring_to_argc_argv(workload_setup_arguments, &(wl_cfg->workload_setup_argc),\n+\t\t\t\t    &(wl_cfg->workload_setup_argv));\n+\t} else {\n+\t\twl_cfg->workload_setup_argc = 0;\n+\t\twl_cfg->workload_setup_argv = NULL;\n \t}\n \n-\twl_cfg->associated_frame = thread_context->frame_type;\n+\tpthread_mutex_init(&wl_cfg->workload_mutex, NULL);\n+\tpthread_cond_init(&wl_cfg->workload_cond, NULL);\n \n-\tfor (i = 0; i < conf->workload_thread_cpus_num; i++) {\n-\t\tstruct workload_thread *thread = &wl_cfg->threads[i];\n-\t\tstruct workload_instance *instance = &thread->instance;\n-\t\tstruct workload_thread_context *ctx = &wl_cfg->ctx[i];\n+\twl_cfg->associated_frame = frame_type;\n \n-\t\t/* Initialize workload instances */\n-\t\tinstance->id = i;\n-\t\tinstance->cpu = conf->workload_thread_cpus[i];\n-\t\tinstance->priv = NULL;\n-\n-\t\tinit_mutex(&thread->workload_mutex);\n-\t\tinit_condition_variable(&thread->workload_cond);\n-\n-\t\tctx->thread_context = thread_context;\n-\t\tctx->id = i;\n-\n-\t\t/* Call the setup function if it exists */\n-\t\tif (wl_cfg->workload_setup_function) {\n-\t\t\tret = wl_cfg->workload_setup_function(instance, wl_cfg->workload_setup_argc,\n-\t\t\t\t\t\t\t      wl_cfg->workload_setup_argv);\n-\t\t\tif (ret) {\n-\t\t\t\tfprintf(stderr,\n-\t\t\t\t\t\"Workload setup function '%s' return with failure code: \"\n-\t\t\t\t\t\"%d\\n\",\n-\t\t\t\t\tconf->workload_setup_function, ret);\n-\t\t\t\tgoto setup;\n-\t\t\t}\n-\t\t}\n-\t}\n-\n-\t/* Create and start threads */\n-\tfor (i = 0; i < conf->workload_thread_cpus_num; i++) {\n-\t\tstruct workload_thread *thread = &wl_cfg->threads[i];\n-\t\tstruct workload_thread_context *ctx = &wl_cfg->ctx[i];\n-\n-\t\tret = create_rt_thread(&thread->workload_task_id, conf->workload_thread_priority,\n-\t\t\t\t       conf->workload_thread_cpus[i], &workload_thread_routine, ctx,\n-\t\t\t\t       \"WorkloadTask%d\", i);\n-\t\tif (ret) {\n-\t\t\tfprintf(stderr, \"Failed to create Workload Thread for CPU %d!\\n\",\n-\t\t\t\tconf->workload_thread_cpus[i]);\n-\t\t\tgoto thread;\n-\t\t}\n+\t/* Call the setup function if it exists */\n+\tif (wl_cfg->workload_setup_function != NULL) {\n+\t\twl_cfg->workload_setup_function(wl_cfg->workload_setup_argc,\n+\t\t\t\t\t\twl_cfg->workload_setup_argv);\n \t}\n-\n-\treturn 0;\n-\n-thread:\n-\tthread_context->stop = 1;\n-\tfor (int j = i - 1; j >= 0; --j)\n-\t\tpthread_join(wl_cfg->threads[j].workload_task_id, NULL);\n-setup:\n-\tif (wl_cfg->workload_teardown_function)\n-\t\tfor (int j = i - 1; j >= 0; --j)\n-\t\t\twl_cfg->workload_teardown_function(&wl_cfg->threads[j].instance);\n-\tfree_argv(wl_cfg->workload_setup_argc, wl_cfg->workload_setup_argv);\n-argv:\n-\tfree_argv(wl_cfg->workload_argc, wl_cfg->workload_argv);\n-dl:\n-\tdlclose(thread_context->workload->workload_handler);\n-dlopen:\n-\tfree(thread_context->workload);\n-\treturn ret;\n }\n \n void workload_thread_free(struct thread_context *thread_context)\n {\n-\tstruct traffic_class_config *conf;\n-\tstruct workload_config *wl_cfg;\n-\n \tif (!thread_context)\n \t\treturn;\n-\n-\tconf = thread_context->conf;\n-\n-\tif (!conf || !conf->rx_workload_enabled)\n-\t\treturn;\n-\n-\twl_cfg = thread_context->workload;\n-\n-\tif (wl_cfg->workload_teardown_function)\n-\t\tfor (int i = 0; i < conf->workload_thread_cpus_num; i++)\n-\t\t\twl_cfg->workload_teardown_function(&wl_cfg->threads[i].instance);\n-\n-\tfree_argv(wl_cfg->workload_argc, wl_cfg->workload_argv);\n-\tfree_argv(wl_cfg->workload_setup_argc, wl_cfg->workload_setup_argv);\n-\n-\tdlclose(wl_cfg->workload_handler);\n-\n+\tdlclose(thread_context->workload->workload_handler);\n \tfree(thread_context->workload);\n }\n \n void workload_thread_wait_for_finish(struct thread_context *thread_context)\n {\n-\tstruct traffic_class_config *conf;\n-\tstruct workload_config *wl_cfg;\n-\n+\tthread_context->stop = 1;\n \tif (!thread_context)\n \t\treturn;\n \n-\tconf = thread_context->conf;\n-\n-\tif (!conf || !conf->rx_workload_enabled)\n-\t\treturn;\n-\n-\twl_cfg = thread_context->workload;\n-\n-\tfor (int i = 0; i < conf->workload_thread_cpus_num; i++)\n-\t\tpthread_join(wl_cfg->threads[i].workload_task_id, NULL);\n-}\n-\n-void workload_check_finished(struct thread_context *thread_context)\n-{\n-\tconst struct traffic_class_config *conf = thread_context->conf;\n-\tstruct workload_config *wl_cfg = thread_context->workload;\n-\n-\tif (!conf->rx_workload_enabled)\n-\t\treturn;\n-\n-\t/* Increment workload outlier count if workload did not finish. */\n-\tfor (int i = 0; i < conf->workload_thread_cpus_num; i++) {\n-\t\tstruct workload_thread *thread = &wl_cfg->threads[i];\n-\n-\t\tpthread_mutex_lock(&thread->workload_mutex);\n-\t\tif (thread->workload_running) {\n-\t\t\tstat_inc_workload_outlier(i, thread_context->frame_type);\n-\t\t\tlog_message(LOG_LEVEL_DEBUG, \"Workload for CPU %d did not finish!\\n\",\n-\t\t\t\t    conf->workload_thread_cpus[i]);\n-\t\t}\n-\t\tpthread_mutex_unlock(&thread->workload_mutex);\n-\t}\n-}\n-\n-void workload_signal(struct thread_context *thread_context, unsigned int received)\n-{\n-\tconst struct traffic_class_config *conf = thread_context->conf;\n-\tstruct workload_config *wl_cfg = thread_context->workload;\n-\n-\tif (!conf->rx_workload_enabled)\n-\t\treturn;\n-\n-\t/* Run workload if we received frames or prewarm is enabled */\n-\tif (received || conf->rx_workload_prewarm) {\n-\t\tfor (int i = 0; i < conf->workload_thread_cpus_num; i++) {\n-\t\t\tstruct workload_thread *thread = &wl_cfg->threads[i];\n-\n-\t\t\tpthread_mutex_lock(&thread->workload_mutex);\n-\t\t\tthread->workload_running = 1;\n-\t\t\tpthread_cond_signal(&thread->workload_cond);\n-\t\t\tpthread_mutex_unlock(&thread->workload_mutex);\n-\t\t}\n-\t}\n+\tpthread_join(thread_context->workload->workload_task_id, NULL);\n }\ndiff --git a/src/workload.h b/src/workload.h\nindex 7e5f8b4..2013684 100644\n--- a/src/workload.h\n+++ b/src/workload.h\n@@ -1,7 +1,6 @@\n // SPDX-License-Identifier: BSD-2-Clause\n /*\n  * Copyright (c) 2025 Intel Corporation\n- * Copyright (c) 2026 Linutronix GmbH\n  */\n #ifndef _WORKLOAD_H_\n #define _WORKLOAD_H_\n@@ -10,56 +9,33 @@\n #include \"stat.h\"\n #include \"thread.h\"\n \n-struct thread_context;\n+struct thread_context; // Forward Declaration\n \n-/* Instance per workload thread passed to ptr_chasing/jacobi/... */\n-struct workload_instance {\n-\tint id;     /* Which workload instance is it? */\n-\tint cpu;    /* On which CPU does this instance run on? */\n-\tvoid *priv; /* Pointer to private data. */\n-};\n-\n-/* Thread context for workload_thread_routine() */\n-struct workload_thread_context {\n-\tstruct thread_context *thread_context;\n-\tint id;\n-};\n-\n-/* Data per workload thread. */\n-struct workload_thread {\n-\tstruct workload_instance instance;\n+struct workload_config {\n \tpthread_t workload_task_id;\n \tuint64_t workload_sequence_counter;\n \tpthread_mutex_t workload_mutex;\n \tpthread_cond_t workload_cond;\n-\tint workload_running;\n-};\n-\n-/* Single workload config per traffic class. */\n-struct workload_config {\n-\t/* Specific per workload thread data */\n-\tstruct workload_thread threads[WORKLOAD_MAX];\n-\n-\t/* Structs passed to workload_thread_routine() */\n-\tstruct workload_thread_context ctx[WORKLOAD_MAX];\n-\n-\t/* Common for all workload threads */\n \tvoid *workload_handler;\n-\tint (*workload_function)(struct workload_instance *instance, int argc, char **argv);\n+\tint (*workload_function)(int argc, char **argv);\n \tint workload_argc;\n \tchar **workload_argv;\n-\tint (*workload_setup_function)(struct workload_instance *instance, int argc, char **argv);\n+\tint (*workload_setup_function)(int argc, char **argv);\n \tint workload_setup_argc;\n \tchar **workload_setup_argv;\n-\tvoid (*workload_teardown_function)(struct workload_instance *instance);\n+\tvolatile int workload_run;\n+\tvolatile int workload_done;\n \tenum stat_frame_type associated_frame;\n };\n \n-int workload_context_init(struct thread_context *thread_context);\n+void *workload_thread_routine(void *data);\n+void workload_context_init(struct thread_context *thread_context, char *workload_file,\n+\t\t\t   char *workload_function, char *workload_argument,\n+\t\t\t   char *workload_setup_function, char *workload_setup_argument,\n+\t\t\t   enum stat_frame_type frame_type);\n void workload_thread_free(struct thread_context *thread_context);\n void workload_thread_wait_for_finish(struct thread_context *thread_context);\n \n-void workload_check_finished(struct thread_context *thread_context);\n-void workload_signal(struct thread_context *thread_context, unsigned int received);\n+void string_to_argc_argv(const char *input, int *argc, char ***argv);\n \n #endif /* _WORKLOAD_H_ */\ndiff --git a/src/xdp.c b/src/xdp.c\nindex efe4269..f6ee1f0 100644\n--- a/src/xdp.c\n+++ b/src/xdp.c\n@@ -1,6 +1,6 @@\n // SPDX-License-Identifier: BSD-2-Clause\n /*\n- * Copyright (C) 2021-2026 Linutronix GmbH\n+ * Copyright (C) 2021-2025 Linutronix GmbH\n  * Author Kurt Kanzenbach <kurt@linutronix.de>\n  */\n \n@@ -35,18 +35,15 @@\n #include \"xdp.h\"\n #include \"xdp_metadata.h\"\n \n-static struct xdp_init {\n-\tstruct xdp_program *prog;\n-\tint initialized;\n-\tint xsks_map;\n-} xdp_init;\n+static int program_loaded;\n+static int xsks_map;\n \n static enum xdp_attach_mode xdp_flags(bool skb_mode)\n {\n \treturn skb_mode ? XDP_MODE_SKB : XDP_MODE_NATIVE;\n }\n \n-static void xdp_set_prog_bind_flags(struct bpf_object __unused *obj, unsigned int __unused if_index)\n+static void xdp_set_prog_bind_flags(struct bpf_object *obj, unsigned int if_index)\n {\n #ifdef RX_TIMESTAMP\n \tstruct bpf_program *bpf_prog = bpf_object__find_program_by_name(obj, \"xdp_sock_prog\");\n@@ -133,85 +130,108 @@ static void xdp_process_tx_timestamp(struct xdp_socket *xsk, uint32_t idx_cq)\n \tstruct xsk_tx_metadata *meta =\n \t\t(struct xsk_tx_metadata *)(data - sizeof(struct xsk_tx_metadata));\n \n-\tlog_message(LOG_LEVEL_DEBUG, \"XdpTxHwTs CQ[%u]: addr=0x%llx, flags=0x%llx, ts=%llu\\n\",\n+\tlog_message(LOG_LEVEL_DEBUG,\n+\t\t    \"XdpTxHwTs CQ[%u]: addr=0x%llx, flags=0x%llx, ts=%llu, seq_lagged=%llu\\n\",\n \t\t    idx_cq, (unsigned long long)addr, (unsigned long long)meta->flags,\n-\t\t    (unsigned long long)meta->completion.tx_timestamp);\n+\t\t    (unsigned long long)meta->completion.tx_timestamp,\n+\t\t    (unsigned long long)xsk->tx_hwts.seq_lagged);\n \n \tif (meta->flags & XDP_TXMD_FLAGS_TIMESTAMP) {\n \t\t/* Determine frame type from the round trip context */\n \t\tenum stat_frame_type frame_type = (enum stat_frame_type)(rtt - round_trip_contexts);\n \n-\t\t/*\n-\t\t * Derive sequence counter directly from the frame payload to correctly\n-\t\t * correlate this completion with the backlog entry, even if the completion\n-\t\t * arrives more than one cycle late or out of submission order.\n-\t\t *\n-\t\t * For multi-frame cycles only the first frame's backlog slot has a valid\n-\t\t * sw_ts (stat_frames_sent_batch writes once per cycle). Round down to the\n-\t\t * cycle start so that all completions within the same cycle share the same\n-\t\t * backlog index.\n-\t\t */\n-\t\tuint64_t fpc = xsk->tx_hwts.frames_per_cycle;\n-\t\tuint64_t seq = get_sequence_counter(data, xsk->tx_hwts.meta_data_offset, fpc);\n-\t\tuint64_t cycle_seq = (seq / fpc) * fpc;\n-\t\tuint64_t frame_in_cycle = seq - cycle_seq;\n-\t\tbool is_first = (frame_in_cycle == 0);\n-\t\tbool is_last = (frame_in_cycle == fpc - 1);\n-\t\tsize_t idx = cycle_seq % rtt->backlog_len;\n+\t\t/* Use seq_lagged for now, but only process if HW timestamp is valid */\n+\t\tuint64_t seq = xsk->tx_hwts.seq_lagged;\n+\t\tsize_t idx = seq % rtt->backlog_len;\n \n \t\t/* Continue only if both HW and SW timestamps are valid */\n \t\tif (meta->completion.tx_timestamp > 0 && rtt->backlog[idx].sw_ts > 0) {\n-\t\t\t/*\n-\t\t\t * Tx latency is measured against the first frame's HW timestamp.\n-\t\t\t * Only the first-frame completion updates hw_ts and calls\n-\t\t\t * stat_frame_sent_latency.\n-\t\t\t */\n-\t\t\tif (is_first) {\n-\t\t\t\trtt->backlog[idx].hw_ts = meta->completion.tx_timestamp;\n-\t\t\t\tstat_frame_sent_latency(frame_type, cycle_seq);\n-\t\t\t}\n+\t\t\trtt->backlog[idx].hw_ts = meta->completion.tx_timestamp;\n \n-\t\t\t/*\n-\t\t\t * ProcFirst / ProcBatch are decided by the frame's position within\n-\t\t\t * the cycle (derived from its sequence counter), not by a counter\n-\t\t\t * of arrivals. This is robust to out-of-order or dropped\n-\t\t\t * completions. For fpc == 1 the same completion is both first and\n-\t\t\t * last, so only ProcFirst is recorded.\n-\t\t\t */\n-\t\t\tif (log_stat_user_selected == LOG_TX_TIMESTAMPS &&\n-\t\t\t    config_have_rx_timestamp() &&\n-\t\t\t    meta->completion.tx_timestamp > rtt->backlog[idx].sw_ts) {\n-\t\t\t\tif (is_first) {\n-\t\t\t\t\tstat_proc_first_latency(frame_type, cycle_seq,\n-\t\t\t\t\t\t\t\tmeta->completion.tx_timestamp);\n-\t\t\t\t} else if (is_last) {\n-\t\t\t\t\tstat_proc_batch_latency(frame_type, cycle_seq,\n+\t\t\t/* Increment TX HW timestamp count for this cycle */\n+\t\t\txsk->tx_hwts.count++;\n+\n+\t\t\tstat_frame_sent_latency(frame_type, seq);\n+\n+\t\t\t/* Handle timestamp processing based on frames per cycle */\n+\t\t\tif (xsk->tx_hwts.frames_per_cycle == 1) {\n+\t\t\t\t/*\n+\t\t\t\t * Single frame case: measure only ProcFirst (since first == last)\n+\t\t\t\t */\n+\t\t\t\tif (log_stat_user_selected == LOG_TX_TIMESTAMPS &&\n+\t\t\t\t    config_have_rx_timestamp() &&\n+\t\t\t\t    meta->completion.tx_timestamp > rtt->backlog[idx].sw_ts) {\n+\t\t\t\t\tstat_proc_first_latency(frame_type, seq,\n \t\t\t\t\t\t\t\tmeta->completion.tx_timestamp);\n \t\t\t\t}\n+\t\t\t\t/* Reset TX HW timestamp count for next cycle */\n+\t\t\t\txsk->tx_hwts.count = 0;\n+\t\t\t} else {\n+\t\t\t\t/*\n+\t\t\t\t * Multiple frames case: handle first and last separately.\n+\t\t\t\t * First timestamp completion corresponds to the first\n+\t\t\t\t * packet (ProcFirst).\n+\t\t\t\t */\n+\t\t\t\tif (xsk->tx_hwts.count == 1) {\n+\t\t\t\t\t/*\n+\t\t\t\t\t * Calculate ProcFirst latency (1st RX HW to 1st TX HW) only\n+\t\t\t\t\t * for mirror mode and valid HW timestamp.\n+\t\t\t\t\t */\n+\t\t\t\t\tif (log_stat_user_selected == LOG_TX_TIMESTAMPS &&\n+\t\t\t\t\t    config_have_rx_timestamp() &&\n+\t\t\t\t\t    meta->completion.tx_timestamp >\n+\t\t\t\t\t\t    rtt->backlog[idx].sw_ts) {\n+\t\t\t\t\t\tstat_proc_first_latency(\n+\t\t\t\t\t\t\tframe_type, seq,\n+\t\t\t\t\t\t\tmeta->completion.tx_timestamp);\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t\t/* Second timestamp completion corresponds to the last packet\n+\t\t\t\t   (ProcBatch) */\n+\t\t\t\telse if (xsk->tx_hwts.count == 2) {\n+\t\t\t\t\t/*\n+\t\t\t\t\t * We timestamp two packets per cycle: first (i==0)\n+\t\t\t\t\t * and last (i==num_frames-1). Therefore,\n+\t\t\t\t\t * tx_hwts.count == 2 always refers to the last packet,\n+\t\t\t\t\t * regardless of the number of frames per cycle.\n+\t\t\t\t\t *\n+\t\t\t\t\t * Calculate ProcBatch latency (1st RX HW to last TX HW)\n+\t\t\t\t\t * for mirror mode and valid HW timestamp.\n+\t\t\t\t\t */\n+\t\t\t\t\tif (log_stat_user_selected == LOG_TX_TIMESTAMPS &&\n+\t\t\t\t\t    config_have_rx_timestamp() &&\n+\t\t\t\t\t    meta->completion.tx_timestamp >\n+\t\t\t\t\t\t    rtt->backlog[idx].sw_ts) {\n+\t\t\t\t\t\tstat_proc_batch_latency(\n+\t\t\t\t\t\t\tframe_type, seq,\n+\t\t\t\t\t\t\tmeta->completion.tx_timestamp);\n+\t\t\t\t\t}\n+\n+\t\t\t\t\t/* Reset TX HW timestamp count for next cycle */\n+\t\t\t\t\txsk->tx_hwts.count = 0;\n+\t\t\t\t}\n \t\t\t}\n \t\t} else {\n \t\t\tlog_message(LOG_LEVEL_DEBUG,\n \t\t\t\t    \"XdpTxHwTs: Invalid timestamps for seq=%llu (HW=%llu, \"\n \t\t\t\t    \"SW=%llu), skipping\\n\",\n-\t\t\t\t    (unsigned long long)cycle_seq,\n+\t\t\t\t    (unsigned long long)seq,\n \t\t\t\t    (unsigned long long)meta->completion.tx_timestamp,\n \t\t\t\t    (unsigned long long)rtt->backlog[idx].sw_ts);\n \t\t}\n \t} else {\n-\t\tuint64_t seq = get_sequence_counter(data, xsk->tx_hwts.meta_data_offset,\n-\t\t\t\t\t\t    xsk->tx_hwts.frames_per_cycle);\n-\t\tuint64_t cycle_seq =\n-\t\t\t(seq / xsk->tx_hwts.frames_per_cycle) * xsk->tx_hwts.frames_per_cycle;\n-\t\tsize_t idx = cycle_seq % rtt->backlog_len;\n+\t\tuint64_t seq = xsk->tx_hwts.seq_lagged;\n+\t\tsize_t idx = seq % rtt->backlog_len;\n \n \t\tlog_message(LOG_LEVEL_WARNING,\n \t\t\t    \"XDP TX HW timestamp missing for expected seq %\" PRIu64 \", idx=%zu\\n\",\n-\t\t\t    cycle_seq, idx);\n+\t\t\t    seq, idx);\n \t}\n }\n #endif\n \n-static int xdp_prog_init(const char *interface, const char *xdp_program, int skb_mode)\n+static int xdp_load_program(struct xdp_socket *xsk, const char *interface, const char *xdp_program,\n+\t\t\t    int skb_mode)\n {\n \tstruct xdp_program *prog;\n \tstruct bpf_object *obj;\n@@ -234,10 +254,8 @@ static int xdp_prog_init(const char *interface, const char *xdp_program, int skb\n \t * dispatcher master program. Therefore, all applications have to use libxdp and specify\n \t * their metadata e.g., priority accordingly.\n \t */\n-\tif (xdp_init.initialized) {\n-\t\txdp_init.initialized++;\n+\tif (program_loaded)\n \t\treturn 0;\n-\t}\n \n \tif_index = if_nametoindex(interface);\n \tif (!if_index) {\n@@ -272,41 +290,18 @@ static int xdp_prog_init(const char *interface, const char *xdp_program, int skb\n \n \t/* Locate xsks_map for AF_XDP socket code */\n \tmap = bpf_object__find_map_by_name(obj, \"xsks_map\");\n-\txdp_init.xsks_map = bpf_map__fd(map);\n-\tif (xdp_init.xsks_map < 0) {\n+\txsks_map = bpf_map__fd(map);\n+\tif (xsks_map < 0) {\n \t\tfprintf(stderr, \"No xsks_map found!\\n\");\n \t\treturn -EINVAL;\n \t}\n \n-\txdp_init.initialized = 1;\n-\txdp_init.prog = prog;\n+\tprogram_loaded = 1;\n+\txsk->prog = prog;\n \n \treturn 0;\n }\n \n-static void xdp_prog_exit(const char *interface, int skb_mode)\n-{\n-\tunsigned int if_index;\n-\n-\txdp_init.initialized--;\n-\n-\t/* Last XDP socket detaches the XDP program. */\n-\tif (xdp_init.initialized)\n-\t\treturn;\n-\n-\tif_index = if_nametoindex(interface);\n-\tif (!if_index) {\n-\t\tfprintf(stderr, \"if_nametoindex() failed\\n\");\n-\t\treturn;\n-\t}\n-\n-\txdp_program__detach(xdp_init.prog, if_index, xdp_flags(skb_mode), 0);\n-\txdp_program__close(xdp_init.prog);\n-\n-\txdp_init.xsks_map = 0;\n-\txdp_init.prog = NULL;\n-}\n-\n static int xdp_configure_socket_options(struct xdp_socket *xsk, bool busy_poll_mode)\n {\n \tint ret = -EINVAL;\n@@ -426,9 +421,9 @@ struct xdp_socket *xdp_open_socket(const char *interface, const char *xdp_progra\n \tif (!xsk)\n \t\treturn NULL;\n \n-\tret = xdp_prog_init(interface, xdp_program, skb_mode);\n+\tret = xdp_load_program(xsk, interface, xdp_program, skb_mode);\n \tif (ret)\n-\t\tgoto err_prog;\n+\t\tgoto err;\n \n #ifdef TX_TIMESTAMP\n \t/* Enable HW TX timestamping if requested */\n@@ -436,7 +431,7 @@ struct xdp_socket *xdp_open_socket(const char *interface, const char *xdp_progra\n \t\tret = xdp_enable_hw_tx_timestamping(interface);\n \t\tif (ret) {\n \t\t\tfprintf(stderr, \"Failed to enable HW TX timestamping on %s!\\n\", interface);\n-\t\t\tgoto err_hw_ts;\n+\t\t\tgoto err;\n \t\t}\n \t}\n #endif\n@@ -445,14 +440,14 @@ struct xdp_socket *xdp_open_socket(const char *interface, const char *xdp_progra\n \tret = xdp_umem_create(xsk, tx_time_mode, tx_hwtstamp_mode);\n \tif (ret) {\n \t\tfprintf(stderr, \"Failed to allocate AF_XDP UMEM area!\\n\");\n-\t\tgoto err_umem;\n+\t\tgoto err2;\n \t}\n \n \t/* Add some buffers */\n \tret = xsk_ring_prod__reserve(&xsk->umem.fq, XSK_RING_PROD__DEFAULT_NUM_DESCS, &idx);\n \tif (ret != XSK_RING_PROD__DEFAULT_NUM_DESCS) {\n \t\tfprintf(stderr, \"xsk_ring_prod__reserve() failed\\n\");\n-\t\tgoto err_reserve;\n+\t\tgoto err3;\n \t}\n \n \tfor (i = 0; i < XSK_RING_PROD__DEFAULT_NUM_DESCS; i++) {\n@@ -481,52 +476,58 @@ struct xdp_socket *xdp_open_socket(const char *interface, const char *xdp_progra\n \t\t\t\t &xsk_cfg);\n \tif (ret) {\n \t\tfprintf(stderr, \"xsk_socket__create() failed: %s\\n\", strerror(-ret));\n-\t\tgoto err_socket;\n+\t\tgoto err3;\n \t}\n \n \t/* Add xsk into xsks_map */\n \tfd = xsk_socket__fd(xsk->xsk);\n-\tret = bpf_map_update_elem(xdp_init.xsks_map, &queue, &fd, 0);\n+\tret = bpf_map_update_elem(xsks_map, &queue, &fd, 0);\n \tif (ret) {\n \t\tfprintf(stderr, \"bpf_map_update_elem() failed: %s\\n\", strerror(-ret));\n-\t\tgoto err_xsksmap;\n+\t\tgoto err4;\n \t}\n \n \t/* Set socket options */\n \tret = xdp_configure_socket_options(xsk, busy_poll_mode);\n \tif (ret) {\n \t\tfprintf(stderr, \"Failed to configure busy polling!\\n\");\n-\t\tgoto err_conf;\n+\t\tgoto err4;\n \t}\n \n \treturn xsk;\n \n-err_conf:\n-err_xsksmap:\n+err4:\n \txsk_socket__delete(xsk->xsk);\n-err_socket:\n-err_reserve:\n-\txsk_umem__delete(xsk->umem.umem);\n+err3:\n \tfree(xsk->umem.buffer);\n-err_umem:\n-#ifdef TX_TIMESTAMP\n-err_hw_ts:\n-#endif\n-\txdp_prog_exit(interface, skb_mode);\n-err_prog:\n+\txsk_umem__delete(xsk->umem.umem);\n+err2:\n+err:\n \tfree(xsk);\n \treturn NULL;\n }\n \n void xdp_close_socket(struct xdp_socket *xsk, const char *interface, bool skb_mode)\n {\n+\tunsigned int if_index;\n+\n \tif (!xsk)\n \t\treturn;\n \n \txsk_socket__delete(xsk->xsk);\n \txsk_umem__delete(xsk->umem.umem);\n \n-\txdp_prog_exit(interface, skb_mode);\n+\tif_index = if_nametoindex(interface);\n+\tif (!if_index) {\n+\t\tfprintf(stderr, \"if_nametoindex() failed\\n\");\n+\t\treturn;\n+\t}\n+\n+\tif (xsk->prog) {\n+\t\txdp_program__detach(xsk->prog, if_index, xdp_flags(skb_mode), 0);\n+\t\txdp_program__close(xsk->prog);\n+\t\tprogram_loaded = 0;\n+\t}\n \n \tfree(xsk->umem.buffer);\n \tfree(xsk);\n@@ -622,9 +623,8 @@ void xdp_complete_tx(struct xdp_socket *xsk)\n }\n \n static unsigned char *xdp_prepare_tx_desc(struct xdp_socket *xsk, struct xdp_desc *tx_desc,\n-\t\t\t\t\t  const struct xdp_tx_time __unused *tx_time,\n-\t\t\t\t\t  int __unused i, bool __unused want_hw_timestamp,\n-\t\t\t\t\t  bool __unused tx)\n+\t\t\t\t\t  const struct xdp_tx_time *tx_time, int i, int num_frames,\n+\t\t\t\t\t  bool tx)\n {\n \tunsigned char *data;\n \n@@ -658,8 +658,8 @@ static unsigned char *xdp_prepare_tx_desc(struct xdp_socket *xsk, struct xdp_des\n \t\t}\n #endif\n #ifdef TX_TIMESTAMP\n-\t\t/* Timestamp first and last frame of each cycle (decision made by caller) */\n-\t\tif (xsk->tx_hwtstamp_mode && want_hw_timestamp) {\n+\t\t/* Timestamp first packet and last packet per cycle */\n+\t\tif (xsk->tx_hwtstamp_mode && (i == 0 || i == num_frames - 1)) {\n \t\t\tmeta->flags |= XDP_TXMD_FLAGS_TIMESTAMP;\n \t\t\t/* Initialize TX HW timestamp */\n \t\t\tmeta->completion.tx_timestamp = 0;\n@@ -697,7 +697,7 @@ void xdp_gen_and_send_frames(struct xdp_socket *xsk, const struct xdp_gen_config\n \t\treturn;\n \t}\n \n-\tapp_clock_get(&tx_time);\n+\tclock_gettime(app_config.application_clock_id, &tx_time);\n \n \tfor (i = 0; i < xdp->num_frames_per_cycle; ++i) {\n \t\tstruct xdp_desc *tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, idx + i);\n@@ -714,8 +714,8 @@ void xdp_gen_and_send_frames(struct xdp_socket *xsk, const struct xdp_gen_config\n \t\t\t\t     XSK_RING_PROD__DEFAULT_NUM_DESCS;\n \n \t\t/* Get frame and prepare it */\n-\t\tdata = xdp_prepare_tx_desc(xsk, tx_desc, xdp->tx_time, i,\n-\t\t\t\t\t   i == 0 || i == xdp->num_frames_per_cycle - 1, true);\n+\t\tdata = xdp_prepare_tx_desc(xsk, tx_desc, xdp->tx_time, i, xdp->num_frames_per_cycle,\n+\t\t\t\t\t   true);\n \n \t\tframe_config.mode = xdp->mode;\n \t\tframe_config.security_context = xdp->security_context;\n@@ -758,6 +758,9 @@ void xdp_gen_and_send_frames(struct xdp_socket *xsk, const struct xdp_gen_config\n \t\t */\n \t\tstat_frames_sent_batch(xdp->frame_type, xdp->sequence_counter_begin,\n \t\t\t\t       xdp->num_frames_per_cycle);\n+\n+\t\t/* Update the sequence number for expected HW timestamp in next cycle */\n+\t\txsk->tx_hwts.seq_lagged = xdp->sequence_counter_begin;\n \t\treturn;\n \t}\n #endif\n@@ -854,30 +857,8 @@ unsigned int xdp_receive_frames(struct xdp_socket *xsk, size_t frame_length, boo\n \t\t\ttx_desc->len = frame_length;\n \n \t\t\t/* Prepare tx desc with Tx Time or Tx HW timestamp */\n-\t\t\tif (xsk->tx_time_mode || xsk->tx_hwtstamp_mode) {\n-\t\t\t\tbool want_hw_ts = false;\n-\n-#ifdef TX_TIMESTAMP\n-\t\t\t\t/*\n-\t\t\t\t * Decide first/last HW-timestamp from the frame's\n-\t\t\t\t * own position within its cycle, not from the RX\n-\t\t\t\t * batch position. The RX batch is not aligned to\n-\t\t\t\t * the sender's cycle boundaries (e.g. one frame at\n-\t\t\t\t * a time), so using the batch index would attach\n-\t\t\t\t * the TIMESTAMP flag to the wrong frame.\n-\t\t\t\t */\n-\t\t\t\tif (xsk->tx_hwtstamp_mode) {\n-\t\t\t\t\tuint64_t fpc = xsk->tx_hwts.frames_per_cycle;\n-\t\t\t\t\tuint64_t seq = get_sequence_counter(\n-\t\t\t\t\t\tpacket, xsk->tx_hwts.meta_data_offset, fpc);\n-\t\t\t\t\tuint64_t frame_in_cycle = seq % fpc;\n-\n-\t\t\t\t\twant_hw_ts = (frame_in_cycle == 0) ||\n-\t\t\t\t\t\t     (frame_in_cycle == fpc - 1);\n-\t\t\t\t}\n-#endif\n-\t\t\t\txdp_prepare_tx_desc(xsk, tx_desc, tx_time, i, want_hw_ts, false);\n-\t\t\t}\n+\t\t\tif (xsk->tx_time_mode || xsk->tx_hwtstamp_mode)\n+\t\t\t\txdp_prepare_tx_desc(xsk, tx_desc, tx_time, i, received, false);\n \t\t} else {\n \t\t\t/* Move buffer back to fill queue */\n \t\t\t*xsk_ring_prod__fill_addr(&xsk->umem.fq, idx_fq++) = orig;\ndiff --git a/src/xdp.h b/src/xdp.h\nindex 0652b73..301a3e8 100644\n--- a/src/xdp.h\n+++ b/src/xdp.h\n@@ -1,6 +1,6 @@\n /* SPDX-License-Identifier: BSD-2-Clause */\n /*\n- * Copyright (C) 2021-2026 Linutronix GmbH\n+ * Copyright (C) 2021-2025 Linutronix GmbH\n  * Author Kurt Kanzenbach <kurt@linutronix.de>\n  */\n \n@@ -43,10 +43,12 @@ struct xsk_umem_info {\n struct xdp_tx_hwts {\n \t/* Store HW timestamps from AF_XDP completion queue */\n \tstruct round_trip_context *rtt;\n+\t/* Sequence number for TX HW timestamp (available one cycle later) */\n+\tint64_t seq_lagged;\n+\t/* Counter for tracking TX HW timestamp completions in current cycle (max 2: first+last) */\n+\tuint32_t count;\n \t/* Total number of frames per cycle (needed to identify last packet) */\n \tuint32_t frames_per_cycle;\n-\t/* Offset of reference_meta_data within the frame (for sequence counter extraction) */\n-\tuint32_t meta_data_offset;\n };\n \n struct xdp_socket {\n@@ -55,6 +57,7 @@ struct xdp_socket {\n \tstruct xsk_ring_prod tx;\n \tstruct xsk_umem_info umem;\n \tstruct xsk_socket *xsk;\n+\tstruct xdp_program *prog;\n \t/* TX hardware timestamping context */\n \tstruct xdp_tx_hwts tx_hwts;\n \tint fd;\ndiff --git a/src/xdp_kern_avtp_vid400.c b/src/xdp_kern_avtp_vid400.c\nnew file mode 100644\nindex 0000000..9e738a3\n--- /dev/null\n+++ b/src/xdp_kern_avtp_vid400.c\n@@ -0,0 +1,63 @@\n+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)\n+/*\n+ * Copyright (C) 2022 Linutronix GmbH\n+ * Author Kurt Kanzenbach <kurt@linutronix.de>\n+ */\n+\n+#include <linux/bpf.h>\n+\n+#include <bpf/bpf_endian.h>\n+#include <bpf/bpf_helpers.h>\n+\n+#include <xdp/xdp_helpers.h>\n+\n+#include \"net_def.h\"\n+#include \"xdp_metadata.c\"\n+\n+struct {\n+\t__uint(type, BPF_MAP_TYPE_XSKMAP);\n+\t__uint(key_size, sizeof(int));\n+\t__uint(value_size, sizeof(int));\n+\t__uint(max_entries, 128);\n+} xsks_map SEC(\".maps\");\n+\n+struct {\n+\t__uint(priority, 20);\n+\t__uint(XDP_PASS, 1);\n+} XDP_RUN_CONFIG(xdp_sock_prog);\n+\n+SEC(\"xdp_sock\")\n+int xdp_sock_prog(struct xdp_md *ctx)\n+{\n+\tvoid *data_end = (void *)(long)ctx->data_end;\n+\tvoid *data = (void *)(long)ctx->data;\n+\tstruct vlan_ethernet_header *veth;\n+\tint idx = ctx->rx_queue_index;\n+\tvoid *p = data;\n+\n+\tveth = p;\n+\tif ((void *)(veth + 1) > data_end)\n+\t\treturn XDP_PASS;\n+\n+\t/* Check for VLAN frames */\n+\tif (veth->vlan_proto != bpf_htons(ETH_P_8021Q))\n+\t\treturn XDP_PASS;\n+\n+\t/* Check for AVTP EtherType */\n+\tif (veth->vlan_encapsulated_proto != bpf_htons(ETH_P_AVTP))\n+\t\treturn XDP_PASS;\n+\n+\t/* Check for VID 400 */\n+\tif ((bpf_ntohs(veth->vlantci) & VLAN_ID_MASK) != 400)\n+\t\treturn XDP_PASS;\n+\n+\t/* If socket bound to rx_queue then redirect to user space */\n+\tif (bpf_map_lookup_elem(&xsks_map, &idx)) {\n+\t\tpopulate_rx_timestamp(ctx);\n+\t\treturn bpf_redirect_map(&xsks_map, idx, 0);\n+\t}\n+\n+\treturn XDP_PASS;\n+}\n+\n+char _license[] SEC(\"license\") = \"Dual BSD/GPL\";\ndiff --git a/src/xdp_kern_opcua_vid100.c b/src/xdp_kern_opcua_vid100.c\nnew file mode 100644\nindex 0000000..4f1e485\n--- /dev/null\n+++ b/src/xdp_kern_opcua_vid100.c\n@@ -0,0 +1,63 @@\n+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)\n+/*\n+ * Copyright (C) 2022 Linutronix GmbH\n+ * Author Kurt Kanzenbach <kurt@linutronix.de>\n+ */\n+\n+#include <linux/bpf.h>\n+\n+#include <bpf/bpf_endian.h>\n+#include <bpf/bpf_helpers.h>\n+\n+#include <xdp/xdp_helpers.h>\n+\n+#include \"net_def.h\"\n+#include \"xdp_metadata.c\"\n+\n+struct {\n+\t__uint(type, BPF_MAP_TYPE_XSKMAP);\n+\t__uint(key_size, sizeof(int));\n+\t__uint(value_size, sizeof(int));\n+\t__uint(max_entries, 128);\n+} xsks_map SEC(\".maps\");\n+\n+struct {\n+\t__uint(priority, 20);\n+\t__uint(XDP_PASS, 1);\n+} XDP_RUN_CONFIG(xdp_sock_prog);\n+\n+SEC(\"xdp_sock\")\n+int xdp_sock_prog(struct xdp_md *ctx)\n+{\n+\tvoid *data_end = (void *)(long)ctx->data_end;\n+\tvoid *data = (void *)(long)ctx->data;\n+\tstruct vlan_ethernet_header *veth;\n+\tint idx = ctx->rx_queue_index;\n+\tvoid *p = data;\n+\n+\tveth = p;\n+\tif ((void *)(veth + 1) > data_end)\n+\t\treturn XDP_PASS;\n+\n+\t/* Check for VLAN frames */\n+\tif (veth->vlan_proto != bpf_htons(ETH_P_8021Q))\n+\t\treturn XDP_PASS;\n+\n+\t/* Check for OPC/UA EtherType */\n+\tif (veth->vlan_encapsulated_proto != bpf_htons(ETH_P_OPCUA_PUBSUB))\n+\t\treturn XDP_PASS;\n+\n+\t/* Check for VID 100 */\n+\tif ((bpf_ntohs(veth->vlantci) & VLAN_ID_MASK) != 100)\n+\t\treturn XDP_PASS;\n+\n+\t/* If socket bound to rx_queue then redirect to user space */\n+\tif (bpf_map_lookup_elem(&xsks_map, &idx)) {\n+\t\tpopulate_rx_timestamp(ctx);\n+\t\treturn bpf_redirect_map(&xsks_map, idx, 0);\n+\t}\n+\n+\treturn XDP_PASS;\n+}\n+\n+char _license[] SEC(\"license\") = \"Dual BSD/GPL\";\ndiff --git a/src/xdp_kern_opcua_vid200.c b/src/xdp_kern_opcua_vid200.c\nnew file mode 100644\nindex 0000000..b925f06\n--- /dev/null\n+++ b/src/xdp_kern_opcua_vid200.c\n@@ -0,0 +1,63 @@\n+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)\n+/*\n+ * Copyright (C) 2022 Linutronix GmbH\n+ * Author Kurt Kanzenbach <kurt@linutronix.de>\n+ */\n+\n+#include <linux/bpf.h>\n+\n+#include <bpf/bpf_endian.h>\n+#include <bpf/bpf_helpers.h>\n+\n+#include <xdp/xdp_helpers.h>\n+\n+#include \"net_def.h\"\n+#include \"xdp_metadata.c\"\n+\n+struct {\n+\t__uint(type, BPF_MAP_TYPE_XSKMAP);\n+\t__uint(key_size, sizeof(int));\n+\t__uint(value_size, sizeof(int));\n+\t__uint(max_entries, 128);\n+} xsks_map SEC(\".maps\");\n+\n+struct {\n+\t__uint(priority, 20);\n+\t__uint(XDP_PASS, 1);\n+} XDP_RUN_CONFIG(xdp_sock_prog);\n+\n+SEC(\"xdp_sock\")\n+int xdp_sock_prog(struct xdp_md *ctx)\n+{\n+\tvoid *data_end = (void *)(long)ctx->data_end;\n+\tvoid *data = (void *)(long)ctx->data;\n+\tstruct vlan_ethernet_header *veth;\n+\tint idx = ctx->rx_queue_index;\n+\tvoid *p = data;\n+\n+\tveth = p;\n+\tif ((void *)(veth + 1) > data_end)\n+\t\treturn XDP_PASS;\n+\n+\t/* Check for VLAN frames */\n+\tif (veth->vlan_proto != bpf_htons(ETH_P_8021Q))\n+\t\treturn XDP_PASS;\n+\n+\t/* Check for OPC/UA EtherType */\n+\tif (veth->vlan_encapsulated_proto != bpf_htons(ETH_P_OPCUA_PUBSUB))\n+\t\treturn XDP_PASS;\n+\n+\t/* Check for VID 200 */\n+\tif ((bpf_ntohs(veth->vlantci) & VLAN_ID_MASK) != 200)\n+\t\treturn XDP_PASS;\n+\n+\t/* If socket bound to rx_queue then redirect to user space */\n+\tif (bpf_map_lookup_elem(&xsks_map, &idx)) {\n+\t\tpopulate_rx_timestamp(ctx);\n+\t\treturn bpf_redirect_map(&xsks_map, idx, 0);\n+\t}\n+\n+\treturn XDP_PASS;\n+}\n+\n+char _license[] SEC(\"license\") = \"Dual BSD/GPL\";\ndiff --git a/src/xdp_kern_opcua_vid300.c b/src/xdp_kern_opcua_vid300.c\nnew file mode 100644\nindex 0000000..58f56fc\n--- /dev/null\n+++ b/src/xdp_kern_opcua_vid300.c\n@@ -0,0 +1,63 @@\n+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)\n+/*\n+ * Copyright (C) 2022 Linutronix GmbH\n+ * Author Kurt Kanzenbach <kurt@linutronix.de>\n+ */\n+\n+#include <linux/bpf.h>\n+\n+#include <bpf/bpf_endian.h>\n+#include <bpf/bpf_helpers.h>\n+\n+#include <xdp/xdp_helpers.h>\n+\n+#include \"net_def.h\"\n+#include \"xdp_metadata.c\"\n+\n+struct {\n+\t__uint(type, BPF_MAP_TYPE_XSKMAP);\n+\t__uint(key_size, sizeof(int));\n+\t__uint(value_size, sizeof(int));\n+\t__uint(max_entries, 128);\n+} xsks_map SEC(\".maps\");\n+\n+struct {\n+\t__uint(priority, 20);\n+\t__uint(XDP_PASS, 1);\n+} XDP_RUN_CONFIG(xdp_sock_prog);\n+\n+SEC(\"xdp_sock\")\n+int xdp_sock_prog(struct xdp_md *ctx)\n+{\n+\tvoid *data_end = (void *)(long)ctx->data_end;\n+\tvoid *data = (void *)(long)ctx->data;\n+\tstruct vlan_ethernet_header *veth;\n+\tint idx = ctx->rx_queue_index;\n+\tvoid *p = data;\n+\n+\tveth = p;\n+\tif ((void *)(veth + 1) > data_end)\n+\t\treturn XDP_PASS;\n+\n+\t/* Check for VLAN frames */\n+\tif (veth->vlan_proto != bpf_htons(ETH_P_8021Q))\n+\t\treturn XDP_PASS;\n+\n+\t/* Check for OPC/UA EtherType */\n+\tif (veth->vlan_encapsulated_proto != bpf_htons(ETH_P_OPCUA_PUBSUB))\n+\t\treturn XDP_PASS;\n+\n+\t/* Check for VID 300 */\n+\tif ((bpf_ntohs(veth->vlantci) & VLAN_ID_MASK) != 300)\n+\t\treturn XDP_PASS;\n+\n+\t/* If socket bound to rx_queue then redirect to user space */\n+\tif (bpf_map_lookup_elem(&xsks_map, &idx)) {\n+\t\tpopulate_rx_timestamp(ctx);\n+\t\treturn bpf_redirect_map(&xsks_map, idx, 0);\n+\t}\n+\n+\treturn XDP_PASS;\n+}\n+\n+char _license[] SEC(\"license\") = \"Dual BSD/GPL\";\ndiff --git a/src/xdp_kern_tb.c b/src/xdp_kern_profinet_vid100.c\nsimilarity index 61%\nrename from src/xdp_kern_tb.c\nrename to src/xdp_kern_profinet_vid100.c\nindex 38744d4..5ced243 100644\n--- a/src/xdp_kern_tb.c\n+++ b/src/xdp_kern_profinet_vid100.c\n@@ -1,14 +1,9 @@\n // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)\n /*\n- * Copyright (C) 2021-2026 Linutronix GmbH\n+ * Copyright (C) 2021,2022 Linutronix GmbH\n  * Author Kurt Kanzenbach <kurt@linutronix.de>\n- *\n- * Skeleton for XDP eBPF filter. Define EBPF_VID, EBPF_ETH_TYPE and EBPF_PRIORITY.  Set\n- * EBPF_CHECK_FRAMEID for Profinet when compiling.\n  */\n \n-#include <stdbool.h>\n-\n #include <linux/bpf.h>\n #include <linux/types.h>\n \n@@ -20,18 +15,6 @@\n #include \"net_def.h\"\n #include \"xdp_metadata.c\"\n \n-#ifndef EBPF_VID\n-#error \"Please compile this with -DEBPF_VID=<VLAN>\"\n-#endif\n-\n-#ifndef EBPF_ETH_TYPE\n-#error \"Please compile this with -DEBPF_ETH_TYPE=<EtherType>\"\n-#endif\n-\n-#ifndef EBPF_PRIORITY\n-#error \"Please compile this with -DEBPF_PRIORITY=<priority>\"\n-#endif\n-\n struct {\n \t__uint(type, BPF_MAP_TYPE_XSKMAP);\n \t__uint(key_size, sizeof(int));\n@@ -40,32 +23,10 @@ struct {\n } xsks_map SEC(\".maps\");\n \n struct {\n-\t__uint(priority, EBPF_PRIORITY);\n+\t__uint(priority, 10);\n \t__uint(XDP_PASS, 1);\n } XDP_RUN_CONFIG(xdp_sock_prog);\n \n-#ifdef EBPF_CHECK_FRAMEID\n-static __always_inline bool check_frame_id(struct profinet_rt_header *rt)\n-{\n-\t__u16 frame_id;\n-\n-\tframe_id = bpf_ntohs(rt->frame_id);\n-\tswitch (frame_id) {\n-\tcase TSN_HIGH_FRAMEID:\n-\tcase TSN_HIGH_SEC_FRAMEID:\n-\tcase TSN_LOW_FRAMEID:\n-\tcase TSN_LOW_SEC_FRAMEID:\n-\tcase RTC_FRAMEID:\n-\tcase RTC_SEC_FRAMEID:\n-\tcase RTA_FRAMEID:\n-\tcase RTA_SEC_FRAMEID:\n-\t\treturn true;\n-\tdefault:\n-\t\treturn false;\n-\t}\n-}\n-#endif\n-\n SEC(\"xdp_sock\")\n int xdp_sock_prog(struct xdp_md *ctx)\n {\n@@ -73,6 +34,8 @@ int xdp_sock_prog(struct xdp_md *ctx)\n \tvoid *data = (void *)(long)ctx->data;\n \tstruct vlan_ethernet_header *veth;\n \tint idx = ctx->rx_queue_index;\n+\tstruct profinet_rt_header *rt;\n+\t__be16 frame_id;\n \tvoid *p = data;\n \n \tveth = p;\n@@ -80,25 +43,41 @@ int xdp_sock_prog(struct xdp_md *ctx)\n \t\treturn XDP_PASS;\n \tp += sizeof(*veth);\n \n+\t/* Check for VLAN frames */\n \tif (veth->vlan_proto != bpf_htons(ETH_P_8021Q))\n \t\treturn XDP_PASS;\n \n-\tif (veth->vlan_encapsulated_proto != bpf_htons(EBPF_ETH_TYPE))\n+\t/* Check for valid Profinet frames */\n+\tif (veth->vlan_encapsulated_proto != bpf_htons(ETH_P_PROFINET_RT))\n \t\treturn XDP_PASS;\n \n-\tif ((bpf_ntohs(veth->vlantci) & VLAN_ID_MASK) != EBPF_VID)\n+\t/* Check for VID 100 */\n+\tif ((bpf_ntohs(veth->vlantci) & VLAN_ID_MASK) != 100)\n \t\treturn XDP_PASS;\n \n-#ifdef EBPF_CHECK_FRAMEID\n-\tstruct profinet_rt_header *rt = p;\n-\n+\t/* Check frameId range */\n+\trt = p;\n \tif ((void *)(rt + 1) > data_end)\n \t\treturn XDP_PASS;\n+\tp += sizeof(*rt);\n \n-\tif (!check_frame_id(rt))\n+\tframe_id = bpf_htons(rt->frame_id);\n+\tswitch (frame_id) {\n+\tcase TSN_HIGH_FRAMEID:\n+\tcase TSN_HIGH_SEC_FRAMEID:\n+\tcase TSN_LOW_FRAMEID:\n+\tcase TSN_LOW_SEC_FRAMEID:\n+\tcase RTC_FRAMEID:\n+\tcase RTC_SEC_FRAMEID:\n+\tcase RTA_FRAMEID:\n+\tcase RTA_SEC_FRAMEID:\n+\t\tgoto redirect;\n+\tdefault:\n \t\treturn XDP_PASS;\n-#endif\n+\t}\n \n+redirect:\n+\t/* If socket bound to rx_queue then redirect to user space */\n \tif (bpf_map_lookup_elem(&xsks_map, &idx)) {\n \t\tpopulate_rx_timestamp(ctx);\n \t\treturn bpf_redirect_map(&xsks_map, idx, 0);\ndiff --git a/src/xdp_kern_profinet_vid200.c b/src/xdp_kern_profinet_vid200.c\nnew file mode 100644\nindex 0000000..a357e02\n--- /dev/null\n+++ b/src/xdp_kern_profinet_vid200.c\n@@ -0,0 +1,89 @@\n+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)\n+/*\n+ * Copyright (C) 2021,2022 Linutronix GmbH\n+ * Author Kurt Kanzenbach <kurt@linutronix.de>\n+ */\n+\n+#include <linux/bpf.h>\n+#include <linux/types.h>\n+\n+#include <bpf/bpf_endian.h>\n+#include <bpf/bpf_helpers.h>\n+\n+#include <xdp/xdp_helpers.h>\n+\n+#include \"net_def.h\"\n+#include \"xdp_metadata.c\"\n+\n+struct {\n+\t__uint(type, BPF_MAP_TYPE_XSKMAP);\n+\t__uint(key_size, sizeof(int));\n+\t__uint(value_size, sizeof(int));\n+\t__uint(max_entries, 128);\n+} xsks_map SEC(\".maps\");\n+\n+struct {\n+\t__uint(priority, 10);\n+\t__uint(XDP_PASS, 1);\n+} XDP_RUN_CONFIG(xdp_sock_prog);\n+\n+SEC(\"xdp_sock\")\n+int xdp_sock_prog(struct xdp_md *ctx)\n+{\n+\tvoid *data_end = (void *)(long)ctx->data_end;\n+\tvoid *data = (void *)(long)ctx->data;\n+\tstruct vlan_ethernet_header *veth;\n+\tint idx = ctx->rx_queue_index;\n+\tstruct profinet_rt_header *rt;\n+\t__be16 frame_id;\n+\tvoid *p = data;\n+\n+\tveth = p;\n+\tif ((void *)(veth + 1) > data_end)\n+\t\treturn XDP_PASS;\n+\tp += sizeof(*veth);\n+\n+\t/* Check for VLAN frames */\n+\tif (veth->vlan_proto != bpf_htons(ETH_P_8021Q))\n+\t\treturn XDP_PASS;\n+\n+\t/* Check for valid Profinet frames */\n+\tif (veth->vlan_encapsulated_proto != bpf_htons(ETH_P_PROFINET_RT))\n+\t\treturn XDP_PASS;\n+\n+\t/* Check for VID 200 */\n+\tif ((bpf_ntohs(veth->vlantci) & VLAN_ID_MASK) != 200)\n+\t\treturn XDP_PASS;\n+\n+\t/* Check frameId range */\n+\trt = p;\n+\tif ((void *)(rt + 1) > data_end)\n+\t\treturn XDP_PASS;\n+\tp += sizeof(*rt);\n+\n+\tframe_id = bpf_htons(rt->frame_id);\n+\tswitch (frame_id) {\n+\tcase TSN_HIGH_FRAMEID:\n+\tcase TSN_HIGH_SEC_FRAMEID:\n+\tcase TSN_LOW_FRAMEID:\n+\tcase TSN_LOW_SEC_FRAMEID:\n+\tcase RTC_FRAMEID:\n+\tcase RTC_SEC_FRAMEID:\n+\tcase RTA_FRAMEID:\n+\tcase RTA_SEC_FRAMEID:\n+\t\tgoto redirect;\n+\tdefault:\n+\t\treturn XDP_PASS;\n+\t}\n+\n+redirect:\n+\t/* If socket bound to rx_queue then redirect to user space */\n+\tif (bpf_map_lookup_elem(&xsks_map, &idx)) {\n+\t\tpopulate_rx_timestamp(ctx);\n+\t\treturn bpf_redirect_map(&xsks_map, idx, 0);\n+\t}\n+\n+\treturn XDP_PASS;\n+}\n+\n+char _license[] SEC(\"license\") = \"Dual BSD/GPL\";\ndiff --git a/src/xdp_metadata.c b/src/xdp_metadata.c\nindex f02663d..f824b00 100644\n--- a/src/xdp_metadata.c\n+++ b/src/xdp_metadata.c\n@@ -9,7 +9,7 @@\n \n extern int bpf_xdp_metadata_rx_timestamp(const struct xdp_md *ctx, __u64 *timestamp) __ksym;\n \n-static __always_inline void populate_rx_timestamp(struct xdp_md *ctx)\n+void populate_rx_timestamp(struct xdp_md *ctx)\n {\n #ifdef RX_TIMESTAMP\n \tstruct xdp_meta *meta;\n@@ -32,7 +32,7 @@ static __always_inline void populate_rx_timestamp(struct xdp_md *ctx)\n \t\tmeta->rx_hw_timestamp_err = err;\n \t\tmeta->hint_valid = 0;\n \t} else {\n-\t\tmeta->hint_valid = XDP_META_FIELD_TS;\n+\t\tmeta->hint_valid |= XDP_META_FIELD_TS;\n \t}\n #endif\n }\ndiff --git a/tests/busypolling/flow.sh b/tests/busypolling/flow_cml.sh\nsimilarity index 100%\nrename from tests/busypolling/flow.sh\nrename to tests/busypolling/flow_cml.sh\ndiff --git a/tests/busypolling/mirror.sh b/tests/busypolling/mirror.sh\nindex 22c4c14..befbe3d 100755\n--- a/tests/busypolling/mirror.sh\n+++ b/tests/busypolling/mirror.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of mirror application\n cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n+../../build/mirror -c mirror_vid100_cml.yaml >mirror1.log &\n \n exit 0\ndiff --git a/tests/busypolling/mirror.yaml b/tests/busypolling/mirror_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/busypolling/mirror.yaml\nrename to tests/busypolling/mirror_vid100_cml.yaml\ndiff --git a/tests/busypolling/ref.sh b/tests/busypolling/ref.sh\nindex ed207ad..3523e8d 100755\n--- a/tests/busypolling/ref.sh\n+++ b/tests/busypolling/ref.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of reference application\n cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n+../../build/reference -c reference_vid100_cml.yaml >ref1.log &\n \n exit 0\ndiff --git a/tests/busypolling/reference.yaml b/tests/busypolling/reference_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/busypolling/reference.yaml\nrename to tests/busypolling/reference_vid100_cml.yaml\ndiff --git a/tests/busypolling_125us/flow.sh b/tests/busypolling_125us/flow_cml.sh\nsimilarity index 100%\nrename from tests/busypolling_125us/flow.sh\nrename to tests/busypolling_125us/flow_cml.sh\ndiff --git a/tests/busypolling_125us/mirror.sh b/tests/busypolling_125us/mirror.sh\nindex 22c4c14..befbe3d 100755\n--- a/tests/busypolling_125us/mirror.sh\n+++ b/tests/busypolling_125us/mirror.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of mirror application\n cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n+../../build/mirror -c mirror_vid100_cml.yaml >mirror1.log &\n \n exit 0\ndiff --git a/tests/busypolling_125us/mirror.yaml b/tests/busypolling_125us/mirror_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/busypolling_125us/mirror.yaml\nrename to tests/busypolling_125us/mirror_vid100_cml.yaml\ndiff --git a/tests/busypolling_125us/ref.sh b/tests/busypolling_125us/ref.sh\nindex ed207ad..3523e8d 100755\n--- a/tests/busypolling_125us/ref.sh\n+++ b/tests/busypolling_125us/ref.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of reference application\n cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n+../../build/reference -c reference_vid100_cml.yaml >ref1.log &\n \n exit 0\ndiff --git a/tests/busypolling_125us/reference.yaml b/tests/busypolling_125us/reference_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/busypolling_125us/reference.yaml\nrename to tests/busypolling_125us/reference_vid100_cml.yaml\ndiff --git a/tests/busypolling_1ms_rtworkload/flow.sh b/tests/busypolling_1ms_rtworkload/flow_cml.sh\nsimilarity index 100%\nrename from tests/busypolling_1ms_rtworkload/flow.sh\nrename to tests/busypolling_1ms_rtworkload/flow_cml.sh\ndiff --git a/tests/busypolling_1ms_rtworkload/mirror.sh b/tests/busypolling_1ms_rtworkload/mirror.sh\nindex 22c4c14..0dd25cd 100755\n--- a/tests/busypolling_1ms_rtworkload/mirror.sh\n+++ b/tests/busypolling_1ms_rtworkload/mirror.sh\n@@ -11,15 +11,15 @@ set -e\n cd \"$(dirname \"$0\")\"\n \n # Start PTP\n-../../scripts/ptp.sh enp3s0\n+../../scripts/ptp.sh enp4s0\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp4s0\n sleep 30\n \n # Start one instance of mirror application\n cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n+../../build/mirror -c mirror_vid100_cml.yaml >mirror1.log &\n \n exit 0\ndiff --git a/tests/busypolling_1ms_rtworkload/mirror.yaml b/tests/busypolling_1ms_rtworkload/mirror_vid100_cml.yaml\nsimilarity index 93%\nrename from tests/busypolling_1ms_rtworkload/mirror.yaml\nrename to tests/busypolling_1ms_rtworkload/mirror_vid100_cml.yaml\nindex f091b5c..1a69039 100644\n--- a/tests/busypolling_1ms_rtworkload/mirror.yaml\n+++ b/tests/busypolling_1ms_rtworkload/mirror_vid100_cml.yaml\n@@ -35,18 +35,17 @@ TsnHigh:\n   TsnHighRxThreadPriority: 98\n   TsnHighTxThreadCpu: 2\n   TsnHighRxThreadCpu: 2\n-  TsnHighInterface: enp3s0\n+  TsnHighInterface: enp4s0\n   TsnHighDestination: a8:74:1d:9d:36:14\n   TsnHighRxWorkloadEnabled: true\n   TsnHighRxWorkloadFile: ../workloads/pointer_chasing/pointer_chasing.so\n   TsnHighRxWorkloadSetupFunction: ptr_chase_setup\n   TsnHighRxWorkloadSetupArguments: \"0x4A4000 0x129000\"\n-  TsnHighRxWorkloadTeardownFunction: ptr_chase_teardown\n   TsnHighRxWorkloadFunction: run_ptr_chasing\n   TsnHighRxWorkloadPrewarm: false\n   TsnHighRxWorkloadSkipCount: 100\n   TsnHighRxWorkloadThreadCpu: 2\n-  TsnHighRxWorkloadThreadPriority: 80\n+  TsnHighRxWorkloadThreadPriority: 98\n Log:\n   LogThreadPriority: 1\n   LogThreadCpu: 0\ndiff --git a/tests/busypolling_1ms_rtworkload/ref.sh b/tests/busypolling_1ms_rtworkload/ref.sh\nindex ed207ad..8bf2374 100755\n--- a/tests/busypolling_1ms_rtworkload/ref.sh\n+++ b/tests/busypolling_1ms_rtworkload/ref.sh\n@@ -11,15 +11,15 @@ set -e\n cd \"$(dirname \"$0\")\"\n \n # Start PTP\n-../../scripts/ptp.sh enp3s0\n+../../scripts/ptp.sh enp4s0\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp4s0\n sleep 30\n \n # Start one instance of reference application\n cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n+../../build/reference -c reference_vid100_cml.yaml >ref1.log &\n \n exit 0\ndiff --git a/tests/busypolling_1ms_rtworkload/reference.yaml b/tests/busypolling_1ms_rtworkload/reference.yaml\ndeleted file mode 100644\nindex cf2607a..0000000\n--- a/tests/busypolling_1ms_rtworkload/reference.yaml\n+++ /dev/null\n@@ -1,66 +0,0 @@\n----\n-#\n-# Copyright (C) 2024 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Sample reference YAML configuration file for Intel Comet Lake platforms with\n-# i225 NIC(s).\n-#\n-Application:\n-  ApplicationClockId: CLOCK_TAI\n-  # ApplicationBaseStartTimeNS: 0\n-  ApplicationBaseCycleTimeNS: 1ms\n-  ApplicationTxBaseOffsetNS: 700us\n-  ApplicationRxBaseOffsetNS: 300us\n-  ApplicationXdpProgram: xdp_kern_profinet_vid100.o\n-TsnHigh:\n-  TsnHighEnabled: true\n-  TsnHighXdpEnabled: true\n-  TsnHighXdpSkbMode: false\n-  TsnHighXdpZcMode: true\n-  TsnHighXdpWakeupMode: true\n-  TsnHighXdpBusyPollMode: true\n-  TsnHighTxTimeStampEnabled: false\n-  TsnHighVid: 100\n-  TsnHighNumFramesPerCycle: 32\n-  TsnHighPayloadPattern: |\n-    TsnHighPayloadPattern\n-  TsnHighFrameLength: 128\n-  TsnHighRxQueue: 1\n-  TsnHighTxQueue: 1\n-  TsnHighSocketPriority: 7\n-  TsnHighTxThreadPriority: 98\n-  TsnHighRxThreadPriority: 98\n-  TsnHighTxThreadCpu: 1\n-  TsnHighRxThreadCpu: 1\n-  TsnHighInterface: enp3s0\n-  TsnHighDestination: a8:74:1d:9d:98:d8\n-  TsnHighRxWorkloadEnabled: true\n-  TsnHighRxWorkloadFile: ../workloads/pointer_chasing/pointer_chasing.so\n-  TsnHighRxWorkloadSetupFunction: ptr_chase_setup\n-  TsnHighRxWorkloadSetupArguments: \"0x4A4000 0x129000\"\n-  TsnHighRxWorkloadTeardownFunction: ptr_chase_teardown\n-  TsnHighRxWorkloadFunction: run_ptr_chasing\n-  TsnHighRxWorkloadPrewarm: false\n-  TsnHighRxWorkloadSkipCount: 100\n-  TsnHighRxWorkloadThreadCpu: 1\n-  TsnHighRxWorkloadThreadPriority: 80\n-Log:\n-  LogThreadPriority: 1\n-  LogThreadCpu: 0\n-  LogFile: /var/log/reference_vid100.log\n-  LogLevel: Info\n-LogMqtt:\n-  LogMqtt: false\n-  LogMqttThreadPriority: 10\n-  LogMqttThreadCpu: 0\n-  LogMqttBrokerIP: 127.0.0.1\n-  LogMqttBrokerPort: 1883\n-  LogMqttMeasurementName: reference\n-Debug:\n-  DebugStopTraceOnOutlier: false\n-  DebugStopTraceOnError: false\n-  DebugMonitorMode: false\n-  DebugMonitorDestination: 44:44:44:44:44:44\ndiff --git a/tests/intel_kpi/i226_xdp_1ms_32x128b_57k/reference.yaml b/tests/busypolling_1ms_rtworkload/reference_vid100_cml.yaml\nsimilarity index 86%\nrename from tests/intel_kpi/i226_xdp_1ms_32x128b_57k/reference.yaml\nrename to tests/busypolling_1ms_rtworkload/reference_vid100_cml.yaml\nindex 4692955..d855a8f 100644\n--- a/tests/intel_kpi/i226_xdp_1ms_32x128b_57k/reference.yaml\n+++ b/tests/busypolling_1ms_rtworkload/reference_vid100_cml.yaml\n@@ -1,11 +1,12 @@\n ---\n #\n-# Copyright (C) 2026 Linutronix GmbH\n+# Copyright (C) 2024 Linutronix GmbH\n # Author Kurt Kanzenbach <kurt@linutronix.de>\n #\n # SPDX-License-Identifier: BSD-2-Clause\n #\n-# Sample reference YAML configuration file for Intel platforms with i226 NIC(s).\n+# Sample reference YAML configuration file for Intel Comet Lake platforms with\n+# i225 NIC(s).\n #\n Application:\n   ApplicationClockId: CLOCK_TAI\n@@ -34,16 +35,15 @@ TsnHigh:\n   TsnHighRxThreadPriority: 98\n   TsnHighTxThreadCpu: 1\n   TsnHighRxThreadCpu: 1\n-  TsnHighInterface: enp3s0\n+  TsnHighInterface: enp4s0\n   TsnHighDestination: a8:74:1d:9d:98:d8\n-  TsnHighRxWorkloadEnabled: false\n Log:\n   LogThreadPriority: 1\n   LogThreadCpu: 0\n   LogFile: /var/log/reference_vid100.log\n   LogLevel: Info\n LogMqtt:\n-  LogMqtt: true\n+  LogMqtt: false\n   LogMqttThreadPriority: 10\n   LogMqttThreadCpu: 0\n   LogMqttBrokerIP: 127.0.0.1\ndiff --git a/tests/busypolling_1ms_rtworkload_multi/flow.sh b/tests/busypolling_1ms_rtworkload_multi/flow.sh\ndeleted file mode 100755\nindex c36f2c4..0000000\n--- a/tests/busypolling_1ms_rtworkload_multi/flow.sh\n+++ /dev/null\n@@ -1,66 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2024 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Setup the Tx and Rx traffic flows for Intel i225 for testing XDP busy polling.\n-#\n-\n-set -e\n-\n-source ../lib/common.sh\n-source ../lib/igc.sh\n-\n-#\n-# Command line arguments.\n-#\n-INTERFACE=$1\n-CYCLETIME_NS=$2\n-BASETIME=$3\n-\n-[ -z $INTERFACE ] && INTERFACE=\"enp3s0\"                          # default: enp3s0\n-[ -z $CYCLETIME_NS ] && CYCLETIME_NS=\"1000000\"                   # default: 125us\n-[ -z $BASETIME ] && BASETIME=$(date '+%s000000000' -d '-30 sec') # default: now - 30s\n-\n-load_kernel_modules\n-\n-napi_defer_hard_irqs \"${INTERFACE}\" \"${CYCLETIME_NS}\"\n-\n-igc_start \"${INTERFACE}\"\n-\n-#\n-# Split traffic between TSN streams, priority and everything else.\n-#\n-ENTRY1_NS=\"500000\" # TSN Streams / Prio\n-ENTRY2_NS=\"500000\" # Everything else\n-\n-#\n-# Tx Assignment with Qbv and full hardware offload.\n-#\n-# PCP 6   - Tx Q 1 - TSN High\n-# PCP X   - Tx Q 0 - Everything else\n-#\n-tc qdisc replace dev ${INTERFACE} handle 100 parent root taprio num_tc 2 \\\n-  map 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 \\\n-  queues 1@0 1@1 \\\n-  base-time ${BASETIME} \\\n-  sched-entry S 0x02 ${ENTRY1_NS} \\\n-  sched-entry S 0x03 ${ENTRY2_NS} \\\n-  flags 0x02\n-\n-#\n-# Rx Queues Assignment.\n-#\n-# PCP 6   - Rx Q 1 - TSN High\n-# PCP X   - Rx Q 0 - Everything else\n-#\n-RXQUEUES=(0 1 0 0 0 0 0 0 0 0)\n-igc_rx_queues_assign \"${INTERFACE}\" RXQUEUES\n-\n-igc_end \"${INTERFACE}\"\n-\n-setup_irqs \"${INTERFACE}\"\n-\n-exit 0\ndiff --git a/tests/busypolling_1ms_rtworkload_multi/mirror.sh b/tests/busypolling_1ms_rtworkload_multi/mirror.sh\ndeleted file mode 100755\nindex 22c4c14..0000000\n--- a/tests/busypolling_1ms_rtworkload_multi/mirror.sh\n+++ /dev/null\n@@ -1,25 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2023 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-\n-set -e\n-\n-cd \"$(dirname \"$0\")\"\n-\n-# Start PTP\n-../../scripts/ptp.sh enp3s0\n-sleep 30\n-\n-# Configure flow\n-./flow.sh enp3s0\n-sleep 30\n-\n-# Start one instance of mirror application\n-cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n-\n-exit 0\ndiff --git a/tests/busypolling_1ms_rtworkload_multi/mirror.yaml b/tests/busypolling_1ms_rtworkload_multi/mirror.yaml\ndeleted file mode 100644\nindex 797c664..0000000\n--- a/tests/busypolling_1ms_rtworkload_multi/mirror.yaml\n+++ /dev/null\n@@ -1,66 +0,0 @@\n----\n-#\n-# Copyright (C) 2024 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Sample reference YAML configuration file for Intel Comet Lake platforms with\n-# i225 NIC(s).\n-#\n-Application:\n-  ApplicationClockId: CLOCK_TAI\n-  # ApplicationBaseStartTimeNS: 0\n-  ApplicationBaseCycleTimeNS: 1ms\n-  ApplicationTxBaseOffsetNS: 700us\n-  ApplicationRxBaseOffsetNS: 300us\n-  ApplicationXdpProgram: xdp_kern_profinet_vid100.o\n-TsnHigh:\n-  TsnHighEnabled: true\n-  TsnHighXdpEnabled: true\n-  TsnHighXdpSkbMode: false\n-  TsnHighXdpZcMode: true\n-  TsnHighXdpWakeupMode: true\n-  TsnHighXdpBusyPollMode: true\n-  TsnHighTxTimeStampEnabled: false\n-  TsnHighVid: 100\n-  TsnHighNumFramesPerCycle: 32\n-  TsnHighPayloadPattern: |\n-    TsnHighPayloadPattern\n-  TsnHighFrameLength: 128\n-  TsnHighRxQueue: 1\n-  TsnHighTxQueue: 1\n-  TsnHighSocketPriority: 7\n-  TsnHighTxThreadPriority: 98\n-  TsnHighRxThreadPriority: 98\n-  TsnHighTxThreadCpu: 1\n-  TsnHighRxThreadCpu: 1\n-  TsnHighInterface: enp3s0\n-  TsnHighDestination: a8:74:1d:9d:36:14\n-  TsnHighRxWorkloadEnabled: true\n-  TsnHighRxWorkloadFile: ../workloads/pointer_chasing/pointer_chasing.so\n-  TsnHighRxWorkloadSetupFunction: ptr_chase_setup\n-  TsnHighRxWorkloadSetupArguments: \"0x4A4000 0x129000\"\n-  TsnHighRxWorkloadTeardownFunction: ptr_chase_teardown\n-  TsnHighRxWorkloadFunction: run_ptr_chasing\n-  TsnHighRxWorkloadPrewarm: false\n-  TsnHighRxWorkloadSkipCount: 100\n-  TsnHighRxWorkloadThreadCpu: 0, 1, 2, 3, 4, 5, 6, 7\n-  TsnHighRxWorkloadThreadPriority: 80\n-Log:\n-  LogThreadPriority: 1\n-  LogThreadCpu: 0\n-  LogFile: /var/log/mirror_vid100.log\n-  LogLevel: Info\n-LogMqtt:\n-  LogMqtt: false\n-  LogMqttThreadPriority: 10\n-  LogMqttThreadCpu: 0\n-  LogMqttBrokerIP: 127.0.0.1\n-  LogMqttBrokerPort: 1883\n-  LogMqttMeasurementName: mirror\n-Debug:\n-  DebugStopTraceOnOutlier: false\n-  DebugStopTraceOnError: false\n-  DebugMonitorMode: false\n-  DebugMonitorDestination: 44:44:44:44:44:44\ndiff --git a/tests/busypolling_1ms_rtworkload_multi/ref.sh b/tests/busypolling_1ms_rtworkload_multi/ref.sh\ndeleted file mode 100755\nindex ed207ad..0000000\n--- a/tests/busypolling_1ms_rtworkload_multi/ref.sh\n+++ /dev/null\n@@ -1,25 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2023 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-\n-set -e\n-\n-cd \"$(dirname \"$0\")\"\n-\n-# Start PTP\n-../../scripts/ptp.sh enp3s0\n-sleep 30\n-\n-# Configure flow\n-./flow.sh enp3s0\n-sleep 30\n-\n-# Start one instance of reference application\n-cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n-\n-exit 0\ndiff --git a/tests/busypolling_1ms_rtworkload_multi/reference.yaml b/tests/busypolling_1ms_rtworkload_multi/reference.yaml\ndeleted file mode 100644\nindex da73bef..0000000\n--- a/tests/busypolling_1ms_rtworkload_multi/reference.yaml\n+++ /dev/null\n@@ -1,66 +0,0 @@\n----\n-#\n-# Copyright (C) 2024 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Sample reference YAML configuration file for Intel Comet Lake platforms with\n-# i225 NIC(s).\n-#\n-Application:\n-  ApplicationClockId: CLOCK_TAI\n-  # ApplicationBaseStartTimeNS: 0\n-  ApplicationBaseCycleTimeNS: 1ms\n-  ApplicationTxBaseOffsetNS: 700us\n-  ApplicationRxBaseOffsetNS: 300us\n-  ApplicationXdpProgram: xdp_kern_profinet_vid100.o\n-TsnHigh:\n-  TsnHighEnabled: true\n-  TsnHighXdpEnabled: true\n-  TsnHighXdpSkbMode: false\n-  TsnHighXdpZcMode: true\n-  TsnHighXdpWakeupMode: true\n-  TsnHighXdpBusyPollMode: true\n-  TsnHighTxTimeStampEnabled: false\n-  TsnHighVid: 100\n-  TsnHighNumFramesPerCycle: 32\n-  TsnHighPayloadPattern: |\n-    TsnHighPayloadPattern\n-  TsnHighFrameLength: 128\n-  TsnHighRxQueue: 1\n-  TsnHighTxQueue: 1\n-  TsnHighSocketPriority: 7\n-  TsnHighTxThreadPriority: 98\n-  TsnHighRxThreadPriority: 98\n-  TsnHighTxThreadCpu: 1\n-  TsnHighRxThreadCpu: 1\n-  TsnHighInterface: enp3s0\n-  TsnHighDestination: a8:74:1d:9d:98:d8\n-  TsnHighRxWorkloadEnabled: true\n-  TsnHighRxWorkloadFile: ../workloads/pointer_chasing/pointer_chasing.so\n-  TsnHighRxWorkloadSetupFunction: ptr_chase_setup\n-  TsnHighRxWorkloadSetupArguments: \"0x4A4000 0x129000\"\n-  TsnHighRxWorkloadTeardownFunction: ptr_chase_teardown\n-  TsnHighRxWorkloadFunction: run_ptr_chasing\n-  TsnHighRxWorkloadPrewarm: false\n-  TsnHighRxWorkloadSkipCount: 100\n-  TsnHighRxWorkloadThreadCpu: 0, 1, 2, 3, 4, 5, 6, 7\n-  TsnHighRxWorkloadThreadPriority: 80\n-Log:\n-  LogThreadPriority: 1\n-  LogThreadCpu: 0\n-  LogFile: /var/log/reference_vid100.log\n-  LogLevel: Info\n-LogMqtt:\n-  LogMqtt: false\n-  LogMqttThreadPriority: 10\n-  LogMqttThreadCpu: 0\n-  LogMqttBrokerIP: 127.0.0.1\n-  LogMqttBrokerPort: 1883\n-  LogMqttMeasurementName: reference\n-Debug:\n-  DebugStopTraceOnOutlier: false\n-  DebugStopTraceOnError: false\n-  DebugMonitorMode: false\n-  DebugMonitorDestination: 44:44:44:44:44:44\ndiff --git a/tests/busypolling_250us/flow.sh b/tests/busypolling_250us/flow_cml.sh\nsimilarity index 100%\nrename from tests/busypolling_250us/flow.sh\nrename to tests/busypolling_250us/flow_cml.sh\ndiff --git a/tests/busypolling_250us/mirror.sh b/tests/busypolling_250us/mirror.sh\nindex 22c4c14..befbe3d 100755\n--- a/tests/busypolling_250us/mirror.sh\n+++ b/tests/busypolling_250us/mirror.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of mirror application\n cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n+../../build/mirror -c mirror_vid100_cml.yaml >mirror1.log &\n \n exit 0\ndiff --git a/tests/busypolling_250us/mirror.yaml b/tests/busypolling_250us/mirror_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/busypolling_250us/mirror.yaml\nrename to tests/busypolling_250us/mirror_vid100_cml.yaml\ndiff --git a/tests/busypolling_250us/ref.sh b/tests/busypolling_250us/ref.sh\nindex ed207ad..3523e8d 100755\n--- a/tests/busypolling_250us/ref.sh\n+++ b/tests/busypolling_250us/ref.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of reference application\n cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n+../../build/reference -c reference_vid100_cml.yaml >ref1.log &\n \n exit 0\ndiff --git a/tests/busypolling_250us/reference.yaml b/tests/busypolling_250us/reference_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/busypolling_250us/reference.yaml\nrename to tests/busypolling_250us/reference_vid100_cml.yaml\ndiff --git a/tests/busypolling_8ms/flow.sh b/tests/busypolling_8ms/flow_cml.sh\nsimilarity index 100%\nrename from tests/busypolling_8ms/flow.sh\nrename to tests/busypolling_8ms/flow_cml.sh\ndiff --git a/tests/busypolling_8ms/mirror.sh b/tests/busypolling_8ms/mirror.sh\nindex 07df965..04bf273 100755\n--- a/tests/busypolling_8ms/mirror.sh\n+++ b/tests/busypolling_8ms/mirror.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of mirror application\n cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n+../../build/mirror -c mirror_vid100_cml.yaml >mirror1.log &\n \n exit 0\ndiff --git a/tests/busypolling_8ms/mirror.yaml b/tests/busypolling_8ms/mirror_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/busypolling_8ms/mirror.yaml\nrename to tests/busypolling_8ms/mirror_vid100_cml.yaml\ndiff --git a/tests/busypolling_8ms/ref.sh b/tests/busypolling_8ms/ref.sh\nindex 39e7a97..5074f4b 100755\n--- a/tests/busypolling_8ms/ref.sh\n+++ b/tests/busypolling_8ms/ref.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of reference application\n cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n+../../build/reference -c reference_vid100_cml.yaml >ref1.log &\n \n exit 0\ndiff --git a/tests/busypolling_8ms/reference.yaml b/tests/busypolling_8ms/reference_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/busypolling_8ms/reference.yaml\nrename to tests/busypolling_8ms/reference_vid100_cml.yaml\ndiff --git a/tests/busypolling_i210/flow.sh b/tests/busypolling_i210/flow_apl.sh\nsimilarity index 100%\nrename from tests/busypolling_i210/flow.sh\nrename to tests/busypolling_i210/flow_apl.sh\ndiff --git a/tests/busypolling_i210/mirror.sh b/tests/busypolling_i210/mirror.sh\nindex 17a4221..0705a57 100755\n--- a/tests/busypolling_i210/mirror.sh\n+++ b/tests/busypolling_i210/mirror.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp2s0\n+./flow_apl.sh enp2s0\n sleep 30\n \n # Start one instance of mirror application\n cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n+../../build/mirror -c mirror_vid100_apl.yaml >mirror1.log &\n \n exit 0\ndiff --git a/tests/busypolling_i210/mirror.yaml b/tests/busypolling_i210/mirror_vid100_apl.yaml\nsimilarity index 100%\nrename from tests/busypolling_i210/mirror.yaml\nrename to tests/busypolling_i210/mirror_vid100_apl.yaml\ndiff --git a/tests/busypolling_i210/ref.sh b/tests/busypolling_i210/ref.sh\nindex 15f948f..52e485c 100755\n--- a/tests/busypolling_i210/ref.sh\n+++ b/tests/busypolling_i210/ref.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp2s0\n+./flow_apl.sh enp2s0\n sleep 30\n \n # Start one instance of reference application\n cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n+../../build/reference -c reference_vid100_apl.yaml >ref1.log &\n \n exit 0\ndiff --git a/tests/busypolling_i210/reference.yaml b/tests/busypolling_i210/reference_vid100_apl.yaml\nsimilarity index 100%\nrename from tests/busypolling_i210/reference.yaml\nrename to tests/busypolling_i210/reference_vid100_apl.yaml\ndiff --git a/tests/busypolling_napiconf/flow.sh b/tests/busypolling_napiconf/flow.sh\ndeleted file mode 100755\nindex 5a1d79f..0000000\n--- a/tests/busypolling_napiconf/flow.sh\n+++ /dev/null\n@@ -1,76 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2023-2025 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Setup the Tx and Rx traffic flows for Intel i226 for testing XDP busy polling.\n-#\n-\n-set -e\n-\n-source ../lib/common.sh\n-source ../lib/igc.sh\n-\n-#\n-# Command line arguments.\n-#\n-INTERFACE=$1\n-\n-#\n-# Config.\n-#\n-CYCLETIME_NS=\"1000000\"\n-BASETIME=$(date '+%s000000000' -d '-30 sec')\n-NAPICTL=\"../../build/napictl\"\n-\n-[ -z $INTERFACE ] && INTERFACE=\"enp3s0\" # default: enp3s0\n-\n-load_kernel_modules\n-\n-#\n-# Configure napi-defer-hard-irqs and gro-flush-timeout for queue 0.\n-#\n-napi_defer_hard_irqs_queue \"${NAPICTL}\" \"${INTERFACE}\" \"${CYCLETIME_NS}\" 0\n-\n-igc_start \"${INTERFACE}\"\n-\n-#\n-# Split traffic between TSN streams, priority and everything else.\n-#\n-ENTRY1_NS=$(echo \"$CYCLETIME_NS * 50 / 100\" | bc) # RTC\n-ENTRY2_NS=$(echo \"$CYCLETIME_NS * 25 / 100\" | bc) # TSN Streams / Prio\n-ENTRY3_NS=$(echo \"$CYCLETIME_NS * 25 / 100\" | bc) # Everything else\n-\n-#\n-# Tx Assignment with Qbv and full hardware offload.\n-#\n-# PCP 4   - Rx Q 0 - RTC\n-# PCP 6/5 - Rx Q 1 - TSN Streams / Prio\n-# PCP X   - Rx Q 2 - Everything else\n-#\n-tc qdisc replace dev ${INTERFACE} handle 100 parent root taprio num_tc 3 \\\n-  map 2 2 2 2 2 2 1 0 2 2 2 2 2 2 2 2 \\\n-  queues 1@0 1@1 2@2 \\\n-  base-time ${BASETIME} \\\n-  sched-entry S 0x01 ${ENTRY1_NS} \\\n-  sched-entry S 0x02 ${ENTRY2_NS} \\\n-  sched-entry S 0x04 ${ENTRY3_NS} \\\n-  flags 0x02\n-\n-#\n-# Rx Queues Assignment.\n-#\n-# PCP 4   - Rx Q 0 - RTC\n-# PCP 6/5 - Rx Q 1 - TSN Streams / Prio\n-# PCP X   - Rx Q 2 - Everything else\n-#\n-RXQUEUES=(2 1 1 0 2 2 2 2 1 1)\n-igc_rx_queues_assign \"${INTERFACE}\" RXQUEUES\n-\n-igc_end \"${INTERFACE}\"\n-\n-setup_irqs \"${INTERFACE}\"\n-\n-exit 0\ndiff --git a/tests/busypolling_napiconf/mirror.sh b/tests/busypolling_napiconf/mirror.sh\ndeleted file mode 100755\nindex 22c4c14..0000000\n--- a/tests/busypolling_napiconf/mirror.sh\n+++ /dev/null\n@@ -1,25 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2023 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-\n-set -e\n-\n-cd \"$(dirname \"$0\")\"\n-\n-# Start PTP\n-../../scripts/ptp.sh enp3s0\n-sleep 30\n-\n-# Configure flow\n-./flow.sh enp3s0\n-sleep 30\n-\n-# Start one instance of mirror application\n-cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n-\n-exit 0\ndiff --git a/tests/busypolling_napiconf/mirror.yaml b/tests/busypolling_napiconf/mirror.yaml\ndeleted file mode 100644\nindex 1db8361..0000000\n--- a/tests/busypolling_napiconf/mirror.yaml\n+++ /dev/null\n@@ -1,48 +0,0 @@\n----\n-#\n-# Copyright (C) 2023 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Sample reference YAML configuration file for Intel platforms with i226 NIC(s).\n-#\n-Application:\n-  ApplicationClockId: CLOCK_TAI\n-  # ApplicationBaseStartTimeNS: 0\n-  ApplicationBaseCycleTimeNS: 1ms\n-  ApplicationTxBaseOffsetNS: 800us\n-  ApplicationRxBaseOffsetNS: 500us\n-  ApplicationXdpProgram: xdp_kern_profinet_vid100.o\n-RTC:\n-  RtcEnabled: true\n-  RtcXdpEnabled: true\n-  RtcXdpSkbMode: false\n-  RtcXdpZcMode: true\n-  RtcXdpWakeupMode: true\n-  RtcXdpBusyPollMode: true\n-  RtcTxTimeStampEnabled: false\n-  RtcVid: 100\n-  RtcNumFramesPerCycle: 256\n-  RtcPayloadPattern: |\n-    RtcPayloadPattern\n-  RtcFrameLength: 128\n-  RtcRxQueue: 0\n-  RtcTxQueue: 0\n-  RtcSocketPriority: 7\n-  RtcTxThreadPriority: 98\n-  RtcRxThreadPriority: 98\n-  RtcTxThreadCpu: 0\n-  RtcRxThreadCpu: 0\n-  RtcInterface: enp3s0\n-  RtcDestination: a8:74:1d:9d:36:14\n-Log:\n-  LogThreadPriority: 1\n-  LogThreadCpu: 7\n-  LogFile: /var/log/mirror_vid100.log\n-  LogLevel: Info\n-Debug:\n-  DebugStopTraceOnOutlier: false\n-  DebugStopTraceOnError: false\n-  DebugMonitorMode: false\n-  DebugMonitorDestination: 44:44:44:44:44:44\ndiff --git a/tests/busypolling_napiconf/ref.sh b/tests/busypolling_napiconf/ref.sh\ndeleted file mode 100755\nindex ed207ad..0000000\n--- a/tests/busypolling_napiconf/ref.sh\n+++ /dev/null\n@@ -1,25 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2023 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-\n-set -e\n-\n-cd \"$(dirname \"$0\")\"\n-\n-# Start PTP\n-../../scripts/ptp.sh enp3s0\n-sleep 30\n-\n-# Configure flow\n-./flow.sh enp3s0\n-sleep 30\n-\n-# Start one instance of reference application\n-cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n-\n-exit 0\ndiff --git a/tests/busypolling_napiconf/reference.yaml b/tests/busypolling_napiconf/reference.yaml\ndeleted file mode 100644\nindex 8b4b78d..0000000\n--- a/tests/busypolling_napiconf/reference.yaml\n+++ /dev/null\n@@ -1,48 +0,0 @@\n----\n-#\n-# Copyright (C) 2023 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Sample reference YAML configuration file for Intel platforms with i226 NIC(s).\n-#\n-Application:\n-  ApplicationClockId: CLOCK_TAI\n-  # ApplicationBaseStartTimeNS: 0\n-  ApplicationBaseCycleTimeNS: 1ms\n-  ApplicationTxBaseOffsetNS: 800us\n-  ApplicationRxBaseOffsetNS: 500us\n-  ApplicationXdpProgram: xdp_kern_profinet_vid100.o\n-RTC:\n-  RtcEnabled: true\n-  RtcXdpEnabled: true\n-  RtcXdpSkbMode: false\n-  RtcXdpZcMode: true\n-  RtcXdpWakeupMode: true\n-  RtcXdpBusyPollMode: true\n-  RtcTxTimeStampEnabled: false\n-  RtcVid: 100\n-  RtcNumFramesPerCycle: 256\n-  RtcPayloadPattern: |\n-    RtcPayloadPattern\n-  RtcFrameLength: 128\n-  RtcRxQueue: 0\n-  RtcTxQueue: 0\n-  RtcSocketPriority: 7\n-  RtcTxThreadPriority: 98\n-  RtcRxThreadPriority: 98\n-  RtcTxThreadCpu: 0\n-  RtcRxThreadCpu: 0\n-  RtcInterface: enp3s0\n-  RtcDestination: a8:74:1d:9d:98:d8\n-Log:\n-  LogThreadPriority: 1\n-  LogThreadCpu: 7\n-  LogFile: /var/log/reference_vid100.log\n-  LogLevel: Info\n-Debug:\n-  DebugStopTraceOnOutlier: false\n-  DebugStopTraceOnError: false\n-  DebugMonitorMode: false\n-  DebugMonitorDestination: 44:44:44:44:44:44\ndiff --git a/tests/busypolling_sp/flow.sh b/tests/busypolling_sp/flow_cml.sh\nsimilarity index 100%\nrename from tests/busypolling_sp/flow.sh\nrename to tests/busypolling_sp/flow_cml.sh\ndiff --git a/tests/busypolling_sp/mirror.sh b/tests/busypolling_sp/mirror.sh\nindex 22c4c14..befbe3d 100755\n--- a/tests/busypolling_sp/mirror.sh\n+++ b/tests/busypolling_sp/mirror.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of mirror application\n cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n+../../build/mirror -c mirror_vid100_cml.yaml >mirror1.log &\n \n exit 0\ndiff --git a/tests/busypolling_sp/mirror.yaml b/tests/busypolling_sp/mirror_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/busypolling_sp/mirror.yaml\nrename to tests/busypolling_sp/mirror_vid100_cml.yaml\ndiff --git a/tests/busypolling_sp/ref.sh b/tests/busypolling_sp/ref.sh\nindex ed207ad..3523e8d 100755\n--- a/tests/busypolling_sp/ref.sh\n+++ b/tests/busypolling_sp/ref.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of reference application\n cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n+../../build/reference -c reference_vid100_cml.yaml >ref1.log &\n \n exit 0\ndiff --git a/tests/busypolling_sp/reference.yaml b/tests/busypolling_sp/reference_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/busypolling_sp/reference.yaml\nrename to tests/busypolling_sp/reference_vid100_cml.yaml\ndiff --git a/tests/er26/flow.sh b/tests/er26/flow.sh\ndeleted file mode 100755\nindex da0d1e2..0000000\n--- a/tests/er26/flow.sh\n+++ /dev/null\n@@ -1,82 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# .:Embedded Recipes 2026:.\n-#\n-\n-set -e\n-\n-source ../lib/common.sh\n-source ../lib/igc.sh\n-\n-#\n-# Command line arguments.\n-#\n-INTERFACE=$1\n-\n-#\n-# Config.\n-#\n-CYCLETIME_NS=\"1000000\"\n-BASETIME=$(date '+%s000000000' -d '-30 sec')\n-NAPICTL=\"../../build/napictl\"\n-\n-[ -z $INTERFACE ] && INTERFACE=\"enp3s0\" # default: enp3s0\n-\n-load_kernel_modules\n-\n-#\n-# Configure napi-defer-hard-irqs and gro-flush-timeout for queue 0/1/2.\n-#\n-napi_defer_hard_irqs_queue \"${NAPICTL}\" \"${INTERFACE}\" \"${CYCLETIME_NS}\" 0\n-napi_defer_hard_irqs_queue \"${NAPICTL}\" \"${INTERFACE}\" \"${CYCLETIME_NS}\" 1\n-napi_defer_hard_irqs_queue \"${NAPICTL}\" \"${INTERFACE}\" \"${CYCLETIME_NS}\" 2\n-\n-igc_start \"${INTERFACE}\"\n-\n-#\n-# Split traffic between TSN streams, real time and everything else.\n-#\n-ENTRY1_NS=\"50000\"  # TSN High\n-ENTRY2_NS=\"50000\"  # TSN Low\n-ENTRY3_NS=\"100000\" # RTC\n-ENTRY4_NS=\"800000\" # RTA and everything else\n-\n-#\n-# Tx Assignment with Qbv and full hardware offload.\n-#\n-# PCP 6   - Tx Q 0 - TSN High\n-# PCP 5   - Tx Q 1 - TSN Low\n-# PCP 4   - Tx Q 2 - RTC\n-# PCP 3/X - Tx Q 3 - RTA and Everything else\n-#\n-tc qdisc replace dev ${INTERFACE} handle 100 parent root taprio num_tc 4 \\\n-  map 3 3 3 3 3 2 1 0 3 3 3 3 3 3 3 3 \\\n-  queues 1@0 1@1 1@2 1@3 \\\n-  base-time ${BASETIME} \\\n-  sched-entry S 0x01 ${ENTRY1_NS} \\\n-  sched-entry S 0x02 ${ENTRY2_NS} \\\n-  sched-entry S 0x04 ${ENTRY3_NS} \\\n-  sched-entry S 0x08 ${ENTRY4_NS} \\\n-  flags 0x02\n-\n-#\n-# Rx Queues Assignment.\n-#\n-# Rx Q 3 - All Traffic\n-# Rx Q 2 - RTC\n-# Rx Q 1 - TSN Low\n-# Rx Q 0 - TSN High\n-#\n-RXQUEUES=(3 0 1 2 3 3 3 3 3 3)\n-igc_rx_queues_assign \"${INTERFACE}\" RXQUEUES\n-\n-igc_end \"${INTERFACE}\"\n-\n-setup_irqs \"${INTERFACE}\"\n-\n-exit 0\ndiff --git a/tests/er26/mirror.sh b/tests/er26/mirror.sh\ndeleted file mode 100755\nindex 22c4c14..0000000\n--- a/tests/er26/mirror.sh\n+++ /dev/null\n@@ -1,25 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2023 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-\n-set -e\n-\n-cd \"$(dirname \"$0\")\"\n-\n-# Start PTP\n-../../scripts/ptp.sh enp3s0\n-sleep 30\n-\n-# Configure flow\n-./flow.sh enp3s0\n-sleep 30\n-\n-# Start one instance of mirror application\n-cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n-\n-exit 0\ndiff --git a/tests/er26/mirror.yaml b/tests/er26/mirror.yaml\ndeleted file mode 100644\nindex 3756e8e..0000000\n--- a/tests/er26/mirror.yaml\n+++ /dev/null\n@@ -1,181 +0,0 @@\n----\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# .:Embedded Recipes 2026:.\n-#\n-Application:\n-  ApplicationClockId: CLOCK_TAI\n-  ApplicationBaseCycleTimeNS: 1ms\n-  ApplicationTxBaseOffsetNS: 800us\n-  ApplicationRxBaseOffsetNS: 300us\n-  ApplicationXdpProgram: xdp_kern_profinet_vid100.o\n-TSNHigh:\n-  TsnHighEnabled: true\n-  TsnHighXdpEnabled: true\n-  TsnHighXdpSkbMode: false\n-  TsnHighXdpZcMode: true\n-  TsnHighXdpWakeupMode: true\n-  TsnHighXdpBusyPollMode: true\n-  TsnHighTxTimeEnabled: false\n-  TsnHighTxTimeOffsetNS: 0\n-  TsnHighTxTimeStampEnabled: false\n-  TsnHighVid: 100\n-  TsnHighNumFramesPerCycle: 16\n-  TsnHighPayloadPattern: |\n-    TsnHighPayloadPattern\n-  TsnHighFrameLength: 64\n-  TsnHighRxQueue: 0\n-  TsnHighTxQueue: 0\n-  TsnHighSocketPriority: 7\n-  TsnHighTxThreadPriority: 98\n-  TsnHighRxThreadPriority: 98\n-  TsnHighTxThreadCpu: 0\n-  TsnHighRxThreadCpu: 0\n-  TsnHighInterface: enp3s0\n-  TsnHighDestination: a8:74:1d:9d:36:14\n-TSNLow:\n-  TsnLowEnabled: true\n-  TsnLowXdpEnabled: true\n-  TsnLowXdpSkbMode: false\n-  TsnLowXdpZcMode: true\n-  TsnLowXdpWakeupMode: true\n-  TsnLowXdpBusyPollMode: true\n-  TsnLowTxTimeEnabled: false\n-  TsnLowTxTimeOffsetNS: 0\n-  TsnLowTxTimeStampEnabled: false\n-  TsnLowVid: 100\n-  TsnLowNumFramesPerCycle: 16\n-  TsnLowPayloadPattern: |\n-    TsnLowPayloadPattern\n-  TsnLowFrameLength: 64\n-  TsnLowRxQueue: 1\n-  TsnLowTxQueue: 1\n-  TsnLowSocketPriority: 6\n-  TsnLowTxThreadPriority: 98\n-  TsnLowRxThreadPriority: 98\n-  TsnLowTxThreadCpu: 1\n-  TsnLowRxThreadCpu: 1\n-  TsnLowInterface: enp3s0\n-  TsnLowDestination: a8:74:1d:9d:36:14\n-RTC:\n-  RtcEnabled: true\n-  RtcXdpEnabled: true\n-  RtcXdpSkbMode: false\n-  RtcXdpZcMode: true\n-  RtcXdpWakeupMode: true\n-  RtcXdpBusyPollMode: true\n-  RtcTxTimeStampEnabled: false\n-  RtcVid: 100\n-  RtcNumFramesPerCycle: 32\n-  RtcPayloadPattern: |\n-    RtcPayloadPattern\n-  RtcFrameLength: 64\n-  RtcRxQueue: 2\n-  RtcTxQueue: 2\n-  RtcSocketPriority: 5\n-  RtcTxThreadPriority: 98\n-  RtcRxThreadPriority: 98\n-  RtcTxThreadCpu: 2\n-  RtcRxThreadCpu: 2\n-  RtcInterface: enp3s0\n-  RtcDestination: a8:74:1d:9d:36:14\n-RTA:\n-  RtaEnabled: true\n-  RtaXdpEnabled: true\n-  RtaXdpSkbMode: false\n-  RtaXdpZcMode: false\n-  RtaXdpWakeupMode: true\n-  RtaTxTimeStampEnabled: false\n-  RtaVid: 100\n-  RtaBurstPeriodNS: 200ms\n-  RtaNumFramesPerCycle: 1\n-  RtaPayloadPattern: |\n-    RtaPayloadPattern\n-  RtaFrameLength: 200\n-  RtaRxQueue: 3\n-  RtaTxQueue: 3\n-  RtaSocketPriority: 4\n-  RtaTxThreadPriority: 97\n-  RtaRxThreadPriority: 97\n-  RtaTxThreadCpu: 3\n-  RtaRxThreadCpu: 3\n-  RtaInterface: enp3s0\n-  RtaDestination: a8:74:1d:9d:36:14\n-DCP:\n-  DcpEnabled: true\n-  DcpVid: 100\n-  DcpBurstPeriodNS: 2s\n-  DcpNumFramesPerCycle: 1\n-  DcpPayloadPattern: |\n-    DcpPayloadPattern\n-  DcpFrameLength: 200\n-  DcpRxQueue: 3\n-  DcpTxQueue: 3\n-  DcpSocketPriority: 3\n-  DcpTxThreadPriority: 53\n-  DcpRxThreadPriority: 53\n-  DcpTxThreadCpu: 4\n-  DcpRxThreadCpu: 4\n-  DcpInterface: enp3s0\n-  DcpDestination: a8:74:1d:9d:36:14\n-LLDP:\n-  LldpEnabled: true\n-  LldpBurstPeriodNS: 5s\n-  LldpNumFramesPerCycle: 1\n-  LldpPayloadPattern: |\n-    LldpPayloadPattern\n-  LldpFrameLength: 200\n-  LldpRxQueue: 3\n-  LldpTxQueue: 3\n-  LldpSocketPriority: 3\n-  LldpTxThreadPriority: 52\n-  LldpRxThreadPriority: 52\n-  LldpTxThreadCpu: 5\n-  LldpRxThreadCpu: 5\n-  LldpInterface: enp3s0\n-  LldpDestination: 01:80:c2:00:00:0e\n-UDPHigh:\n-  UdpHighEnabled: true\n-  UdpHighBurstPeriodNS: 1s\n-  UdpHighNumFramesPerCycle: 1\n-  UdpHighPayloadPattern: |\n-    UdpHighPayloadPattern\n-  UdpHighFrameLength: 1400\n-  UdpHighRxQueue: 3\n-  UdpHighTxQueue: 3\n-  UdpHighSocketPriority: 3\n-  UdpHighTxThreadPriority: 51\n-  UdpHighRxThreadPriority: 51\n-  UdpHighTxThreadCpu: 6\n-  UdpHighRxThreadCpu: 6\n-  UdpHighInterface: enp3s0\n-  UdpHighPort: 6666\n-  UdpHighDestination: 192.168.1.1\n-  UdpHighSource: 192.168.1.2\n-UDPLow:\n-  UdpLowEnabled: true\n-  UdpLowBurstPeriodNS: 1s\n-  UdpLowNumFramesPerCycle: 1\n-  UdpLowPayloadPattern: |\n-    UdpLowPayloadPattern\n-  UdpLowFrameLength: 1400\n-  UdpLowRxQueue: 3\n-  UdpLowTxQueue: 3\n-  UdpLowSocketPriority: 3\n-  UdpLowTxThreadPriority: 51\n-  UdpLowRxThreadPriority: 51\n-  UdpLowTxThreadCpu: 7\n-  UdpLowRxThreadCpu: 7\n-  UdpLowInterface: enp3s0\n-  UdpLowPort: 6667\n-  UdpLowDestination: 192.168.1.1\n-  UdpLowSource: 192.168.1.2\n-Log:\n-  LogThreadPriority: 1\n-  LogThreadCpu: 7\n-  LogFile: /var/log/mirror_vid100.log\n-  LogLevel: Info\ndiff --git a/tests/er26/ref.sh b/tests/er26/ref.sh\ndeleted file mode 100755\nindex ed207ad..0000000\n--- a/tests/er26/ref.sh\n+++ /dev/null\n@@ -1,25 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2023 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-\n-set -e\n-\n-cd \"$(dirname \"$0\")\"\n-\n-# Start PTP\n-../../scripts/ptp.sh enp3s0\n-sleep 30\n-\n-# Configure flow\n-./flow.sh enp3s0\n-sleep 30\n-\n-# Start one instance of reference application\n-cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n-\n-exit 0\ndiff --git a/tests/er26/reference.yaml b/tests/er26/reference.yaml\ndeleted file mode 100644\nindex 42fb27a..0000000\n--- a/tests/er26/reference.yaml\n+++ /dev/null\n@@ -1,188 +0,0 @@\n----\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# .:Embedded Recipes 2026:.\n-#\n-Application:\n-  ApplicationClockId: CLOCK_TAI\n-  ApplicationBaseCycleTimeNS: 1ms\n-  ApplicationTxBaseOffsetNS: 800us\n-  ApplicationRxBaseOffsetNS: 300us\n-  ApplicationXdpProgram: xdp_kern_profinet_vid100.o\n-TSNHigh:\n-  TsnHighEnabled: true\n-  TsnHighXdpEnabled: true\n-  TsnHighXdpSkbMode: false\n-  TsnHighXdpZcMode: true\n-  TsnHighXdpWakeupMode: true\n-  TsnHighXdpBusyPollMode: true\n-  TsnHighTxTimeEnabled: false\n-  TsnHighTxTimeOffsetNS: 0\n-  TsnHighTxTimeStampEnabled: false\n-  TsnHighVid: 100\n-  TsnHighNumFramesPerCycle: 16\n-  TsnHighPayloadPattern: |\n-    TsnHighPayloadPattern\n-  TsnHighFrameLength: 64\n-  TsnHighRxQueue: 0\n-  TsnHighTxQueue: 0\n-  TsnHighSocketPriority: 7\n-  TsnHighTxThreadPriority: 98\n-  TsnHighRxThreadPriority: 98\n-  TsnHighTxThreadCpu: 0\n-  TsnHighRxThreadCpu: 0\n-  TsnHighInterface: enp3s0\n-  TsnHighDestination: a8:74:1d:9d:98:d8\n-TSNLow:\n-  TsnLowEnabled: true\n-  TsnLowXdpEnabled: true\n-  TsnLowXdpSkbMode: false\n-  TsnLowXdpZcMode: true\n-  TsnLowXdpWakeupMode: true\n-  TsnLowXdpBusyPollMode: true\n-  TsnLowTxTimeEnabled: false\n-  TsnLowTxTimeOffsetNS: 0\n-  TsnLowTxTimeStampEnabled: false\n-  TsnLowVid: 100\n-  TsnLowNumFramesPerCycle: 16\n-  TsnLowPayloadPattern: |\n-    TsnLowPayloadPattern\n-  TsnLowFrameLength: 64\n-  TsnLowRxQueue: 1\n-  TsnLowTxQueue: 1\n-  TsnLowSocketPriority: 6\n-  TsnLowTxThreadPriority: 98\n-  TsnLowRxThreadPriority: 98\n-  TsnLowTxThreadCpu: 1\n-  TsnLowRxThreadCpu: 1\n-  TsnLowInterface: enp3s0\n-  TsnLowDestination: a8:74:1d:9d:98:d8\n-RTC:\n-  RtcEnabled: true\n-  RtcXdpEnabled: true\n-  RtcXdpSkbMode: false\n-  RtcXdpZcMode: true\n-  RtcXdpWakeupMode: true\n-  RtcXdpBusyPollMode: true\n-  RtcTxTimeStampEnabled: false\n-  RtcVid: 100\n-  RtcNumFramesPerCycle: 32\n-  RtcPayloadPattern: |\n-    RtcPayloadPattern\n-  RtcFrameLength: 64\n-  RtcRxQueue: 2\n-  RtcTxQueue: 2\n-  RtcSocketPriority: 5\n-  RtcTxThreadPriority: 98\n-  RtcRxThreadPriority: 98\n-  RtcTxThreadCpu: 2\n-  RtcRxThreadCpu: 2\n-  RtcInterface: enp3s0\n-  RtcDestination: a8:74:1d:9d:98:d8\n-RTA:\n-  RtaEnabled: true\n-  RtaXdpEnabled: true\n-  RtaXdpSkbMode: false\n-  RtaXdpZcMode: false\n-  RtaXdpWakeupMode: true\n-  RtaTxTimeStampEnabled: false\n-  RtaVid: 100\n-  RtaBurstPeriodNS: 200ms\n-  RtaNumFramesPerCycle: 1\n-  RtaPayloadPattern: |\n-    RtaPayloadPattern\n-  RtaFrameLength: 200\n-  RtaRxQueue: 3\n-  RtaTxQueue: 3\n-  RtaSocketPriority: 4\n-  RtaTxThreadPriority: 97\n-  RtaRxThreadPriority: 97\n-  RtaTxThreadCpu: 3\n-  RtaRxThreadCpu: 3\n-  RtaInterface: enp3s0\n-  RtaDestination: a8:74:1d:9d:98:d8\n-DCP:\n-  DcpEnabled: true\n-  DcpVid: 100\n-  DcpBurstPeriodNS: 2s\n-  DcpNumFramesPerCycle: 1\n-  DcpPayloadPattern: |\n-    DcpPayloadPattern\n-  DcpFrameLength: 200\n-  DcpRxQueue: 3\n-  DcpTxQueue: 3\n-  DcpSocketPriority: 3\n-  DcpTxThreadPriority: 53\n-  DcpRxThreadPriority: 53\n-  DcpTxThreadCpu: 4\n-  DcpRxThreadCpu: 4\n-  DcpInterface: enp3s0\n-  DcpDestination: 01:0e:cf:00:00:00\n-LLDP:\n-  LldpEnabled: true\n-  LldpBurstPeriodNS: 5s\n-  LldpNumFramesPerCycle: 1\n-  LldpPayloadPattern: |\n-    LldpPayloadPattern\n-  LldpFrameLength: 200\n-  LldpRxQueue: 3\n-  LldpTxQueue: 3\n-  LldpSocketPriority: 3\n-  LldpTxThreadPriority: 52\n-  LldpRxThreadPriority: 52\n-  LldpTxThreadCpu: 5\n-  LldpRxThreadCpu: 5\n-  LldpInterface: enp3s0\n-  LldpDestination: 01:80:c2:00:00:0e\n-UDPHigh:\n-  UdpHighEnabled: true\n-  UdpHighBurstPeriodNS: 1s\n-  UdpHighNumFramesPerCycle: 1\n-  UdpHighPayloadPattern: |\n-    UdpHighPayloadPattern\n-  UdpHighFrameLength: 1400\n-  UdpHighRxQueue: 3\n-  UdpHighTxQueue: 3\n-  UdpHighSocketPriority: 3\n-  UdpHighTxThreadPriority: 51\n-  UdpHighRxThreadPriority: 51\n-  UdpHighTxThreadCpu: 6\n-  UdpHighRxThreadCpu: 6\n-  UdpHighInterface: enp3s0\n-  UdpHighPort: 6666\n-  UdpHighDestination: 192.168.1.2\n-  UdpHighSource: 192.168.1.1\n-UDPLow:\n-  UdpLowEnabled: true\n-  UdpLowBurstPeriodNS: 1s\n-  UdpLowNumFramesPerCycle: 1\n-  UdpLowPayloadPattern: |\n-    UdpLowPayloadPattern\n-  UdpLowFrameLength: 1400\n-  UdpLowRxQueue: 3\n-  UdpLowTxQueue: 3\n-  UdpLowSocketPriority: 3\n-  UdpLowTxThreadPriority: 51\n-  UdpLowRxThreadPriority: 51\n-  UdpLowTxThreadCpu: 7\n-  UdpLowRxThreadCpu: 7\n-  UdpLowInterface: enp3s0\n-  UdpLowPort: 6667\n-  UdpLowDestination: 192.168.1.2\n-  UdpLowSource: 192.168.1.1\n-Log:\n-  LogThreadPriority: 1\n-  LogThreadCpu: 7\n-  LogFile: /var/log/reference_vid100.log\n-  LogLevel: Info\n-LogMqtt:\n-  LogMqtt: true\n-  LogMqttThreadPriority: 1\n-  LogMqttThreadCpu: 7\n-  LogMqttBrokerIP: grafana\n-  LogMqttBrokerPort: 1883\n-  LogMqttMeasurementName: reference\ndiff --git a/tests/i210/flow.sh b/tests/i210/flow.sh\ndeleted file mode 100755\nindex edb48f2..0000000\n--- a/tests/i210/flow.sh\n+++ /dev/null\n@@ -1,59 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Setup the Tx and Rx traffic flows for Intel i210 for testing XDP busy polling.\n-#\n-\n-set -e\n-\n-source ../lib/common.sh\n-source ../lib/igb.sh\n-\n-#\n-# Command line arguments.\n-#\n-INTERFACE=$1\n-CYCLETIME_NS=$2\n-\n-[ -z $INTERFACE ] && INTERFACE=\"enp1s0\"        # default: enp1s0\n-[ -z $CYCLETIME_NS ] && CYCLETIME_NS=\"1000000\" # default: 1ms\n-\n-load_kernel_modules\n-\n-napi_defer_hard_irqs \"${INTERFACE}\" \"${CYCLETIME_NS}\"\n-\n-igb_start \"${INTERFACE}\"\n-\n-#\n-# Tx Assignment with strict priority in HW.\n-#\n-# PCP 6   - Tx Q 0 - TSN High\n-# PCP 5   - Tx Q 1 - TSN Low\n-# PCP 4   - Tx Q 2 - RTC\n-# PCP 3/X - Tx Q 3 - RTA and Everything else\n-#\n-tc qdisc replace dev ${INTERFACE} handle 100 parent root mqprio num_tc 4 \\\n-  map 3 3 3 3 3 2 1 0 3 3 3 3 3 3 3 3 \\\n-  queues 1@0 1@1 1@2 1@3 \\\n-  hw 1\n-\n-#\n-# Rx Queues Assignment.\n-#\n-# Rx Q 0 - TSN High\n-# Rx Q 1 - TSN Low\n-# Rx Q 2 - RTC\n-# Rx Q 3 - All Traffic\n-#\n-RXQUEUES=(3 0 1 2 3 3 3 3 3 3)\n-igb_rx_queues_assign \"${INTERFACE}\" RXQUEUES\n-\n-igb_end \"${INTERFACE}\"\n-\n-setup_irqs \"${INTERFACE}\"\n-\n-exit 0\ndiff --git a/tests/i210/mirror.sh b/tests/i210/mirror.sh\ndeleted file mode 100755\nindex 15d9605..0000000\n--- a/tests/i210/mirror.sh\n+++ /dev/null\n@@ -1,25 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2024 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-\n-set -e\n-\n-cd \"$(dirname \"$0\")\"\n-\n-# Start PTP\n-../../scripts/ptp.sh enp1s0\n-sleep 30\n-\n-# Configure flow\n-./flow.sh enp1s0\n-sleep 30\n-\n-# Start one instance of mirror application\n-cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n-\n-exit 0\ndiff --git a/tests/i210/mirror.yaml b/tests/i210/mirror.yaml\ndeleted file mode 100644\nindex 20b6b60..0000000\n--- a/tests/i210/mirror.yaml\n+++ /dev/null\n@@ -1,185 +0,0 @@\n----\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-Application:\n-  ApplicationClockId: CLOCK_TAI\n-  # ApplicationBaseStartTimeNS: 0\n-  ApplicationBaseCycleTimeNS: 1ms\n-  ApplicationTxBaseOffsetNS: 800us\n-  ApplicationRxBaseOffsetNS: 300us\n-  ApplicationXdpProgram: xdp_kern_profinet_vid100.o\n-TSNHigh:\n-  TsnHighEnabled: true\n-  TsnHighXdpEnabled: true\n-  TsnHighXdpSkbMode: false\n-  TsnHighXdpZcMode: true\n-  TsnHighXdpWakeupMode: true\n-  TsnHighXdpBusyPollMode: true\n-  TsnHighTxTimeEnabled: false\n-  TsnHighTxTimeOffsetNS: 0\n-  TsnHighTxTimeStampEnabled: false\n-  TsnHighVid: 100\n-  TsnHighNumFramesPerCycle: 1\n-  TsnHighPayloadPattern: |\n-    TsnHighPayloadPattern\n-  TsnHighFrameLength: 128\n-  TsnHighRxQueue: 0\n-  TsnHighTxQueue: 0\n-  TsnHighSocketPriority: 7\n-  TsnHighTxThreadPriority: 98\n-  TsnHighRxThreadPriority: 98\n-  TsnHighTxThreadCpu: 0\n-  TsnHighRxThreadCpu: 0\n-  TsnHighInterface: enp1s0\n-  TsnHighDestination: 6c:b3:11:52:39:15\n-TSNLow:\n-  TsnLowEnabled: true\n-  TsnLowXdpEnabled: true\n-  TsnLowXdpSkbMode: false\n-  TsnLowXdpZcMode: true\n-  TsnLowXdpWakeupMode: true\n-  TsnLowXdpBusyPollMode: true\n-  TsnLowTxTimeEnabled: false\n-  TsnLowTxTimeOffsetNS: 0\n-  TsnLowTxTimeStampEnabled: false\n-  TsnLowVid: 100\n-  TsnLowNumFramesPerCycle: 1\n-  TsnLowPayloadPattern: |\n-    TsnLowPayloadPattern\n-  TsnLowFrameLength: 128\n-  TsnLowRxQueue: 1\n-  TsnLowTxQueue: 1\n-  TsnLowSocketPriority: 6\n-  TsnLowTxThreadPriority: 98\n-  TsnLowRxThreadPriority: 98\n-  TsnLowTxThreadCpu: 1\n-  TsnLowRxThreadCpu: 1\n-  TsnLowInterface: enp1s0\n-  TsnLowDestination: 6c:b3:11:52:39:15\n-RTC:\n-  RtcEnabled: true\n-  RtcXdpEnabled: true\n-  RtcXdpSkbMode: false\n-  RtcXdpZcMode: true\n-  RtcXdpWakeupMode: true\n-  RtcXdpBusyPollMode: true\n-  RtcTxTimeStampEnabled: false\n-  RtcVid: 100\n-  RtcNumFramesPerCycle: 50\n-  RtcPayloadPattern: |\n-    RtcPayloadPattern\n-  RtcFrameLength: 128\n-  RtcRxQueue: 2\n-  RtcTxQueue: 2\n-  RtcSocketPriority: 5\n-  RtcTxThreadPriority: 98\n-  RtcRxThreadPriority: 98\n-  RtcTxThreadCpu: 2\n-  RtcRxThreadCpu: 2\n-  RtcInterface: enp1s0\n-  RtcDestination: 6c:b3:11:52:39:15\n-RTA:\n-  RtaEnabled: true\n-  RtaXdpEnabled: true\n-  RtaXdpSkbMode: false\n-  RtaXdpZcMode: false\n-  RtaXdpWakeupMode: true\n-  RtaTxTimeStampEnabled: false\n-  RtaVid: 100\n-  RtaBurstPeriodNS: 200ms\n-  RtaNumFramesPerCycle: 1\n-  RtaPayloadPattern: |\n-    RtaPayloadPattern\n-  RtaFrameLength: 200\n-  RtaRxQueue: 3\n-  RtaTxQueue: 3\n-  RtaSocketPriority: 4\n-  RtaTxThreadPriority: 97\n-  RtaRxThreadPriority: 97\n-  RtaTxThreadCpu: 3\n-  RtaRxThreadCpu: 3\n-  RtaInterface: enp1s0\n-  RtaDestination: 6c:b3:11:52:39:15\n-DCP:\n-  DcpEnabled: true\n-  DcpVid: 100\n-  DcpBurstPeriodNS: 2s\n-  DcpNumFramesPerCycle: 1\n-  DcpPayloadPattern: |\n-    DcpPayloadPattern\n-  DcpFrameLength: 200\n-  DcpRxQueue: 3\n-  DcpTxQueue: 3\n-  DcpSocketPriority: 3\n-  DcpTxThreadPriority: 53\n-  DcpRxThreadPriority: 53\n-  DcpTxThreadCpu: 4\n-  DcpRxThreadCpu: 4\n-  DcpInterface: enp1s0\n-  DcpDestination: 6c:b3:11:52:39:15\n-LLDP:\n-  LldpEnabled: true\n-  LldpBurstPeriodNS: 5s\n-  LldpNumFramesPerCycle: 1\n-  LldpPayloadPattern: |\n-    LldpPayloadPattern\n-  LldpFrameLength: 200\n-  LldpRxQueue: 3\n-  LldpTxQueue: 3\n-  LldpSocketPriority: 3\n-  LldpTxThreadPriority: 52\n-  LldpRxThreadPriority: 52\n-  LldpTxThreadCpu: 5\n-  LldpRxThreadCpu: 5\n-  LldpInterface: enp1s0\n-  LldpDestination: 01:80:c2:00:00:0e\n-UDPHigh:\n-  UdpHighEnabled: true\n-  UdpHighBurstPeriodNS: 1s\n-  UdpHighNumFramesPerCycle: 1\n-  UdpHighPayloadPattern: |\n-    UdpHighPayloadPattern\n-  UdpHighFrameLength: 1400\n-  UdpHighRxQueue: 3\n-  UdpHighTxQueue: 3\n-  UdpHighSocketPriority: 3\n-  UdpHighTxThreadPriority: 51\n-  UdpHighRxThreadPriority: 51\n-  UdpHighTxThreadCpu: 6\n-  UdpHighRxThreadCpu: 6\n-  UdpHighInterface: enp1s0\n-  UdpHighPort: 6666\n-  UdpHighDestination: 192.168.1.1\n-  UdpHighSource: 192.168.1.2\n-UDPLow:\n-  UdpLowEnabled: true\n-  UdpLowBurstPeriodNS: 1s\n-  UdpLowNumFramesPerCycle: 1\n-  UdpLowPayloadPattern: |\n-    UdpLowPayloadPattern\n-  UdpLowFrameLength: 1400\n-  UdpLowRxQueue: 3\n-  UdpLowTxQueue: 3\n-  UdpLowSocketPriority: 3\n-  UdpLowTxThreadPriority: 51\n-  UdpLowRxThreadPriority: 51\n-  UdpLowTxThreadCpu: 7\n-  UdpLowRxThreadCpu: 7\n-  UdpLowInterface: enp1s0\n-  UdpLowPort: 6667\n-  UdpLowDestination: 192.168.1.1\n-  UdpLowSource: 192.168.1.2\n-Log:\n-  LogThreadPriority: 1\n-  LogThreadCpu: 8\n-  LogFile: /var/log/mirror_vid100.log\n-  LogLevel: Info\n-Debug:\n-  DebugStopTraceOnOutlier: false\n-  DebugStopTraceOnError: false\n-  DebugMonitorMode: false\n-  DebugMonitorDestination: 44:44:44:44:44:44\ndiff --git a/tests/i210/ref.sh b/tests/i210/ref.sh\ndeleted file mode 100755\nindex 1a7c2cf..0000000\n--- a/tests/i210/ref.sh\n+++ /dev/null\n@@ -1,25 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2024 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-\n-set -e\n-\n-cd \"$(dirname \"$0\")\"\n-\n-# Start PTP\n-../../scripts/ptp.sh enp1s0\n-sleep 30\n-\n-# Configure flow\n-./flow.sh enp1s0\n-sleep 30\n-\n-# Start one instance of reference application\n-cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n-\n-exit 0\ndiff --git a/tests/i210/reference.yaml b/tests/i210/reference.yaml\ndeleted file mode 100644\nindex c7aef90..0000000\n--- a/tests/i210/reference.yaml\n+++ /dev/null\n@@ -1,192 +0,0 @@\n----\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-Application:\n-  ApplicationClockId: CLOCK_TAI\n-  # ApplicationBaseStartTimeNS: 0\n-  ApplicationBaseCycleTimeNS: 1ms\n-  ApplicationTxBaseOffsetNS: 800us\n-  ApplicationRxBaseOffsetNS: 300us\n-  ApplicationXdpProgram: xdp_kern_profinet_vid100.o\n-TSNHigh:\n-  TsnHighEnabled: true\n-  TsnHighXdpEnabled: true\n-  TsnHighXdpSkbMode: false\n-  TsnHighXdpZcMode: true\n-  TsnHighXdpWakeupMode: true\n-  TsnHighXdpBusyPollMode: true\n-  TsnHighTxTimeEnabled: false\n-  TsnHighTxTimeOffsetNS: 0\n-  TsnHighTxTimeStampEnabled: false\n-  TsnHighVid: 100\n-  TsnHighNumFramesPerCycle: 1\n-  TsnHighPayloadPattern: |\n-    TsnHighPayloadPattern\n-  TsnHighFrameLength: 128\n-  TsnHighRxQueue: 0\n-  TsnHighTxQueue: 0\n-  TsnHighSocketPriority: 7\n-  TsnHighTxThreadPriority: 98\n-  TsnHighRxThreadPriority: 98\n-  TsnHighTxThreadCpu: 0\n-  TsnHighRxThreadCpu: 0\n-  TsnHighInterface: enp1s0\n-  TsnHighDestination: 6c:b3:11:52:8b:d7\n-TSNLow:\n-  TsnLowEnabled: true\n-  TsnLowXdpEnabled: true\n-  TsnLowXdpSkbMode: false\n-  TsnLowXdpZcMode: true\n-  TsnLowXdpWakeupMode: true\n-  TsnLowXdpBusyPollMode: true\n-  TsnLowTxTimeEnabled: false\n-  TsnLowTxTimeOffsetNS: 0\n-  TsnLowTxTimeStampEnabled: false\n-  TsnLowVid: 100\n-  TsnLowNumFramesPerCycle: 1\n-  TsnLowPayloadPattern: |\n-    TsnLowPayloadPattern\n-  TsnLowFrameLength: 128\n-  TsnLowRxQueue: 1\n-  TsnLowTxQueue: 1\n-  TsnLowSocketPriority: 6\n-  TsnLowTxThreadPriority: 98\n-  TsnLowRxThreadPriority: 98\n-  TsnLowTxThreadCpu: 1\n-  TsnLowRxThreadCpu: 1\n-  TsnLowInterface: enp1s0\n-  TsnLowDestination: 6c:b3:11:52:8b:d7\n-RTC:\n-  RtcEnabled: true\n-  RtcXdpEnabled: true\n-  RtcXdpSkbMode: false\n-  RtcXdpZcMode: true\n-  RtcXdpWakeupMode: true\n-  RtcXdpBusyPollMode: true\n-  RtcTxTimeStampEnabled: false\n-  RtcVid: 100\n-  RtcNumFramesPerCycle: 50\n-  RtcPayloadPattern: |\n-    RtcPayloadPattern\n-  RtcFrameLength: 128\n-  RtcRxQueue: 2\n-  RtcTxQueue: 2\n-  RtcSocketPriority: 5\n-  RtcTxThreadPriority: 98\n-  RtcRxThreadPriority: 98\n-  RtcTxThreadCpu: 2\n-  RtcRxThreadCpu: 2\n-  RtcInterface: enp1s0\n-  RtcDestination: 6c:b3:11:52:8b:d7\n-RTA:\n-  RtaEnabled: true\n-  RtaXdpEnabled: true\n-  RtaXdpSkbMode: false\n-  RtaXdpZcMode: false\n-  RtaXdpWakeupMode: true\n-  RtaTxTimeStampEnabled: false\n-  RtaVid: 100\n-  RtaBurstPeriodNS: 200ms\n-  RtaNumFramesPerCycle: 1\n-  RtaPayloadPattern: |\n-    RtaPayloadPattern\n-  RtaFrameLength: 200\n-  RtaRxQueue: 3\n-  RtaTxQueue: 3\n-  RtaSocketPriority: 4\n-  RtaTxThreadPriority: 97\n-  RtaRxThreadPriority: 97\n-  RtaTxThreadCpu: 3\n-  RtaRxThreadCpu: 3\n-  RtaInterface: enp1s0\n-  RtaDestination: 6c:b3:11:52:8b:d7\n-DCP:\n-  DcpEnabled: true\n-  DcpVid: 100\n-  DcpBurstPeriodNS: 2s\n-  DcpNumFramesPerCycle: 1\n-  DcpPayloadPattern: |\n-    DcpPayloadPattern\n-  DcpFrameLength: 200\n-  DcpRxQueue: 3\n-  DcpTxQueue: 3\n-  DcpSocketPriority: 3\n-  DcpTxThreadPriority: 53\n-  DcpRxThreadPriority: 53\n-  DcpTxThreadCpu: 4\n-  DcpRxThreadCpu: 4\n-  DcpInterface: enp1s0\n-  DcpDestination: 01:0e:cf:00:00:00\n-LLDP:\n-  LldpEnabled: true\n-  LldpBurstPeriodNS: 5s\n-  LldpNumFramesPerCycle: 1\n-  LldpPayloadPattern: |\n-    LldpPayloadPattern\n-  LldpFrameLength: 200\n-  LldpRxQueue: 3\n-  LldpTxQueue: 3\n-  LldpSocketPriority: 3\n-  LldpTxThreadPriority: 52\n-  LldpRxThreadPriority: 52\n-  LldpTxThreadCpu: 5\n-  LldpRxThreadCpu: 5\n-  LldpInterface: enp1s0\n-  LldpDestination: 01:80:c2:00:00:0e\n-UDPHigh:\n-  UdpHighEnabled: true\n-  UdpHighBurstPeriodNS: 1s\n-  UdpHighNumFramesPerCycle: 1\n-  UdpHighPayloadPattern: |\n-    UdpHighPayloadPattern\n-  UdpHighFrameLength: 1400\n-  UdpHighRxQueue: 3\n-  UdpHighTxQueue: 3\n-  UdpHighSocketPriority: 3\n-  UdpHighTxThreadPriority: 51\n-  UdpHighRxThreadPriority: 51\n-  UdpHighTxThreadCpu: 6\n-  UdpHighRxThreadCpu: 6\n-  UdpHighInterface: enp1s0\n-  UdpHighPort: 6666\n-  UdpHighDestination: 192.168.1.2\n-  UdpHighSource: 192.168.1.1\n-UDPLow:\n-  UdpLowEnabled: true\n-  UdpLowBurstPeriodNS: 1s\n-  UdpLowNumFramesPerCycle: 1\n-  UdpLowPayloadPattern: |\n-    UdpLowPayloadPattern\n-  UdpLowFrameLength: 1400\n-  UdpLowRxQueue: 3\n-  UdpLowTxQueue: 3\n-  UdpLowSocketPriority: 3\n-  UdpLowTxThreadPriority: 51\n-  UdpLowRxThreadPriority: 51\n-  UdpLowTxThreadCpu: 7\n-  UdpLowRxThreadCpu: 7\n-  UdpLowInterface: enp1s0\n-  UdpLowPort: 6667\n-  UdpLowDestination: 192.168.1.2\n-  UdpLowSource: 192.168.1.1\n-Log:\n-  LogThreadPriority: 1\n-  LogThreadCpu: 8\n-  LogFile: /var/log/reference_vid100.log\n-  LogLevel: Info\n-LogMqtt:\n-  LogMqtt: true\n-  LogMqttThreadPriority: 1\n-  LogMqttThreadCpu: 9\n-  LogMqttBrokerIP: grafana\n-  LogMqttBrokerPort: 1883\n-  LogMqttMeasurementName: reference\n-Debug:\n-  DebugStopTraceOnOutlier: false\n-  DebugStopTraceOnError: false\n-  DebugMonitorMode: false\n-  DebugMonitorDestination: 44:44:44:44:44:44\ndiff --git a/tests/intel_kpi/i226_xdp_125us_1x64b_11k_proclat/flow_mirror.sh b/tests/intel_kpi/i226_xdp_125us_1x64b_11k_proclat/flow_mirror.sh\ndeleted file mode 100755\nindex a894b26..0000000\n--- a/tests/intel_kpi/i226_xdp_125us_1x64b_11k_proclat/flow_mirror.sh\n+++ /dev/null\n@@ -1,62 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Setup the Tx and Rx traffic flows for Intel i226 NIC(s) KPI testing.\n-#\n-\n-set -e\n-\n-source ../../lib/common.sh\n-source ../../lib/igc.sh\n-\n-#\n-# Command line arguments.\n-#\n-INTERFACE=$1\n-CYCLETIME_NS=$2\n-BASETIME=$3\n-\n-[ -z $INTERFACE ] && INTERFACE=\"enp3s0\"                          # default: enp3s0\n-[ -z $CYCLETIME_NS ] && CYCLETIME_NS=\"125000\"                    # default: 125us\n-[ -z $BASETIME ] && BASETIME=$(date '+%s000000000' -d '-30 sec') # default: now - 30s\n-\n-load_kernel_modules\n-\n-igc_start \"${INTERFACE}\"\n-ethtool -C ${INTERFACE} rx-usecs 0\n-\n-#\n-# Split traffic between TSN High Stream and everything else.\n-#\n-ENTRY1_NS=\"75000\" # Everything else\n-ENTRY2_NS=\"50000\" # TSN High Stream\n-\n-#\n-# Tx Assignment with Qbv and full hardware offload.\n-#\n-# PCP 6 - Tx Q 1 - TSN High Stream\n-# PCP X - Tx Q 0 - Everything else\n-#\n-tc qdisc replace dev ${INTERFACE} handle 100 parent root taprio num_tc 2 \\\n-  map 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 \\\n-  queues 1@0 1@1 \\\n-  base-time ${BASETIME} \\\n-  sched-entry S 0x03 ${ENTRY1_NS} \\\n-  sched-entry S 0x02 ${ENTRY2_NS} \\\n-  flags 0x02\n-\n-#\n-# Rx Queues Assignment.\n-#\n-# PCP 6 - Rx Q 1 - TSN High Stream\n-# PCP X - Rx Q 0 - Everything else\n-RXQUEUES=(0 1 0 0 0 0 0 0 0 0)\n-igc_rx_queues_assign \"${INTERFACE}\" RXQUEUES\n-\n-setup_irqs \"${INTERFACE}\"\n-\n-exit 0\ndiff --git a/tests/intel_kpi/i226_xdp_125us_1x64b_11k_proclat/flow_ref.sh b/tests/intel_kpi/i226_xdp_125us_1x64b_11k_proclat/flow_ref.sh\ndeleted file mode 100755\nindex b950d00..0000000\n--- a/tests/intel_kpi/i226_xdp_125us_1x64b_11k_proclat/flow_ref.sh\n+++ /dev/null\n@@ -1,62 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Setup the Tx and Rx traffic flows for Intel i226 NIC(s) KPI testing.\n-#\n-\n-set -e\n-\n-source ../../lib/common.sh\n-source ../../lib/igc.sh\n-\n-#\n-# Command line arguments.\n-#\n-INTERFACE=$1\n-CYCLETIME_NS=$2\n-BASETIME=$3\n-\n-[ -z $INTERFACE ] && INTERFACE=\"enp3s0\"                          # default: enp3s0\n-[ -z $CYCLETIME_NS ] && CYCLETIME_NS=\"125000\"                    # default: 125us\n-[ -z $BASETIME ] && BASETIME=$(date '+%s000000000' -d '-30 sec') # default: now - 30s\n-\n-load_kernel_modules\n-\n-igc_start \"${INTERFACE}\"\n-ethtool -C ${INTERFACE} rx-usecs 0\n-\n-#\n-# Split traffic between TSN High Stream and everything else.\n-#\n-ENTRY1_NS=\"50000\" # TSN High Stream\n-ENTRY2_NS=\"75000\" # Everything else\n-\n-#\n-# Tx Assignment with Qbv and full hardware offload.\n-#\n-# PCP 6 - Tx Q 1 - TSN High Stream\n-# PCP X - Tx Q 0 - Everything else\n-#\n-tc qdisc replace dev ${INTERFACE} handle 100 parent root taprio num_tc 2 \\\n-  map 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 \\\n-  queues 1@0 1@1 \\\n-  base-time ${BASETIME} \\\n-  sched-entry S 0x02 ${ENTRY1_NS} \\\n-  sched-entry S 0x01 ${ENTRY2_NS} \\\n-  flags 0x02\n-\n-#\n-# Rx Queues Assignment.\n-#\n-# PCP 6 - Rx Q 1 - TSN High Stream\n-# PCP X - Rx Q 0 - Everything else\n-RXQUEUES=(0 1 0 0 0 0 0 0 0 0)\n-igc_rx_queues_assign \"${INTERFACE}\" RXQUEUES\n-\n-setup_irqs \"${INTERFACE}\"\n-\n-exit 0\ndiff --git a/tests/intel_kpi/i226_xdp_125us_1x64b_11k_proclat/mirror.sh b/tests/intel_kpi/i226_xdp_125us_1x64b_11k_proclat/mirror.sh\ndeleted file mode 100755\nindex f12f6cd..0000000\n--- a/tests/intel_kpi/i226_xdp_125us_1x64b_11k_proclat/mirror.sh\n+++ /dev/null\n@@ -1,25 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-\n-set -e\n-\n-cd \"$(dirname \"$0\")\"\n-\n-# Start PTP\n-../../../scripts/ptp.sh enp3s0\n-sleep 30\n-\n-# Configure flow\n-./flow_mirror.sh enp3s0\n-sleep 30\n-\n-# Start one instance of mirror application\n-cp ../../../build/xdp_kern_*.o .\n-../../../build/mirror -c mirror.yaml >/dev/shm/mirror.log &\n-\n-exit 0\ndiff --git a/tests/intel_kpi/i226_xdp_125us_1x64b_11k_proclat/mirror.yaml b/tests/intel_kpi/i226_xdp_125us_1x64b_11k_proclat/mirror.yaml\ndeleted file mode 100644\nindex 6fd7f48..0000000\n--- a/tests/intel_kpi/i226_xdp_125us_1x64b_11k_proclat/mirror.yaml\n+++ /dev/null\n@@ -1,65 +0,0 @@\n----\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Mirror YAML configuration file for Intel i226 NIC(s) KPI testing.\n-#\n-Application:\n-  ApplicationClockId: CLOCK_TAI\n-  # ApplicationBaseStartTimeNS: 0\n-  ApplicationBaseCycleTimeNS: 125us\n-  ApplicationTxBaseOffsetNS: 80us\n-  ApplicationRxBaseOffsetNS: 30us\n-  ApplicationXdpProgram: xdp_kern_profinet_vid100.o\n-TsnHigh:\n-  TsnHighEnabled: true\n-  TsnHighXdpEnabled: true\n-  TsnHighXdpSkbMode: false\n-  TsnHighXdpZcMode: true\n-  TsnHighXdpWakeupMode: true\n-  TsnHighXdpBusyPollMode: false\n-  TsnHighTxTimeStampEnabled: true\n-  TsnHighVid: 100\n-  TsnHighNumFramesPerCycle: 1\n-  TsnHighPayloadPattern: |\n-    TsnHighPayloadPattern\n-  TsnHighFrameLength: 64\n-  TsnHighRxQueue: 1\n-  TsnHighTxQueue: 1\n-  TsnHighSocketPriority: 6\n-  TsnHighTxThreadPriority: 98\n-  TsnHighRxThreadPriority: 98\n-  TsnHighTxThreadCpu: 1\n-  TsnHighRxThreadCpu: 1\n-  TsnHighInterface: enp3s0\n-  TsnHighDestination: a8:74:1d:9d:36:14\n-  TsnHighRxWorkloadEnabled: true\n-  TsnHighRxWorkloadFile: ../../workloads/pointer_chasing/pointer_chasing.so\n-  TsnHighRxWorkloadSetupFunction: ptr_chase_setup\n-  TsnHighRxWorkloadSetupArguments: \"0xEC000 0x3B000\"\n-  TsnHighRxWorkloadTeardownFunction: ptr_chase_teardown\n-  TsnHighRxWorkloadFunction: run_ptr_chasing\n-  TsnHighRxWorkloadPrewarm: false\n-  TsnHighRxWorkloadSkipCount: 100\n-  TsnHighRxWorkloadThreadCpu: 1\n-  TsnHighRxWorkloadThreadPriority: 80\n-Log:\n-  LogThreadPriority: 1\n-  LogThreadCpu: 0\n-  LogFile: /var/log/mirror_vid100.log\n-  LogLevel: Info\n-LogMqtt:\n-  LogMqtt: false\n-  LogMqttThreadPriority: 10\n-  LogMqttThreadCpu: 0\n-  LogMqttBrokerIP: 127.0.0.1\n-  LogMqttBrokerPort: 1883\n-  LogMqttMeasurementName: mirror\n-Debug:\n-  DebugStopTraceOnOutlier: false\n-  DebugStopTraceOnError: false\n-  DebugMonitorMode: false\n-  DebugMonitorDestination: 44:44:44:44:44:44\ndiff --git a/tests/intel_kpi/i226_xdp_125us_1x64b_11k_proclat/ref.sh b/tests/intel_kpi/i226_xdp_125us_1x64b_11k_proclat/ref.sh\ndeleted file mode 100755\nindex c7e3719..0000000\n--- a/tests/intel_kpi/i226_xdp_125us_1x64b_11k_proclat/ref.sh\n+++ /dev/null\n@@ -1,25 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-\n-set -e\n-\n-cd \"$(dirname \"$0\")\"\n-\n-# Start PTP\n-../../../scripts/ptp.sh enp3s0\n-sleep 30\n-\n-# Configure flow\n-./flow_ref.sh enp3s0\n-sleep 30\n-\n-# Start one instance of reference application\n-cp ../../../build/xdp_kern_*.o .\n-../../../build/reference -c reference.yaml >/dev/shm/ref.log &\n-\n-exit 0\ndiff --git a/tests/intel_kpi/i226_xdp_125us_1x64b_11k_proclat/reference.yaml b/tests/intel_kpi/i226_xdp_125us_1x64b_11k_proclat/reference.yaml\ndeleted file mode 100644\nindex 50b55ad..0000000\n--- a/tests/intel_kpi/i226_xdp_125us_1x64b_11k_proclat/reference.yaml\n+++ /dev/null\n@@ -1,55 +0,0 @@\n----\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Reference YAML configuration file for Intel i226 NIC(s) KPI testing.\n-#\n-Application:\n-  ApplicationClockId: CLOCK_TAI\n-  # ApplicationBaseStartTimeNS: 0\n-  ApplicationBaseCycleTimeNS: 125us\n-  ApplicationTxBaseOffsetNS: 80us\n-  ApplicationRxBaseOffsetNS: 15us\n-  ApplicationXdpProgram: xdp_kern_profinet_vid100.o\n-TsnHigh:\n-  TsnHighEnabled: true\n-  TsnHighXdpEnabled: true\n-  TsnHighXdpSkbMode: false\n-  TsnHighXdpZcMode: true\n-  TsnHighXdpWakeupMode: true\n-  TsnHighXdpBusyPollMode: false\n-  TsnHighTxTimeStampEnabled: false\n-  TsnHighVid: 100\n-  TsnHighNumFramesPerCycle: 1\n-  TsnHighPayloadPattern: |\n-    TsnHighPayloadPattern\n-  TsnHighFrameLength: 64\n-  TsnHighRxQueue: 1\n-  TsnHighTxQueue: 1\n-  TsnHighSocketPriority: 6\n-  TsnHighTxThreadPriority: 98\n-  TsnHighRxThreadPriority: 98\n-  TsnHighTxThreadCpu: 1\n-  TsnHighRxThreadCpu: 1\n-  TsnHighInterface: enp3s0\n-  TsnHighDestination: a8:74:1d:9d:98:d8\n-Log:\n-  LogThreadPriority: 1\n-  LogThreadCpu: 0\n-  LogFile: /var/log/reference_vid100.log\n-  LogLevel: Info\n-LogMqtt:\n-  LogMqtt: false\n-  LogMqttThreadPriority: 10\n-  LogMqttThreadCpu: 0\n-  LogMqttBrokerIP: 127.0.0.1\n-  LogMqttBrokerPort: 1883\n-  LogMqttMeasurementName: reference\n-Debug:\n-  DebugStopTraceOnOutlier: false\n-  DebugStopTraceOnError: false\n-  DebugMonitorMode: false\n-  DebugMonitorDestination: 44:44:44:44:44:44\ndiff --git a/tests/intel_kpi/i226_xdp_1ms_1x64b_57k_proclat/flow_mirror.sh b/tests/intel_kpi/i226_xdp_1ms_1x64b_57k_proclat/flow_mirror.sh\ndeleted file mode 100755\nindex 363ed5b..0000000\n--- a/tests/intel_kpi/i226_xdp_1ms_1x64b_57k_proclat/flow_mirror.sh\n+++ /dev/null\n@@ -1,62 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Setup the Tx and Rx traffic flows for Intel i226 NIC(s) KPI testing.\n-#\n-\n-set -e\n-\n-source ../../lib/common.sh\n-source ../../lib/igc.sh\n-\n-#\n-# Command line arguments.\n-#\n-INTERFACE=$1\n-CYCLETIME_NS=$2\n-BASETIME=$3\n-\n-[ -z $INTERFACE ] && INTERFACE=\"enp3s0\"                          # default: enp3s0\n-[ -z $CYCLETIME_NS ] && CYCLETIME_NS=\"1000000\"                   # default: 1ms\n-[ -z $BASETIME ] && BASETIME=$(date '+%s000000000' -d '-30 sec') # default: now - 30s\n-\n-load_kernel_modules\n-\n-igc_start \"${INTERFACE}\"\n-ethtool -C ${INTERFACE} rx-usecs 0\n-\n-#\n-# Split traffic between TSN High Stream and everything else.\n-#\n-ENTRY1_NS=\"500000\" # Everything else\n-ENTRY2_NS=\"500000\" # TSN High Stream\n-\n-#\n-# Tx Assignment with Qbv and full hardware offload.\n-#\n-# PCP 6 - Tx Q 1 - TSN High Stream\n-# PCP X - Tx Q 0 - Everything else\n-#\n-tc qdisc replace dev ${INTERFACE} handle 100 parent root taprio num_tc 2 \\\n-  map 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 \\\n-  queues 1@0 1@1 \\\n-  base-time ${BASETIME} \\\n-  sched-entry S 0x03 ${ENTRY1_NS} \\\n-  sched-entry S 0x02 ${ENTRY2_NS} \\\n-  flags 0x02\n-\n-#\n-# Rx Queues Assignment.\n-#\n-# PCP 6 - Rx Q 1 - TSN High Stream\n-# PCP X - Rx Q 0 - Everything else\n-RXQUEUES=(0 1 0 0 0 0 0 0 0 0)\n-igc_rx_queues_assign \"${INTERFACE}\" RXQUEUES\n-\n-setup_irqs \"${INTERFACE}\"\n-\n-exit 0\ndiff --git a/tests/intel_kpi/i226_xdp_1ms_1x64b_57k_proclat/flow_ref.sh b/tests/intel_kpi/i226_xdp_1ms_1x64b_57k_proclat/flow_ref.sh\ndeleted file mode 100755\nindex 87a3b5d..0000000\n--- a/tests/intel_kpi/i226_xdp_1ms_1x64b_57k_proclat/flow_ref.sh\n+++ /dev/null\n@@ -1,62 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Setup the Tx and Rx traffic flows for Intel i226 NIC(s) KPI testing.\n-#\n-\n-set -e\n-\n-source ../../lib/common.sh\n-source ../../lib/igc.sh\n-\n-#\n-# Command line arguments.\n-#\n-INTERFACE=$1\n-CYCLETIME_NS=$2\n-BASETIME=$3\n-\n-[ -z $INTERFACE ] && INTERFACE=\"enp3s0\"                          # default: enp3s0\n-[ -z $CYCLETIME_NS ] && CYCLETIME_NS=\"1000000\"                   # default: 1ms\n-[ -z $BASETIME ] && BASETIME=$(date '+%s000000000' -d '-30 sec') # default: now - 30s\n-\n-load_kernel_modules\n-\n-igc_start \"${INTERFACE}\"\n-ethtool -C ${INTERFACE} rx-usecs 0\n-\n-#\n-# Split traffic between TSN High Stream and everything else.\n-#\n-ENTRY1_NS=\"500000\" # TSN High Stream\n-ENTRY2_NS=\"500000\" # Everything else\n-\n-#\n-# Tx Assignment with Qbv and full hardware offload.\n-#\n-# PCP 6 - Tx Q 1 - TSN High Stream\n-# PCP X - Tx Q 0 - Everything else\n-#\n-tc qdisc replace dev ${INTERFACE} handle 100 parent root taprio num_tc 2 \\\n-  map 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 \\\n-  queues 1@0 1@1 \\\n-  base-time ${BASETIME} \\\n-  sched-entry S 0x02 ${ENTRY1_NS} \\\n-  sched-entry S 0x01 ${ENTRY2_NS} \\\n-  flags 0x02\n-\n-#\n-# Rx Queues Assignment.\n-#\n-# PCP 6 - Rx Q 1 - TSN High Stream\n-# PCP X - Rx Q 0 - Everything else\n-RXQUEUES=(0 1 0 0 0 0 0 0 0 0)\n-igc_rx_queues_assign \"${INTERFACE}\" RXQUEUES\n-\n-setup_irqs \"${INTERFACE}\"\n-\n-exit 0\ndiff --git a/tests/intel_kpi/i226_xdp_1ms_1x64b_57k_proclat/mirror.sh b/tests/intel_kpi/i226_xdp_1ms_1x64b_57k_proclat/mirror.sh\ndeleted file mode 100755\nindex f12f6cd..0000000\n--- a/tests/intel_kpi/i226_xdp_1ms_1x64b_57k_proclat/mirror.sh\n+++ /dev/null\n@@ -1,25 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-\n-set -e\n-\n-cd \"$(dirname \"$0\")\"\n-\n-# Start PTP\n-../../../scripts/ptp.sh enp3s0\n-sleep 30\n-\n-# Configure flow\n-./flow_mirror.sh enp3s0\n-sleep 30\n-\n-# Start one instance of mirror application\n-cp ../../../build/xdp_kern_*.o .\n-../../../build/mirror -c mirror.yaml >/dev/shm/mirror.log &\n-\n-exit 0\ndiff --git a/tests/intel_kpi/i226_xdp_1ms_1x64b_57k_proclat/mirror.yaml b/tests/intel_kpi/i226_xdp_1ms_1x64b_57k_proclat/mirror.yaml\ndeleted file mode 100644\nindex c2ee3b4..0000000\n--- a/tests/intel_kpi/i226_xdp_1ms_1x64b_57k_proclat/mirror.yaml\n+++ /dev/null\n@@ -1,65 +0,0 @@\n----\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Mirror YAML configuration file for Intel i226 NIC(s) KPI testing.\n-#\n-Application:\n-  ApplicationClockId: CLOCK_TAI\n-  # ApplicationBaseStartTimeNS: 0\n-  ApplicationBaseCycleTimeNS: 1ms\n-  ApplicationTxBaseOffsetNS: 700us\n-  ApplicationRxBaseOffsetNS: 300us\n-  ApplicationXdpProgram: xdp_kern_profinet_vid100.o\n-TsnHigh:\n-  TsnHighEnabled: true\n-  TsnHighXdpEnabled: true\n-  TsnHighXdpSkbMode: false\n-  TsnHighXdpZcMode: true\n-  TsnHighXdpWakeupMode: true\n-  TsnHighXdpBusyPollMode: false\n-  TsnHighTxTimeStampEnabled: true\n-  TsnHighVid: 100\n-  TsnHighNumFramesPerCycle: 1\n-  TsnHighPayloadPattern: |\n-    TsnHighPayloadPattern\n-  TsnHighFrameLength: 64\n-  TsnHighRxQueue: 1\n-  TsnHighTxQueue: 1\n-  TsnHighSocketPriority: 6\n-  TsnHighTxThreadPriority: 98\n-  TsnHighRxThreadPriority: 98\n-  TsnHighTxThreadCpu: 1\n-  TsnHighRxThreadCpu: 1\n-  TsnHighInterface: enp3s0\n-  TsnHighDestination: a8:74:1d:9d:36:14\n-  TsnHighRxWorkloadEnabled: true\n-  TsnHighRxWorkloadFile: ../../workloads/pointer_chasing/pointer_chasing.so\n-  TsnHighRxWorkloadSetupFunction: ptr_chase_setup\n-  TsnHighRxWorkloadSetupArguments: \"0x4A4000 0x129000\"\n-  TsnHighRxWorkloadTeardownFunction: ptr_chase_teardown\n-  TsnHighRxWorkloadFunction: run_ptr_chasing\n-  TsnHighRxWorkloadPrewarm: false\n-  TsnHighRxWorkloadSkipCount: 100\n-  TsnHighRxWorkloadThreadCpu: 1\n-  TsnHighRxWorkloadThreadPriority: 80\n-Log:\n-  LogThreadPriority: 1\n-  LogThreadCpu: 0\n-  LogFile: /var/log/mirror_vid100.log\n-  LogLevel: Info\n-LogMqtt:\n-  LogMqtt: false\n-  LogMqttThreadPriority: 10\n-  LogMqttThreadCpu: 0\n-  LogMqttBrokerIP: 127.0.0.1\n-  LogMqttBrokerPort: 1883\n-  LogMqttMeasurementName: mirror\n-Debug:\n-  DebugStopTraceOnOutlier: false\n-  DebugStopTraceOnError: false\n-  DebugMonitorMode: false\n-  DebugMonitorDestination: 44:44:44:44:44:44\ndiff --git a/tests/intel_kpi/i226_xdp_1ms_1x64b_57k_proclat/ref.sh b/tests/intel_kpi/i226_xdp_1ms_1x64b_57k_proclat/ref.sh\ndeleted file mode 100755\nindex c7e3719..0000000\n--- a/tests/intel_kpi/i226_xdp_1ms_1x64b_57k_proclat/ref.sh\n+++ /dev/null\n@@ -1,25 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-\n-set -e\n-\n-cd \"$(dirname \"$0\")\"\n-\n-# Start PTP\n-../../../scripts/ptp.sh enp3s0\n-sleep 30\n-\n-# Configure flow\n-./flow_ref.sh enp3s0\n-sleep 30\n-\n-# Start one instance of reference application\n-cp ../../../build/xdp_kern_*.o .\n-../../../build/reference -c reference.yaml >/dev/shm/ref.log &\n-\n-exit 0\ndiff --git a/tests/intel_kpi/i226_xdp_1ms_1x64b_57k_proclat/reference.yaml b/tests/intel_kpi/i226_xdp_1ms_1x64b_57k_proclat/reference.yaml\ndeleted file mode 100644\nindex 68174ae..0000000\n--- a/tests/intel_kpi/i226_xdp_1ms_1x64b_57k_proclat/reference.yaml\n+++ /dev/null\n@@ -1,55 +0,0 @@\n----\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Reference YAML configuration file for Intel i226 NIC(s) KPI testing.\n-#\n-Application:\n-  ApplicationClockId: CLOCK_TAI\n-  # ApplicationBaseStartTimeNS: 0\n-  ApplicationBaseCycleTimeNS: 1ms\n-  ApplicationTxBaseOffsetNS: 700us\n-  ApplicationRxBaseOffsetNS: 300us\n-  ApplicationXdpProgram: xdp_kern_profinet_vid100.o\n-TsnHigh:\n-  TsnHighEnabled: true\n-  TsnHighXdpEnabled: true\n-  TsnHighXdpSkbMode: false\n-  TsnHighXdpZcMode: true\n-  TsnHighXdpWakeupMode: true\n-  TsnHighXdpBusyPollMode: false\n-  TsnHighTxTimeStampEnabled: false\n-  TsnHighVid: 100\n-  TsnHighNumFramesPerCycle: 1\n-  TsnHighPayloadPattern: |\n-    TsnHighPayloadPattern\n-  TsnHighFrameLength: 64\n-  TsnHighRxQueue: 1\n-  TsnHighTxQueue: 1\n-  TsnHighSocketPriority: 6\n-  TsnHighTxThreadPriority: 98\n-  TsnHighRxThreadPriority: 98\n-  TsnHighTxThreadCpu: 1\n-  TsnHighRxThreadCpu: 1\n-  TsnHighInterface: enp3s0\n-  TsnHighDestination: a8:74:1d:9d:98:d8\n-Log:\n-  LogThreadPriority: 1\n-  LogThreadCpu: 0\n-  LogFile: /var/log/reference_vid100.log\n-  LogLevel: Info\n-LogMqtt:\n-  LogMqtt: false\n-  LogMqttThreadPriority: 10\n-  LogMqttThreadCpu: 0\n-  LogMqttBrokerIP: 127.0.0.1\n-  LogMqttBrokerPort: 1883\n-  LogMqttMeasurementName: reference\n-Debug:\n-  DebugStopTraceOnOutlier: false\n-  DebugStopTraceOnError: false\n-  DebugMonitorMode: false\n-  DebugMonitorDestination: 44:44:44:44:44:44\ndiff --git a/tests/intel_kpi/i226_xdp_1ms_32x128b_57k/flow.sh b/tests/intel_kpi/i226_xdp_1ms_32x128b_57k/flow.sh\ndeleted file mode 100755\nindex a7dcf58..0000000\n--- a/tests/intel_kpi/i226_xdp_1ms_32x128b_57k/flow.sh\n+++ /dev/null\n@@ -1,66 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Setup the Tx and Rx traffic flows for Intel i226 for KPI testing.\n-#\n-\n-set -e\n-\n-source ../../lib/common.sh\n-source ../../lib/igc.sh\n-\n-#\n-# Command line arguments.\n-#\n-INTERFACE=$1\n-CYCLETIME_NS=$2\n-BASETIME=$3\n-\n-[ -z $INTERFACE ] && INTERFACE=\"enp3s0\"                          # default: enp3s0\n-[ -z $CYCLETIME_NS ] && CYCLETIME_NS=\"1000000\"                   # default: 1ms\n-[ -z $BASETIME ] && BASETIME=$(date '+%s000000000' -d '-30 sec') # default: now - 30s\n-\n-load_kernel_modules\n-\n-napi_defer_hard_irqs \"${INTERFACE}\" \"${CYCLETIME_NS}\"\n-\n-igc_start \"${INTERFACE}\"\n-\n-#\n-# Split traffic between TSN High stream and everything else.\n-#\n-ENTRY1_NS=\"500000\" # TSN High stream\n-ENTRY2_NS=\"500000\" # Everything else\n-\n-#\n-# Tx Assignment with Qbv and full hardware offload.\n-#\n-# PCP 6 - Tx Q 1 - TSN High stream\n-# PCP X - Tx Q 0 - Everything else\n-#\n-tc qdisc replace dev ${INTERFACE} handle 100 parent root taprio num_tc 2 \\\n-  map 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 \\\n-  queues 1@0 1@1 \\\n-  base-time ${BASETIME} \\\n-  sched-entry S 0x02 ${ENTRY1_NS} \\\n-  sched-entry S 0x03 ${ENTRY2_NS} \\\n-  flags 0x02\n-\n-#\n-# Rx Queues Assignment.\n-#\n-# PCP 6 - Rx Q 1 - TSN High stream\n-# PCP X - Rx Q 0 - Everything else\n-#\n-RXQUEUES=(0 1 0 0 0 0 0 0 0 0)\n-igc_rx_queues_assign \"${INTERFACE}\" RXQUEUES\n-\n-igc_end \"${INTERFACE}\"\n-\n-setup_irqs \"${INTERFACE}\"\n-\n-exit 0\ndiff --git a/tests/intel_kpi/i226_xdp_1ms_32x128b_57k/mirror.sh b/tests/intel_kpi/i226_xdp_1ms_32x128b_57k/mirror.sh\ndeleted file mode 100755\nindex 7777d82..0000000\n--- a/tests/intel_kpi/i226_xdp_1ms_32x128b_57k/mirror.sh\n+++ /dev/null\n@@ -1,25 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-\n-set -e\n-\n-cd \"$(dirname \"$0\")\"\n-\n-# Start PTP\n-../../../scripts/ptp.sh enp3s0\n-sleep 30\n-\n-# Configure flow\n-./flow.sh enp3s0\n-sleep 30\n-\n-# Start one instance of mirror application\n-cp ../../../build/xdp_kern_*.o .\n-../../../build/mirror -c mirror.yaml >mirror.log &\n-\n-exit 0\ndiff --git a/tests/intel_kpi/i226_xdp_1ms_32x128b_57k/mirror.yaml b/tests/intel_kpi/i226_xdp_1ms_32x128b_57k/mirror.yaml\ndeleted file mode 100644\nindex 92f1140..0000000\n--- a/tests/intel_kpi/i226_xdp_1ms_32x128b_57k/mirror.yaml\n+++ /dev/null\n@@ -1,65 +0,0 @@\n----\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Sample mirror YAML configuration file for Intel platforms with i226 NIC(s).\n-#\n-Application:\n-  ApplicationClockId: CLOCK_TAI\n-  # ApplicationBaseStartTimeNS: 0\n-  ApplicationBaseCycleTimeNS: 1ms\n-  ApplicationTxBaseOffsetNS: 700us\n-  ApplicationRxBaseOffsetNS: 300us\n-  ApplicationXdpProgram: xdp_kern_profinet_vid100.o\n-TsnHigh:\n-  TsnHighEnabled: true\n-  TsnHighXdpEnabled: true\n-  TsnHighXdpSkbMode: false\n-  TsnHighXdpZcMode: true\n-  TsnHighXdpWakeupMode: true\n-  TsnHighXdpBusyPollMode: true\n-  TsnHighTxTimeStampEnabled: false\n-  TsnHighVid: 100\n-  TsnHighNumFramesPerCycle: 32\n-  TsnHighPayloadPattern: |\n-    TsnHighPayloadPattern\n-  TsnHighFrameLength: 128\n-  TsnHighRxQueue: 1\n-  TsnHighTxQueue: 1\n-  TsnHighSocketPriority: 7\n-  TsnHighTxThreadPriority: 98\n-  TsnHighRxThreadPriority: 98\n-  TsnHighTxThreadCpu: 1\n-  TsnHighRxThreadCpu: 1\n-  TsnHighInterface: enp3s0\n-  TsnHighDestination: a8:74:1d:9d:36:14\n-  TsnHighRxWorkloadEnabled: true\n-  TsnHighRxWorkloadFile: ../../workloads/pointer_chasing/pointer_chasing.so\n-  TsnHighRxWorkloadSetupFunction: ptr_chase_setup\n-  TsnHighRxWorkloadSetupArguments: \"0x4A4000 0x129000\"\n-  TsnHighRxWorkloadTeardownFunction: ptr_chase_teardown\n-  TsnHighRxWorkloadFunction: run_ptr_chasing\n-  TsnHighRxWorkloadPrewarm: false\n-  TsnHighRxWorkloadSkipCount: 100\n-  TsnHighRxWorkloadThreadCpu: 1\n-  TsnHighRxWorkloadThreadPriority: 80\n-Log:\n-  LogThreadPriority: 1\n-  LogThreadCpu: 0\n-  LogFile: /var/log/mirror_vid100.log\n-  LogLevel: Info\n-LogMqtt:\n-  LogMqtt: true\n-  LogMqttThreadPriority: 10\n-  LogMqttThreadCpu: 0\n-  LogMqttBrokerIP: 127.0.0.1\n-  LogMqttBrokerPort: 1883\n-  LogMqttMeasurementName: mirror\n-Debug:\n-  DebugStopTraceOnOutlier: false\n-  DebugStopTraceOnError: false\n-  DebugMonitorMode: false\n-  DebugMonitorDestination: 44:44:44:44:44:44\ndiff --git a/tests/intel_kpi/i226_xdp_1ms_32x128b_57k/ref.sh b/tests/intel_kpi/i226_xdp_1ms_32x128b_57k/ref.sh\ndeleted file mode 100755\nindex 0eb36d0..0000000\n--- a/tests/intel_kpi/i226_xdp_1ms_32x128b_57k/ref.sh\n+++ /dev/null\n@@ -1,25 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-\n-set -e\n-\n-cd \"$(dirname \"$0\")\"\n-\n-# Start PTP\n-../../../scripts/ptp.sh enp3s0\n-sleep 30\n-\n-# Configure flow\n-./flow.sh enp3s0\n-sleep 30\n-\n-# Start one instance of reference application\n-cp ../../../build/xdp_kern_*.o .\n-../../../build/reference -c reference.yaml >ref.log &\n-\n-exit 0\ndiff --git a/tests/lib/common.sh b/tests/lib/common.sh\nindex 3c472d1..45184b7 100644\n--- a/tests/lib/common.sh\n+++ b/tests/lib/common.sh\n@@ -37,27 +37,6 @@ napi_defer_hard_irqs() {\n   echo \"${gro_flush_timeout}\" >\"/sys/class/net/${interface}/gro_flush_timeout\"\n }\n \n-#\n-# napi_defer_hard_irqs_queue($napictl, $interface, $cycle_time_ns, $queue)\n-#\n-# Same as napi_defer_hard_irqs(), but does it only for a specific queue.\n-#\n-napi_defer_hard_irqs_queue() {\n-  local napictl=$1\n-  local interface=$2\n-  local cycle_time=$3\n-  local queue=$4\n-  local gro_flush_timeout\n-\n-  if ! [ -x \"$napictl\" ]; then\n-    echo \"$napictl not found!\"\n-    return 1\n-  fi\n-\n-  gro_flush_timeout=$(echo \"$cycle_time * 2\" | bc)\n-  $napictl -i \"${interface}\" -q \"${queue}\" -d 10 -g \"${gro_flush_timeout}\"\n-}\n-\n #\n # setup_threaded_napi($interface)\n #\ndiff --git a/tests/lib/igc.sh b/tests/lib/igc.sh\nindex d918f54..5f2f3e2 100644\n--- a/tests/lib/igc.sh\n+++ b/tests/lib/igc.sh\n@@ -28,11 +28,6 @@ igc_start() {\n   # Increase the number of Rx and Tx queues in case of $num_cpus < 4.\n   #\n   ethtool -L \"${interface}\" combined 4\n-\n-  #\n-  # Disable Energy Efficient Ethernet (EEE).\n-  #\n-  ethtool --set-eee \"${interface}\" eee off\n }\n \n #\ndiff --git a/tests/multi_middleware/stmmac/packet/flow.sh b/tests/multi_middleware/stmmac/packet/flow_ehl.sh\nsimilarity index 100%\nrename from tests/multi_middleware/stmmac/packet/flow.sh\nrename to tests/multi_middleware/stmmac/packet/flow_ehl.sh\ndiff --git a/tests/multi_middleware/stmmac/packet/mirror.sh b/tests/multi_middleware/stmmac/packet/mirror.sh\nindex 397e0be..6f5da7a 100755\n--- a/tests/multi_middleware/stmmac/packet/mirror.sh\n+++ b/tests/multi_middleware/stmmac/packet/mirror.sh\n@@ -15,16 +15,16 @@ cd \"$(dirname \"$0\")\"\n sleep 10\n \n # Configure flow\n-./flow.sh enp0s29f2\n+./flow_ehl.sh enp0s29f2\n sleep 10\n \n # Start four instances of mirror applications\n-../../../../build/mirror -c mirror_vid100.yaml >mirror1.log &\n+../../../../build/mirror -c mirror_vid100_ehl.yaml >mirror1.log &\n sleep 1\n-../../../../build/mirror -c mirror_opcua_vid200.yaml >mirror2.log &\n+../../../../build/mirror -c mirror_opcua_vid200_ehl.yaml >mirror2.log &\n sleep 1\n-../../../../build/mirror -c mirror_opcua_vid300.yaml >mirror3.log &\n+../../../../build/mirror -c mirror_opcua_vid300_ehl.yaml >mirror3.log &\n sleep 1\n-../../../../build/mirror -c mirror_avtp_vid400.yaml >mirror4.log &\n+../../../../build/mirror -c mirror_avtp_vid400_ehl.yaml >mirror4.log &\n \n exit 0\ndiff --git a/tests/multi_middleware/stmmac/packet/mirror_avtp_vid400.yaml b/tests/multi_middleware/stmmac/packet/mirror_avtp_vid400_ehl.yaml\nsimilarity index 100%\nrename from tests/multi_middleware/stmmac/packet/mirror_avtp_vid400.yaml\nrename to tests/multi_middleware/stmmac/packet/mirror_avtp_vid400_ehl.yaml\ndiff --git a/tests/multi_middleware/stmmac/packet/mirror_opcua_vid200.yaml b/tests/multi_middleware/stmmac/packet/mirror_opcua_vid200_ehl.yaml\nsimilarity index 100%\nrename from tests/multi_middleware/stmmac/packet/mirror_opcua_vid200.yaml\nrename to tests/multi_middleware/stmmac/packet/mirror_opcua_vid200_ehl.yaml\ndiff --git a/tests/multi_middleware/stmmac/packet/mirror_opcua_vid300.yaml b/tests/multi_middleware/stmmac/packet/mirror_opcua_vid300_ehl.yaml\nsimilarity index 100%\nrename from tests/multi_middleware/stmmac/packet/mirror_opcua_vid300.yaml\nrename to tests/multi_middleware/stmmac/packet/mirror_opcua_vid300_ehl.yaml\ndiff --git a/tests/multi_middleware/stmmac/packet/mirror_vid100.yaml b/tests/multi_middleware/stmmac/packet/mirror_vid100_ehl.yaml\nsimilarity index 100%\nrename from tests/multi_middleware/stmmac/packet/mirror_vid100.yaml\nrename to tests/multi_middleware/stmmac/packet/mirror_vid100_ehl.yaml\ndiff --git a/tests/multi_middleware/stmmac/packet/ref.sh b/tests/multi_middleware/stmmac/packet/ref.sh\nindex 08e1f19..5161319 100755\n--- a/tests/multi_middleware/stmmac/packet/ref.sh\n+++ b/tests/multi_middleware/stmmac/packet/ref.sh\n@@ -15,16 +15,16 @@ cd \"$(dirname \"$0\")\"\n sleep 10\n \n # Configure flow\n-./flow.sh enp0s29f2\n+./flow_ehl.sh enp0s29f2\n sleep 10\n \n # Start four instances of reference applications\n-../../../../build/reference -c reference_vid100.yaml >ref1.log &\n+../../../../build/reference -c reference_vid100_ehl.yaml >ref1.log &\n sleep 1\n-../../../../build/reference -c reference_opcua_vid200.yaml >ref2.log &\n+../../../../build/reference -c reference_opcua_vid200_ehl.yaml >ref2.log &\n sleep 1\n-../../../../build/reference -c reference_opcua_vid300.yaml >ref3.log &\n+../../../../build/reference -c reference_opcua_vid300_ehl.yaml >ref3.log &\n sleep 1\n-../../../../build/reference -c reference_avtp_vid400.yaml >ref4.log &\n+../../../../build/reference -c reference_avtp_vid400_ehl.yaml >ref4.log &\n \n exit 0\ndiff --git a/tests/multi_middleware/stmmac/packet/reference_avtp_vid400.yaml b/tests/multi_middleware/stmmac/packet/reference_avtp_vid400_ehl.yaml\nsimilarity index 100%\nrename from tests/multi_middleware/stmmac/packet/reference_avtp_vid400.yaml\nrename to tests/multi_middleware/stmmac/packet/reference_avtp_vid400_ehl.yaml\ndiff --git a/tests/multi_middleware/stmmac/packet/reference_opcua_vid200.yaml b/tests/multi_middleware/stmmac/packet/reference_opcua_vid200_ehl.yaml\nsimilarity index 100%\nrename from tests/multi_middleware/stmmac/packet/reference_opcua_vid200.yaml\nrename to tests/multi_middleware/stmmac/packet/reference_opcua_vid200_ehl.yaml\ndiff --git a/tests/multi_middleware/stmmac/packet/reference_opcua_vid300.yaml b/tests/multi_middleware/stmmac/packet/reference_opcua_vid300_ehl.yaml\nsimilarity index 100%\nrename from tests/multi_middleware/stmmac/packet/reference_opcua_vid300.yaml\nrename to tests/multi_middleware/stmmac/packet/reference_opcua_vid300_ehl.yaml\ndiff --git a/tests/multi_middleware/stmmac/packet/reference_vid100.yaml b/tests/multi_middleware/stmmac/packet/reference_vid100_ehl.yaml\nsimilarity index 100%\nrename from tests/multi_middleware/stmmac/packet/reference_vid100.yaml\nrename to tests/multi_middleware/stmmac/packet/reference_vid100_ehl.yaml\ndiff --git a/tests/multi_middleware/stmmac/xdp/flow.sh b/tests/multi_middleware/stmmac/xdp/flow_ehl.sh\nsimilarity index 100%\nrename from tests/multi_middleware/stmmac/xdp/flow.sh\nrename to tests/multi_middleware/stmmac/xdp/flow_ehl.sh\ndiff --git a/tests/multi_middleware/stmmac/xdp/mirror.sh b/tests/multi_middleware/stmmac/xdp/mirror.sh\nindex 25e059d..efd2dc0 100755\n--- a/tests/multi_middleware/stmmac/xdp/mirror.sh\n+++ b/tests/multi_middleware/stmmac/xdp/mirror.sh\n@@ -15,17 +15,17 @@ cd \"$(dirname \"$0\")\"\n sleep 10\n \n # Configure flow\n-./flow.sh enp0s29f2\n+./flow_ehl.sh enp0s29f2\n sleep 10\n \n # Start four instances of mirror applications\n cp ../../../../build/xdp_kern_*.o .\n-../../../../build/mirror -c mirror_vid100.yaml >mirror1.log &\n+../../../../build/mirror -c mirror_vid100_ehl.yaml >mirror1.log &\n sleep 1\n-../../../../build/mirror -c mirror_opcua_vid200.yaml >mirror2.log &\n+../../../../build/mirror -c mirror_opcua_vid200_ehl.yaml >mirror2.log &\n sleep 1\n-../../../../build/mirror -c mirror_opcua_vid300.yaml >mirror3.log &\n+../../../../build/mirror -c mirror_opcua_vid300_ehl.yaml >mirror3.log &\n sleep 1\n-../../../../build/mirror -c mirror_avtp_vid400.yaml >mirror4.log &\n+../../../../build/mirror -c mirror_avtp_vid400_ehl.yaml >mirror4.log &\n \n exit 0\ndiff --git a/tests/multi_middleware/stmmac/xdp/mirror_avtp_vid400.yaml b/tests/multi_middleware/stmmac/xdp/mirror_avtp_vid400_ehl.yaml\nsimilarity index 100%\nrename from tests/multi_middleware/stmmac/xdp/mirror_avtp_vid400.yaml\nrename to tests/multi_middleware/stmmac/xdp/mirror_avtp_vid400_ehl.yaml\ndiff --git a/tests/multi_middleware/stmmac/xdp/mirror_opcua_vid200.yaml b/tests/multi_middleware/stmmac/xdp/mirror_opcua_vid200_ehl.yaml\nsimilarity index 100%\nrename from tests/multi_middleware/stmmac/xdp/mirror_opcua_vid200.yaml\nrename to tests/multi_middleware/stmmac/xdp/mirror_opcua_vid200_ehl.yaml\ndiff --git a/tests/multi_middleware/stmmac/xdp/mirror_opcua_vid300.yaml b/tests/multi_middleware/stmmac/xdp/mirror_opcua_vid300_ehl.yaml\nsimilarity index 100%\nrename from tests/multi_middleware/stmmac/xdp/mirror_opcua_vid300.yaml\nrename to tests/multi_middleware/stmmac/xdp/mirror_opcua_vid300_ehl.yaml\ndiff --git a/tests/multi_middleware/stmmac/xdp/mirror_vid100.yaml b/tests/multi_middleware/stmmac/xdp/mirror_vid100_ehl.yaml\nsimilarity index 100%\nrename from tests/multi_middleware/stmmac/xdp/mirror_vid100.yaml\nrename to tests/multi_middleware/stmmac/xdp/mirror_vid100_ehl.yaml\ndiff --git a/tests/multi_middleware/stmmac/xdp/ref.sh b/tests/multi_middleware/stmmac/xdp/ref.sh\nindex 554d4b3..6513746 100755\n--- a/tests/multi_middleware/stmmac/xdp/ref.sh\n+++ b/tests/multi_middleware/stmmac/xdp/ref.sh\n@@ -15,17 +15,17 @@ cd \"$(dirname \"$0\")\"\n sleep 10\n \n # Configure flow\n-./flow.sh enp0s29f2\n+./flow_ehl.sh enp0s29f2\n sleep 10\n \n # Start four instances of reference applications\n cp ../../../../build/xdp_kern_*.o .\n-../../../../build/reference -c reference_vid100.yaml >ref1.log &\n+../../../../build/reference -c reference_vid100_ehl.yaml >ref1.log &\n sleep 1\n-../../../../build/reference -c reference_opcua_vid200.yaml >ref2.log &\n+../../../../build/reference -c reference_opcua_vid200_ehl.yaml >ref2.log &\n sleep 1\n-../../../../build/reference -c reference_opcua_vid300.yaml >ref3.log &\n+../../../../build/reference -c reference_opcua_vid300_ehl.yaml >ref3.log &\n sleep 1\n-../../../../build/reference -c reference_avtp_vid400.yaml >ref4.log &\n+../../../../build/reference -c reference_avtp_vid400_ehl.yaml >ref4.log &\n \n exit 0\ndiff --git a/tests/multi_middleware/stmmac/xdp/reference_avtp_vid400.yaml b/tests/multi_middleware/stmmac/xdp/reference_avtp_vid400_ehl.yaml\nsimilarity index 100%\nrename from tests/multi_middleware/stmmac/xdp/reference_avtp_vid400.yaml\nrename to tests/multi_middleware/stmmac/xdp/reference_avtp_vid400_ehl.yaml\ndiff --git a/tests/multi_middleware/stmmac/xdp/reference_opcua_vid200.yaml b/tests/multi_middleware/stmmac/xdp/reference_opcua_vid200_ehl.yaml\nsimilarity index 100%\nrename from tests/multi_middleware/stmmac/xdp/reference_opcua_vid200.yaml\nrename to tests/multi_middleware/stmmac/xdp/reference_opcua_vid200_ehl.yaml\ndiff --git a/tests/multi_middleware/stmmac/xdp/reference_opcua_vid300.yaml b/tests/multi_middleware/stmmac/xdp/reference_opcua_vid300_ehl.yaml\nsimilarity index 100%\nrename from tests/multi_middleware/stmmac/xdp/reference_opcua_vid300.yaml\nrename to tests/multi_middleware/stmmac/xdp/reference_opcua_vid300_ehl.yaml\ndiff --git a/tests/multi_middleware/stmmac/xdp/reference_vid100.yaml b/tests/multi_middleware/stmmac/xdp/reference_vid100_ehl.yaml\nsimilarity index 100%\nrename from tests/multi_middleware/stmmac/xdp/reference_vid100.yaml\nrename to tests/multi_middleware/stmmac/xdp/reference_vid100_ehl.yaml\ndiff --git a/tests/netload/flow.sh b/tests/netload/flow_cml.sh\nsimilarity index 100%\nrename from tests/netload/flow.sh\nrename to tests/netload/flow_cml.sh\ndiff --git a/tests/netload/mirror.sh b/tests/netload/mirror.sh\nindex 22c4c14..befbe3d 100755\n--- a/tests/netload/mirror.sh\n+++ b/tests/netload/mirror.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of mirror application\n cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n+../../build/mirror -c mirror_vid100_cml.yaml >mirror1.log &\n \n exit 0\ndiff --git a/tests/netload/mirror.yaml b/tests/netload/mirror_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/netload/mirror.yaml\nrename to tests/netload/mirror_vid100_cml.yaml\ndiff --git a/tests/netload/ref.sh b/tests/netload/ref.sh\nindex ed207ad..3523e8d 100755\n--- a/tests/netload/ref.sh\n+++ b/tests/netload/ref.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of reference application\n cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n+../../build/reference -c reference_vid100_cml.yaml >ref1.log &\n \n exit 0\ndiff --git a/tests/netload/reference.yaml b/tests/netload/reference_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/netload/reference.yaml\nrename to tests/netload/reference_vid100_cml.yaml\ndiff --git a/tests/opcua/flow.sh b/tests/opcua/flow_cml.sh\nsimilarity index 100%\nrename from tests/opcua/flow.sh\nrename to tests/opcua/flow_cml.sh\ndiff --git a/tests/opcua/mirror.sh b/tests/opcua/mirror.sh\nindex 22c4c14..c133952 100755\n--- a/tests/opcua/mirror.sh\n+++ b/tests/opcua/mirror.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of mirror application\n cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n+../../build/mirror -c mirror_opcua_vid200_cml.yaml >mirror1.log &\n \n exit 0\ndiff --git a/tests/opcua/mirror.yaml b/tests/opcua/mirror_opcua_vid200_cml.yaml\nsimilarity index 100%\nrename from tests/opcua/mirror.yaml\nrename to tests/opcua/mirror_opcua_vid200_cml.yaml\ndiff --git a/tests/opcua/ref.sh b/tests/opcua/ref.sh\nindex ed207ad..339506b 100755\n--- a/tests/opcua/ref.sh\n+++ b/tests/opcua/ref.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of reference application\n cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n+../../build/reference -c reference_opcua_vid200_cml.yaml >ref1.log &\n \n exit 0\ndiff --git a/tests/opcua/reference.yaml b/tests/opcua/reference_opcua_vid200_cml.yaml\nsimilarity index 100%\nrename from tests/opcua/reference.yaml\nrename to tests/opcua/reference_opcua_vid200_cml.yaml\ndiff --git a/tests/opcua_fpe/flow.sh b/tests/opcua_fpe/flow_cml.sh\nsimilarity index 100%\nrename from tests/opcua_fpe/flow.sh\nrename to tests/opcua_fpe/flow_cml.sh\ndiff --git a/tests/opcua_fpe/mirror.sh b/tests/opcua_fpe/mirror.sh\nindex 22c4c14..c133952 100755\n--- a/tests/opcua_fpe/mirror.sh\n+++ b/tests/opcua_fpe/mirror.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of mirror application\n cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n+../../build/mirror -c mirror_opcua_vid200_cml.yaml >mirror1.log &\n \n exit 0\ndiff --git a/tests/opcua_fpe/mirror.yaml b/tests/opcua_fpe/mirror_opcua_vid200_cml.yaml\nsimilarity index 100%\nrename from tests/opcua_fpe/mirror.yaml\nrename to tests/opcua_fpe/mirror_opcua_vid200_cml.yaml\ndiff --git a/tests/opcua_fpe/ref.sh b/tests/opcua_fpe/ref.sh\nindex ed207ad..339506b 100755\n--- a/tests/opcua_fpe/ref.sh\n+++ b/tests/opcua_fpe/ref.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of reference application\n cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n+../../build/reference -c reference_opcua_vid200_cml.yaml >ref1.log &\n \n exit 0\ndiff --git a/tests/opcua_fpe/reference.yaml b/tests/opcua_fpe/reference_opcua_vid200_cml.yaml\nsimilarity index 100%\nrename from tests/opcua_fpe/reference.yaml\nrename to tests/opcua_fpe/reference_opcua_vid200_cml.yaml\ndiff --git a/tests/opcua_packet/flow.sh b/tests/opcua_packet/flow_cml.sh\nsimilarity index 100%\nrename from tests/opcua_packet/flow.sh\nrename to tests/opcua_packet/flow_cml.sh\ndiff --git a/tests/opcua_packet/mirror.sh b/tests/opcua_packet/mirror.sh\nindex 22c4c14..c133952 100755\n--- a/tests/opcua_packet/mirror.sh\n+++ b/tests/opcua_packet/mirror.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of mirror application\n cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n+../../build/mirror -c mirror_opcua_vid200_cml.yaml >mirror1.log &\n \n exit 0\ndiff --git a/tests/opcua_packet/mirror.yaml b/tests/opcua_packet/mirror_opcua_vid200_cml.yaml\nsimilarity index 100%\nrename from tests/opcua_packet/mirror.yaml\nrename to tests/opcua_packet/mirror_opcua_vid200_cml.yaml\ndiff --git a/tests/opcua_packet/ref.sh b/tests/opcua_packet/ref.sh\nindex ed207ad..339506b 100755\n--- a/tests/opcua_packet/ref.sh\n+++ b/tests/opcua_packet/ref.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of reference application\n cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n+../../build/reference -c reference_opcua_vid200_cml.yaml >ref1.log &\n \n exit 0\ndiff --git a/tests/opcua_packet/reference.yaml b/tests/opcua_packet/reference_opcua_vid200_cml.yaml\nsimilarity index 100%\nrename from tests/opcua_packet/reference.yaml\nrename to tests/opcua_packet/reference_opcua_vid200_cml.yaml\ndiff --git a/tests/opcua_txtime/flow.sh b/tests/opcua_txtime/flow_cml.sh\nsimilarity index 100%\nrename from tests/opcua_txtime/flow.sh\nrename to tests/opcua_txtime/flow_cml.sh\ndiff --git a/tests/opcua_txtime/mirror.sh b/tests/opcua_txtime/mirror.sh\nindex 22c4c14..c133952 100755\n--- a/tests/opcua_txtime/mirror.sh\n+++ b/tests/opcua_txtime/mirror.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of mirror application\n cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n+../../build/mirror -c mirror_opcua_vid200_cml.yaml >mirror1.log &\n \n exit 0\ndiff --git a/tests/opcua_txtime/mirror.yaml b/tests/opcua_txtime/mirror_opcua_vid200_cml.yaml\nsimilarity index 100%\nrename from tests/opcua_txtime/mirror.yaml\nrename to tests/opcua_txtime/mirror_opcua_vid200_cml.yaml\ndiff --git a/tests/opcua_txtime/ref.sh b/tests/opcua_txtime/ref.sh\nindex ed207ad..339506b 100755\n--- a/tests/opcua_txtime/ref.sh\n+++ b/tests/opcua_txtime/ref.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of reference application\n cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n+../../build/reference -c reference_opcua_vid200_cml.yaml >ref1.log &\n \n exit 0\ndiff --git a/tests/opcua_txtime/reference.yaml b/tests/opcua_txtime/reference_opcua_vid200_cml.yaml\nsimilarity index 100%\nrename from tests/opcua_txtime/reference.yaml\nrename to tests/opcua_txtime/reference_opcua_vid200_cml.yaml\ndiff --git a/tests/profinet/flow.sh b/tests/profinet/flow_cml.sh\nsimilarity index 100%\nrename from tests/profinet/flow.sh\nrename to tests/profinet/flow_cml.sh\ndiff --git a/tests/profinet/mirror.sh b/tests/profinet/mirror.sh\nindex 22c4c14..befbe3d 100755\n--- a/tests/profinet/mirror.sh\n+++ b/tests/profinet/mirror.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of mirror application\n cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n+../../build/mirror -c mirror_vid100_cml.yaml >mirror1.log &\n \n exit 0\ndiff --git a/tests/profinet/mirror.yaml b/tests/profinet/mirror_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/profinet/mirror.yaml\nrename to tests/profinet/mirror_vid100_cml.yaml\ndiff --git a/tests/profinet/ref.sh b/tests/profinet/ref.sh\nindex ed207ad..3523e8d 100755\n--- a/tests/profinet/ref.sh\n+++ b/tests/profinet/ref.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of reference application\n cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n+../../build/reference -c reference_vid100_cml.yaml >ref1.log &\n \n exit 0\ndiff --git a/tests/profinet/reference.yaml b/tests/profinet/reference_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/profinet/reference.yaml\nrename to tests/profinet/reference_vid100_cml.yaml\ndiff --git a/tests/profinet_auth/flow.sh b/tests/profinet_auth/flow_cml.sh\nsimilarity index 100%\nrename from tests/profinet_auth/flow.sh\nrename to tests/profinet_auth/flow_cml.sh\ndiff --git a/tests/profinet_auth/mirror.sh b/tests/profinet_auth/mirror.sh\nindex 22c4c14..befbe3d 100755\n--- a/tests/profinet_auth/mirror.sh\n+++ b/tests/profinet_auth/mirror.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of mirror application\n cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n+../../build/mirror -c mirror_vid100_cml.yaml >mirror1.log &\n \n exit 0\ndiff --git a/tests/profinet_auth/mirror.yaml b/tests/profinet_auth/mirror_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/profinet_auth/mirror.yaml\nrename to tests/profinet_auth/mirror_vid100_cml.yaml\ndiff --git a/tests/profinet_auth/ref.sh b/tests/profinet_auth/ref.sh\nindex ed207ad..3523e8d 100755\n--- a/tests/profinet_auth/ref.sh\n+++ b/tests/profinet_auth/ref.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of reference application\n cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n+../../build/reference -c reference_vid100_cml.yaml >ref1.log &\n \n exit 0\ndiff --git a/tests/profinet_auth/reference.yaml b/tests/profinet_auth/reference_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/profinet_auth/reference.yaml\nrename to tests/profinet_auth/reference_vid100_cml.yaml\ndiff --git a/tests/profinet_crypt/flow.sh b/tests/profinet_crypt/flow_cml.sh\nsimilarity index 100%\nrename from tests/profinet_crypt/flow.sh\nrename to tests/profinet_crypt/flow_cml.sh\ndiff --git a/tests/profinet_crypt/mirror.sh b/tests/profinet_crypt/mirror.sh\nindex 22c4c14..befbe3d 100755\n--- a/tests/profinet_crypt/mirror.sh\n+++ b/tests/profinet_crypt/mirror.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of mirror application\n cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n+../../build/mirror -c mirror_vid100_cml.yaml >mirror1.log &\n \n exit 0\ndiff --git a/tests/profinet_crypt/mirror.yaml b/tests/profinet_crypt/mirror_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/profinet_crypt/mirror.yaml\nrename to tests/profinet_crypt/mirror_vid100_cml.yaml\ndiff --git a/tests/profinet_crypt/ref.sh b/tests/profinet_crypt/ref.sh\nindex ed207ad..3523e8d 100755\n--- a/tests/profinet_crypt/ref.sh\n+++ b/tests/profinet_crypt/ref.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of reference application\n cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n+../../build/reference -c reference_vid100_cml.yaml >ref1.log &\n \n exit 0\ndiff --git a/tests/profinet_crypt/reference.yaml b/tests/profinet_crypt/reference_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/profinet_crypt/reference.yaml\nrename to tests/profinet_crypt/reference_vid100_cml.yaml\ndiff --git a/tests/profinet_packet/flow.sh b/tests/profinet_packet/flow_cml.sh\nsimilarity index 100%\nrename from tests/profinet_packet/flow.sh\nrename to tests/profinet_packet/flow_cml.sh\ndiff --git a/tests/profinet_packet/mirror.sh b/tests/profinet_packet/mirror.sh\nindex 22c4c14..befbe3d 100755\n--- a/tests/profinet_packet/mirror.sh\n+++ b/tests/profinet_packet/mirror.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of mirror application\n cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n+../../build/mirror -c mirror_vid100_cml.yaml >mirror1.log &\n \n exit 0\ndiff --git a/tests/profinet_packet/mirror.yaml b/tests/profinet_packet/mirror_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/profinet_packet/mirror.yaml\nrename to tests/profinet_packet/mirror_vid100_cml.yaml\ndiff --git a/tests/profinet_packet/ref.sh b/tests/profinet_packet/ref.sh\nindex ed207ad..3523e8d 100755\n--- a/tests/profinet_packet/ref.sh\n+++ b/tests/profinet_packet/ref.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of reference application\n cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n+../../build/reference -c reference_vid100_cml.yaml >ref1.log &\n \n exit 0\ndiff --git a/tests/profinet_packet/reference.yaml b/tests/profinet_packet/reference_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/profinet_packet/reference.yaml\nrename to tests/profinet_packet/reference_vid100_cml.yaml\ndiff --git a/tests/profinet_rt/flow.sh b/tests/profinet_rt/flow_cml.sh\nsimilarity index 100%\nrename from tests/profinet_rt/flow.sh\nrename to tests/profinet_rt/flow_cml.sh\ndiff --git a/tests/profinet_rt/flow_noqbv.sh b/tests/profinet_rt/flow_cml_noqbv.sh\nsimilarity index 100%\nrename from tests/profinet_rt/flow_noqbv.sh\nrename to tests/profinet_rt/flow_cml_noqbv.sh\ndiff --git a/tests/profinet_rt/flow_noqbv_polling.sh b/tests/profinet_rt/flow_cml_noqbv_polling.sh\nsimilarity index 100%\nrename from tests/profinet_rt/flow_noqbv_polling.sh\nrename to tests/profinet_rt/flow_cml_noqbv_polling.sh\ndiff --git a/tests/profinet_rt/flow_polling.sh b/tests/profinet_rt/flow_cml_polling.sh\nsimilarity index 100%\nrename from tests/profinet_rt/flow_polling.sh\nrename to tests/profinet_rt/flow_cml_polling.sh\ndiff --git a/tests/profinet_rt/irq.sh b/tests/profinet_rt/irq_cml.sh\nsimilarity index 100%\nrename from tests/profinet_rt/irq.sh\nrename to tests/profinet_rt/irq_cml.sh\ndiff --git a/tests/profinet_rt/mirror.sh b/tests/profinet_rt/mirror.sh\nindex 22c4c14..befbe3d 100755\n--- a/tests/profinet_rt/mirror.sh\n+++ b/tests/profinet_rt/mirror.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of mirror application\n cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n+../../build/mirror -c mirror_vid100_cml.yaml >mirror1.log &\n \n exit 0\ndiff --git a/tests/profinet_rt/mirror.yaml b/tests/profinet_rt/mirror_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/profinet_rt/mirror.yaml\nrename to tests/profinet_rt/mirror_vid100_cml.yaml\ndiff --git a/tests/profinet_rt/mirror_polling.yaml b/tests/profinet_rt/mirror_vid100_cml_polling.yaml\nsimilarity index 100%\nrename from tests/profinet_rt/mirror_polling.yaml\nrename to tests/profinet_rt/mirror_vid100_cml_polling.yaml\ndiff --git a/tests/profinet_rt/ref.sh b/tests/profinet_rt/ref.sh\nindex ed207ad..3523e8d 100755\n--- a/tests/profinet_rt/ref.sh\n+++ b/tests/profinet_rt/ref.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of reference application\n cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n+../../build/reference -c reference_vid100_cml.yaml >ref1.log &\n \n exit 0\ndiff --git a/tests/profinet_rt/reference.yaml b/tests/profinet_rt/reference_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/profinet_rt/reference.yaml\nrename to tests/profinet_rt/reference_vid100_cml.yaml\ndiff --git a/tests/profinet_rt/reference_polling.yaml b/tests/profinet_rt/reference_vid100_cml_polling.yaml\nsimilarity index 100%\nrename from tests/profinet_rt/reference_polling.yaml\nrename to tests/profinet_rt/reference_vid100_cml_polling.yaml\ndiff --git a/tests/profinet_txtime/flow.sh b/tests/profinet_txtime/flow_cml.sh\nsimilarity index 100%\nrename from tests/profinet_txtime/flow.sh\nrename to tests/profinet_txtime/flow_cml.sh\ndiff --git a/tests/profinet_txtime/mirror.sh b/tests/profinet_txtime/mirror.sh\nindex 22c4c14..befbe3d 100755\n--- a/tests/profinet_txtime/mirror.sh\n+++ b/tests/profinet_txtime/mirror.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of mirror application\n cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n+../../build/mirror -c mirror_vid100_cml.yaml >mirror1.log &\n \n exit 0\ndiff --git a/tests/profinet_txtime/mirror.yaml b/tests/profinet_txtime/mirror_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/profinet_txtime/mirror.yaml\nrename to tests/profinet_txtime/mirror_vid100_cml.yaml\ndiff --git a/tests/profinet_txtime/ref.sh b/tests/profinet_txtime/ref.sh\nindex ed207ad..3523e8d 100755\n--- a/tests/profinet_txtime/ref.sh\n+++ b/tests/profinet_txtime/ref.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of reference application\n cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n+../../build/reference -c reference_vid100_cml.yaml >ref1.log &\n \n exit 0\ndiff --git a/tests/profinet_txtime/reference.yaml b/tests/profinet_txtime/reference_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/profinet_txtime/reference.yaml\nrename to tests/profinet_txtime/reference_vid100_cml.yaml\ndiff --git a/tests/profinet_veth/flow.sh b/tests/profinet_veth/flow_cml.sh\nsimilarity index 100%\nrename from tests/profinet_veth/flow.sh\nrename to tests/profinet_veth/flow_cml.sh\ndiff --git a/tests/profinet_veth/mirror.sh b/tests/profinet_veth/mirror.sh\nindex 57534fb..befbe3d 100755\n--- a/tests/profinet_veth/mirror.sh\n+++ b/tests/profinet_veth/mirror.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of mirror application\n cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror_vid100.yaml >mirror.log &\n+../../build/mirror -c mirror_vid100_cml.yaml >mirror1.log &\n \n exit 0\ndiff --git a/tests/profinet_veth/mirror_vid100.yaml b/tests/profinet_veth/mirror_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/profinet_veth/mirror_vid100.yaml\nrename to tests/profinet_veth/mirror_vid100_cml.yaml\ndiff --git a/tests/profinet_veth/mirror_vid200.yaml b/tests/profinet_veth/mirror_vid200_cml.yaml\nsimilarity index 100%\nrename from tests/profinet_veth/mirror_vid200.yaml\nrename to tests/profinet_veth/mirror_vid200_cml.yaml\ndiff --git a/tests/profinet_veth/ref.sh b/tests/profinet_veth/ref.sh\nindex 64e57da..3523e8d 100755\n--- a/tests/profinet_veth/ref.sh\n+++ b/tests/profinet_veth/ref.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of reference application\n cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference_vid100.yaml >ref.log &\n+../../build/reference -c reference_vid100_cml.yaml >ref1.log &\n \n exit 0\ndiff --git a/tests/profinet_veth/reference_vid100.yaml b/tests/profinet_veth/reference_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/profinet_veth/reference_vid100.yaml\nrename to tests/profinet_veth/reference_vid100_cml.yaml\ndiff --git a/tests/profinet_veth/reference_vid200.yaml b/tests/profinet_veth/reference_vid200_cml.yaml\nsimilarity index 100%\nrename from tests/profinet_veth/reference_vid200.yaml\nrename to tests/profinet_veth/reference_vid200_cml.yaml\ndiff --git a/tests/profinet_veth_xdp/flow.sh b/tests/profinet_veth_xdp/flow_cml.sh\nsimilarity index 100%\nrename from tests/profinet_veth_xdp/flow.sh\nrename to tests/profinet_veth_xdp/flow_cml.sh\ndiff --git a/tests/profinet_veth_xdp/mirror.sh b/tests/profinet_veth_xdp/mirror.sh\nindex 57534fb..befbe3d 100755\n--- a/tests/profinet_veth_xdp/mirror.sh\n+++ b/tests/profinet_veth_xdp/mirror.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of mirror application\n cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror_vid100.yaml >mirror.log &\n+../../build/mirror -c mirror_vid100_cml.yaml >mirror1.log &\n \n exit 0\ndiff --git a/tests/profinet_veth_xdp/mirror_vid100.yaml b/tests/profinet_veth_xdp/mirror_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/profinet_veth_xdp/mirror_vid100.yaml\nrename to tests/profinet_veth_xdp/mirror_vid100_cml.yaml\ndiff --git a/tests/profinet_veth_xdp/mirror_vid200.yaml b/tests/profinet_veth_xdp/mirror_vid200_cml.yaml\nsimilarity index 100%\nrename from tests/profinet_veth_xdp/mirror_vid200.yaml\nrename to tests/profinet_veth_xdp/mirror_vid200_cml.yaml\ndiff --git a/tests/profinet_veth_xdp/ref.sh b/tests/profinet_veth_xdp/ref.sh\nindex 64e57da..3523e8d 100755\n--- a/tests/profinet_veth_xdp/ref.sh\n+++ b/tests/profinet_veth_xdp/ref.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of reference application\n cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference_vid100.yaml >ref.log &\n+../../build/reference -c reference_vid100_cml.yaml >ref1.log &\n \n exit 0\ndiff --git a/tests/profinet_veth_xdp/reference_vid100.yaml b/tests/profinet_veth_xdp/reference_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/profinet_veth_xdp/reference_vid100.yaml\nrename to tests/profinet_veth_xdp/reference_vid100_cml.yaml\ndiff --git a/tests/profinet_veth_xdp/reference_vid200.yaml b/tests/profinet_veth_xdp/reference_vid200_cml.yaml\nsimilarity index 100%\nrename from tests/profinet_veth_xdp/reference_vid200.yaml\nrename to tests/profinet_veth_xdp/reference_vid200_cml.yaml\ndiff --git a/tests/profinet_zc/flow.sh b/tests/profinet_zc/flow_cml.sh\nsimilarity index 100%\nrename from tests/profinet_zc/flow.sh\nrename to tests/profinet_zc/flow_cml.sh\ndiff --git a/tests/profinet_zc/mirror.sh b/tests/profinet_zc/mirror.sh\nindex 22c4c14..befbe3d 100755\n--- a/tests/profinet_zc/mirror.sh\n+++ b/tests/profinet_zc/mirror.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of mirror application\n cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n+../../build/mirror -c mirror_vid100_cml.yaml >mirror1.log &\n \n exit 0\ndiff --git a/tests/profinet_zc/mirror.yaml b/tests/profinet_zc/mirror_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/profinet_zc/mirror.yaml\nrename to tests/profinet_zc/mirror_vid100_cml.yaml\ndiff --git a/tests/profinet_zc/ref.sh b/tests/profinet_zc/ref.sh\nindex ed207ad..3523e8d 100755\n--- a/tests/profinet_zc/ref.sh\n+++ b/tests/profinet_zc/ref.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start one instance of reference application\n cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n+../../build/reference -c reference_vid100_cml.yaml >ref1.log &\n \n exit 0\ndiff --git a/tests/profinet_zc/reference.yaml b/tests/profinet_zc/reference_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/profinet_zc/reference.yaml\nrename to tests/profinet_zc/reference_vid100_cml.yaml\ndiff --git a/tests/ptm/flow.sh b/tests/ptm/flow_cml.sh\nsimilarity index 100%\nrename from tests/ptm/flow.sh\nrename to tests/ptm/flow_cml.sh\ndiff --git a/tests/ptm/mirror.sh b/tests/ptm/mirror.sh\nindex 44b5042..6dbbec5 100755\n--- a/tests/ptm/mirror.sh\n+++ b/tests/ptm/mirror.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start four instances of mirror applications\n cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n+../../build/mirror -c mirror_vid100_cml.yaml >mirror1.log &\n \n exit 0\ndiff --git a/tests/ptm/mirror.yaml b/tests/ptm/mirror_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/ptm/mirror.yaml\nrename to tests/ptm/mirror_vid100_cml.yaml\ndiff --git a/tests/ptm/ref.sh b/tests/ptm/ref.sh\nindex 08e25d8..24bb2e1 100755\n--- a/tests/ptm/ref.sh\n+++ b/tests/ptm/ref.sh\n@@ -15,11 +15,11 @@ cd \"$(dirname \"$0\")\"\n sleep 30\n \n # Configure flow\n-./flow.sh enp3s0\n+./flow_cml.sh enp3s0\n sleep 30\n \n # Start four instances of reference applications\n cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n+../../build/reference -c reference_vid100_cml.yaml >ref1.log &\n \n exit 0\ndiff --git a/tests/ptm/reference.yaml b/tests/ptm/reference_vid100_cml.yaml\nsimilarity index 100%\nrename from tests/ptm/reference.yaml\nrename to tests/ptm/reference_vid100_cml.yaml\ndiff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt\ndeleted file mode 100644\nindex 2f3dd1e..0000000\n--- a/tests/unit/CMakeLists.txt\n+++ /dev/null\n@@ -1,22 +0,0 @@\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-#\n-\n-pkg_search_module(CMOCKA REQUIRED cmocka)\n-\n-add_executable(ring_buffer_test\n-  ring_buffer_test.c\n-  ${CMAKE_SOURCE_DIR}/src/ring_buffer.c)\n-\n-target_compile_options(ring_buffer_test PRIVATE -Wall -Wextra -Wno-sign-compare -Wshadow -std=gnu99 -Wvla)\n-target_compile_definitions(ring_buffer_test PRIVATE -D_GNU_SOURCE)\n-\n-target_include_directories(ring_buffer_test PRIVATE\n-  ${CMAKE_SOURCE_DIR}/src\n-  ${PROJECT_BINARY_DIR}\n-  ${CMOCKA_INCLUDE_DIRS})\n-\n-target_link_libraries(ring_buffer_test ${CMOCKA_LIBRARIES} Threads::Threads)\n-\n-add_test(NAME ring_buffer_test COMMAND ring_buffer_test)\ndiff --git a/tests/unit/ring_buffer_test.c b/tests/unit/ring_buffer_test.c\ndeleted file mode 100644\nindex d0858a9..0000000\n--- a/tests/unit/ring_buffer_test.c\n+++ /dev/null\n@@ -1,167 +0,0 @@\n-// SPDX-License-Identifier: BSD-2-Clause\n-/*\n- * Copyright (C) 2026 Linutronix GmbH\n- */\n-\n-#include <pthread.h>\n-#include <setjmp.h>\n-#include <stdarg.h>\n-#include <stddef.h>\n-#include <stdint.h>\n-#include <string.h>\n-\n-#include <cmocka.h>\n-\n-#include \"ring_buffer.h\"\n-\n-/*\n- * ring_buffer requires init_mutex().\n- */\n-void init_mutex(pthread_mutex_t *mutex)\n-{\n-\tpthread_mutex_init(mutex, NULL);\n-}\n-\n-static void test_fetch_empty(void **state)\n-{\n-\tstruct ring_buffer *rb = ring_buffer_allocate(128);\n-\tunsigned char data[8];\n-\tsize_t len;\n-\n-\t(void)state;\n-\n-\tassert_non_null(rb);\n-\n-\tring_buffer_fetch(rb, data, sizeof(data), &len);\n-\n-\tassert_int_equal(len, 0);\n-\n-\tring_buffer_free(rb);\n-}\n-\n-static void test_basic_usage(void **state)\n-{\n-\tstruct ring_buffer *rb = ring_buffer_allocate(128);\n-\tunsigned char data2[48];\n-\tunsigned char data[16];\n-\tsize_t len;\n-\n-\t(void)state;\n-\n-\tassert_non_null(rb);\n-\n-\tfor (size_t i = 0; i < sizeof(data); i++)\n-\t\tdata[i] = i;\n-\n-\tring_buffer_add(rb, data, sizeof(data));\n-\tring_buffer_add(rb, data, sizeof(data));\n-\tring_buffer_add(rb, data, sizeof(data));\n-\n-\tring_buffer_fetch(rb, data2, sizeof(data2), &len);\n-\n-\tassert_int_equal(len, sizeof(data2));\n-\n-\tassert_memory_equal(data, data2, sizeof(data));\n-\tassert_memory_equal(data, data2 + sizeof(data), sizeof(data));\n-\tassert_memory_equal(data, data2 + sizeof(data) + sizeof(data), sizeof(data));\n-\n-\tring_buffer_free(rb);\n-}\n-\n-static void test_drop_oldest(void **state)\n-{\n-\tstruct ring_buffer *rb = ring_buffer_allocate(8);\n-\tunsigned char data3[4] = {'i', 'j', 'k', 'l'};\n-\tunsigned char data2[4] = {'e', 'f', 'g', 'h'};\n-\tunsigned char data1[4] = {'a', 'b', 'c', 'd'};\n-\tunsigned char out[8];\n-\tsize_t len;\n-\n-\t(void)state;\n-\n-\tassert_non_null(rb);\n-\n-\tring_buffer_add(rb, data1, sizeof(data1));\n-\tring_buffer_add(rb, data2, sizeof(data2));\n-\tring_buffer_add(rb, data3, sizeof(data3));\n-\n-\tring_buffer_fetch(rb, out, sizeof(out), &len);\n-\n-\tassert_int_equal(len, sizeof(out));\n-\n-\tassert_memory_equal(out, data2, sizeof(data2));\n-\tassert_memory_equal(out + sizeof(data2), data3, sizeof(data3));\n-\n-\tring_buffer_free(rb);\n-}\n-\n-static void test_full(void **state)\n-{\n-\tstruct ring_buffer *rb = ring_buffer_allocate(8);\n-\tunsigned char data2[4] = {'e', 'f', 'g', 'h'};\n-\tunsigned char data1[4] = {'a', 'b', 'c', 'd'};\n-\tunsigned char out[8];\n-\tsize_t len;\n-\n-\t(void)state;\n-\n-\tassert_non_null(rb);\n-\n-\tring_buffer_add(rb, data1, sizeof(data1));\n-\tring_buffer_add(rb, data2, sizeof(data2));\n-\n-\tring_buffer_fetch(rb, out, sizeof(out), &len);\n-\n-\tassert_int_equal(len, sizeof(out));\n-\n-\tassert_memory_equal(out, data1, sizeof(data1));\n-\tassert_memory_equal(out + sizeof(data1), data2, sizeof(data2));\n-\n-\tring_buffer_free(rb);\n-}\n-\n-static void test_wrap_around(void **state)\n-{\n-\tstruct ring_buffer *rb = ring_buffer_allocate(8);\n-\tunsigned char data1[8] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'};\n-\tunsigned char data2[6] = {'i', 'j', 'k', 'l', 'm', 'n'};\n-\tunsigned char out_expected[6] = {'a', 'b', 'c', 'd', 'e', 'f'};\n-\tunsigned char out_expected2[8] = {'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n'};\n-\tunsigned char out[6];\n-\tunsigned char out2[8];\n-\tsize_t len;\n-\n-\t(void)state;\n-\n-\tassert_non_null(rb);\n-\n-\t/* Add 8 bytes -> full */\n-\tring_buffer_add(rb, data1, sizeof(data1));\n-\n-\t/* Drain 6 bytes */\n-\tring_buffer_fetch(rb, out, sizeof(out), &len);\n-\tassert_int_equal(len, sizeof(out));\n-\tassert_memory_equal(out, out_expected, sizeof(out));\n-\n-\t/* Add 6 bytes again to force wrap around */\n-\tring_buffer_add(rb, data2, sizeof(data2));\n-\n-\t/* Should be 8 bytes in total */\n-\tring_buffer_fetch(rb, out2, sizeof(out2), &len);\n-\tassert_int_equal(len, sizeof(out2));\n-\n-\tassert_memory_equal(out2, out_expected2, sizeof(out2));\n-\n-\tring_buffer_free(rb);\n-}\n-\n-int main(void)\n-{\n-\tconst struct CMUnitTest tests[] = {\n-\t\tcmocka_unit_test(test_fetch_empty), cmocka_unit_test(test_basic_usage),\n-\t\tcmocka_unit_test(test_drop_oldest), cmocka_unit_test(test_full),\n-\t\tcmocka_unit_test(test_wrap_around),\n-\t};\n-\n-\treturn cmocka_run_group_tests(tests, NULL, NULL);\n-}\ndiff --git a/tests/working_clock/flow.sh b/tests/working_clock/flow.sh\ndeleted file mode 100755\nindex fb22259..0000000\n--- a/tests/working_clock/flow.sh\n+++ /dev/null\n@@ -1,74 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2023 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Setup the Tx and Rx traffic flows for Intel i225 for profinet scenario.\n-#\n-\n-set -e\n-\n-source ../lib/common.sh\n-source ../lib/igc.sh\n-\n-#\n-# Command line arguments.\n-#\n-INTERFACE=$1\n-CYCLETIME_NS=$2\n-BASETIME=$3\n-\n-[ -z $INTERFACE ] && INTERFACE=\"enp3s0\"        # default: enp3s0\n-[ -z $CYCLETIME_NS ] && CYCLETIME_NS=\"1000000\" # default: 1ms\n-[ -z $BASETIME ] && BASETIME=0                 # default: next cycle\n-\n-load_kernel_modules\n-\n-setup_threaded_napi \"${INTERFACE}\"\n-\n-igc_start \"${INTERFACE}\"\n-\n-#\n-# Split traffic between TSN streams, real time and everything else.\n-#\n-ENTRY1_NS=\"50000\"  # TSN High\n-ENTRY2_NS=\"50000\"  # TSN Low\n-ENTRY3_NS=\"100000\" # RTC\n-ENTRY4_NS=\"800000\" # RTA and everything else\n-\n-#\n-# Tx Assignment with Qbv and full hardware offload.\n-#\n-# PCP 6   - Tx Q 0 - TSN High\n-# PCP 5   - Tx Q 1 - TSN Low\n-# PCP 4   - Tx Q 2 - RTC\n-# PCP 3/X - Tx Q 3 - RTA and Everything else\n-#\n-tc qdisc replace dev ${INTERFACE} handle 100 parent root taprio num_tc 4 \\\n-  map 3 3 3 3 3 2 1 0 3 3 3 3 3 3 3 3 \\\n-  queues 1@0 1@1 1@2 1@3 \\\n-  base-time ${BASETIME} \\\n-  sched-entry S 0x01 ${ENTRY1_NS} \\\n-  sched-entry S 0x02 ${ENTRY2_NS} \\\n-  sched-entry S 0x04 ${ENTRY3_NS} \\\n-  sched-entry S 0x08 ${ENTRY4_NS} \\\n-  flags 0x02\n-\n-#\n-# Rx Queues Assignment.\n-#\n-# Rx Q 3 - All Traffic\n-# Rx Q 2 - RTC\n-# Rx Q 1 - TSN Low\n-# Rx Q 0 - TSN High\n-#\n-RXQUEUES=(3 0 1 2 3 3 3 3 3 3)\n-igc_rx_queues_assign \"${INTERFACE}\" RXQUEUES\n-\n-igc_end \"${INTERFACE}\"\n-\n-setup_irqs \"${INTERFACE}\"\n-\n-exit 0\ndiff --git a/tests/working_clock/mirror.sh b/tests/working_clock/mirror.sh\ndeleted file mode 100755\nindex 546d4d1..0000000\n--- a/tests/working_clock/mirror.sh\n+++ /dev/null\n@@ -1,25 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2023 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-\n-set -e\n-\n-cd \"$(dirname \"$0\")\"\n-\n-# Start PTP\n-./ptp.sh enp3s0\n-sleep 30\n-\n-# Configure flow\n-./flow.sh enp3s0\n-sleep 30\n-\n-# Start one instance of mirror application\n-cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n-\n-exit 0\ndiff --git a/tests/working_clock/mirror.yaml b/tests/working_clock/mirror.yaml\ndeleted file mode 100644\nindex dfa15ac..0000000\n--- a/tests/working_clock/mirror.yaml\n+++ /dev/null\n@@ -1,185 +0,0 @@\n----\n-#\n-# Copyright (C) 2023 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Sample reference YAML configuration file for Intel Comet Lake platforms with\n-# i225 NIC(s).\n-#\n-Application:\n-  ApplicationClockId: CLOCK_AUX0\n-  # ApplicationBaseStartTimeNS: 0\n-  ApplicationBaseCycleTimeNS: 1ms\n-  ApplicationTxBaseOffsetNS: 800us\n-  ApplicationRxBaseOffsetNS: 300us\n-  ApplicationXdpProgram: xdp_kern_profinet_vid100.o\n-TSNHigh:\n-  TsnHighEnabled: true\n-  TsnHighXdpEnabled: true\n-  TsnHighXdpSkbMode: false\n-  TsnHighXdpZcMode: true\n-  TsnHighXdpWakeupMode: true\n-  TsnHighTxTimeEnabled: false\n-  TsnHighTxTimeOffsetNS: 0\n-  TsnHighTxTimeStampEnabled: false\n-  TsnHighVid: 100\n-  TsnHighNumFramesPerCycle: 1\n-  TsnHighPayloadPattern: |\n-    TsnHighPayloadPattern\n-  TsnHighFrameLength: 128\n-  TsnHighRxQueue: 0\n-  TsnHighTxQueue: 0\n-  TsnHighSocketPriority: 7\n-  TsnHighTxThreadPriority: 98\n-  TsnHighRxThreadPriority: 98\n-  TsnHighTxThreadCpu: 0\n-  TsnHighRxThreadCpu: 0\n-  TsnHighInterface: enp3s0\n-  TsnHighDestination: a8:74:1d:9d:36:14\n-TSNLow:\n-  TsnLowEnabled: true\n-  TsnLowXdpEnabled: true\n-  TsnLowXdpSkbMode: false\n-  TsnLowXdpZcMode: true\n-  TsnLowXdpWakeupMode: true\n-  TsnLowTxTimeEnabled: false\n-  TsnLowTxTimeOffsetNS: 0\n-  TsnLowTxTimeStampEnabled: false\n-  TsnLowVid: 100\n-  TsnLowNumFramesPerCycle: 1\n-  TsnLowPayloadPattern: |\n-    TsnLowPayloadPattern\n-  TsnLowFrameLength: 128\n-  TsnLowRxQueue: 1\n-  TsnLowTxQueue: 1\n-  TsnLowSocketPriority: 6\n-  TsnLowTxThreadPriority: 98\n-  TsnLowRxThreadPriority: 98\n-  TsnLowTxThreadCpu: 1\n-  TsnLowRxThreadCpu: 1\n-  TsnLowInterface: enp3s0\n-  TsnLowDestination: a8:74:1d:9d:36:14\n-RTC:\n-  RtcEnabled: true\n-  RtcXdpEnabled: true\n-  RtcXdpSkbMode: false\n-  RtcXdpZcMode: true\n-  RtcXdpWakeupMode: true\n-  RtcTxTimeStampEnabled: false\n-  RtcVid: 100\n-  RtcNumFramesPerCycle: 1\n-  RtcPayloadPattern: |\n-    RtcPayloadPattern\n-  RtcFrameLength: 128\n-  RtcRxQueue: 2\n-  RtcTxQueue: 2\n-  RtcSocketPriority: 5\n-  RtcTxThreadPriority: 98\n-  RtcRxThreadPriority: 98\n-  RtcTxThreadCpu: 2\n-  RtcRxThreadCpu: 2\n-  RtcInterface: enp3s0\n-  RtcDestination: a8:74:1d:9d:36:14\n-RTA:\n-  RtaEnabled: true\n-  RtaXdpEnabled: true\n-  RtaXdpSkbMode: false\n-  RtaXdpZcMode: false\n-  RtaXdpWakeupMode: true\n-  RtaTxTimeStampEnabled: false\n-  RtaVid: 100\n-  RtaBurstPeriodNS: 200ms\n-  RtaNumFramesPerCycle: 1\n-  RtaPayloadPattern: |\n-    RtaPayloadPattern\n-  RtaFrameLength: 200\n-  RtaRxQueue: 3\n-  RtaTxQueue: 3\n-  RtaSocketPriority: 4\n-  RtaTxThreadPriority: 97\n-  RtaRxThreadPriority: 97\n-  RtaTxThreadCpu: 3\n-  RtaRxThreadCpu: 3\n-  RtaInterface: enp3s0\n-  RtaDestination: a8:74:1d:9d:36:14\n-DCP:\n-  DcpEnabled: true\n-  DcpVid: 100\n-  DcpBurstPeriodNS: 2s\n-  DcpNumFramesPerCycle: 1\n-  DcpPayloadPattern: |\n-    DcpPayloadPattern\n-  DcpFrameLength: 200\n-  DcpRxQueue: 3\n-  DcpTxQueue: 3\n-  DcpSocketPriority: 3\n-  DcpTxThreadPriority: 53\n-  DcpRxThreadPriority: 53\n-  DcpTxThreadCpu: 4\n-  DcpRxThreadCpu: 4\n-  DcpInterface: enp3s0\n-  DcpDestination: a8:74:1d:9d:36:14\n-LLDP:\n-  LldpEnabled: true\n-  LldpBurstPeriodNS: 5s\n-  LldpNumFramesPerCycle: 1\n-  LldpPayloadPattern: |\n-    LldpPayloadPattern\n-  LldpFrameLength: 200\n-  LldpRxQueue: 3\n-  LldpTxQueue: 3\n-  LldpSocketPriority: 3\n-  LldpTxThreadPriority: 52\n-  LldpRxThreadPriority: 52\n-  LldpTxThreadCpu: 5\n-  LldpRxThreadCpu: 5\n-  LldpInterface: enp3s0\n-  LldpDestination: 01:80:c2:00:00:0e\n-UDPHigh:\n-  UdpHighEnabled: true\n-  UdpHighBurstPeriodNS: 1s\n-  UdpHighNumFramesPerCycle: 1\n-  UdpHighPayloadPattern: |\n-    UdpHighPayloadPattern\n-  UdpHighFrameLength: 1400\n-  UdpHighRxQueue: 3\n-  UdpHighTxQueue: 3\n-  UdpHighSocketPriority: 3\n-  UdpHighTxThreadPriority: 51\n-  UdpHighRxThreadPriority: 51\n-  UdpHighTxThreadCpu: 6\n-  UdpHighRxThreadCpu: 6\n-  UdpHighInterface: enp3s0\n-  UdpHighPort: 6666\n-  UdpHighDestination: 192.168.1.1\n-  UdpHighSource: 192.168.1.2\n-UDPLow:\n-  UdpLowEnabled: true\n-  UdpLowBurstPeriodNS: 1s\n-  UdpLowNumFramesPerCycle: 1\n-  UdpLowPayloadPattern: |\n-    UdpLowPayloadPattern\n-  UdpLowFrameLength: 1400\n-  UdpLowRxQueue: 3\n-  UdpLowTxQueue: 3\n-  UdpLowSocketPriority: 3\n-  UdpLowTxThreadPriority: 51\n-  UdpLowRxThreadPriority: 51\n-  UdpLowTxThreadCpu: 7\n-  UdpLowRxThreadCpu: 7\n-  UdpLowInterface: enp3s0\n-  UdpLowPort: 6667\n-  UdpLowDestination: 192.168.1.1\n-  UdpLowSource: 192.168.1.2\n-Log:\n-  LogThreadPriority: 1\n-  LogThreadCpu: 7\n-  LogFile: /var/log/mirror_vid100.log\n-  LogLevel: Info\n-Debug:\n-  DebugStopTraceOnOutlier: false\n-  DebugStopTraceOnError: false\n-  DebugMonitorMode: false\n-  DebugMonitorDestination: 44:44:44:44:44:44\ndiff --git a/tests/working_clock/ptp.sh b/tests/working_clock/ptp.sh\ndeleted file mode 100755\nindex 8363b00..0000000\n--- a/tests/working_clock/ptp.sh\n+++ /dev/null\n@@ -1,59 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2021-2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# This testcase has two time domains:\n-#\n-#   - Global time:\n-#       Synchronized by NTP e.g., with chrony. Corresponds to CLOCK_REALTIME and CLOCK_TAI in Linux.\n-#\n-#   - Working clock:\n-#       High precision clock synchronized by gPTP used for real time scheduling. Corresponds to\n-#       CLOCK_AUX0 in Linux.\n-#\n-# The point is CLOCK_AUX0 is an arbitrary time domain (e.g. starting at zero), whereas\n-# CLOCK_REALTIME points to valid UTC time. Both time domains are independent of each other.\n-#\n-# Note 1: This test case requires an update-to-date Linux kernel >= 7.2 for clock_gettime(2) and\n-# clock_nanosleep(2) support on CLOCK_AUX*.\n-#\n-\n-set -e\n-\n-cd \"$(dirname \"$0\")\"\n-\n-# Interface\n-INTERFACE=$1\n-[ -z $INTERFACE ] && INTERFACE=\"eth0\"\n-\n-# Kill already running daemons\n-pkill ptp4l || true\n-pkill phc2sys || true\n-\n-# Build phc2sys with support for CLOCK_AUX\n-if ! [ -d linuxptp ]; then\n-  git clone https://github.com/richardcochran/linuxptp\n-  pushd linuxptp\n-  make -j$(nproc)\n-  popd\n-fi\n-\n-# Set NTP time into PHC\n-phc_ctl ${INTERFACE} set\n-\n-# Start ptp with 802.1AS-2011 endstation profile\n-linuxptp/ptp4l -2 -H -i ${INTERFACE} --socket_priority=4 --tx_timestamp_timeout=40 -f /etc/gPTP.cfg &\n-\n-# Wait for ptp4l\n-sleep 10\n-\n-# Enable CLOCK_AUX0 in kernel timekeeping first\n-echo 1 >/sys/kernel/time/aux_clocks/0/aux_clock_enable\n-\n-# Synchronize CLOCK_AUX0 to network time\n-linuxptp/phc2sys -s ${INTERFACE} -c CLOCK_AUX0 --step_threshold=1 --transportSpecific=1 -w &\n-\n-exit 0\ndiff --git a/tests/working_clock/ref.sh b/tests/working_clock/ref.sh\ndeleted file mode 100755\nindex ba03dc0..0000000\n--- a/tests/working_clock/ref.sh\n+++ /dev/null\n@@ -1,25 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2023 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-\n-set -e\n-\n-cd \"$(dirname \"$0\")\"\n-\n-# Start PTP\n-./ptp.sh enp3s0\n-sleep 30\n-\n-# Configure flow\n-./flow.sh enp3s0\n-sleep 30\n-\n-# Start one instance of reference application\n-cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n-\n-exit 0\ndiff --git a/tests/working_clock/reference.yaml b/tests/working_clock/reference.yaml\ndeleted file mode 100644\nindex 79b9d24..0000000\n--- a/tests/working_clock/reference.yaml\n+++ /dev/null\n@@ -1,185 +0,0 @@\n----\n-#\n-# Copyright (C) 2023 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Sample reference YAML configuration file for Intel Comet Lake platforms with\n-# i225 NIC(s).\n-#\n-Application:\n-  ApplicationClockId: CLOCK_AUX0\n-  # ApplicationBaseStartTimeNS: 0\n-  ApplicationBaseCycleTimeNS: 1ms\n-  ApplicationTxBaseOffsetNS: 800us\n-  ApplicationRxBaseOffsetNS: 300us\n-  ApplicationXdpProgram: xdp_kern_profinet_vid100.o\n-TSNHigh:\n-  TsnHighEnabled: true\n-  TsnHighXdpEnabled: true\n-  TsnHighXdpSkbMode: false\n-  TsnHighXdpZcMode: true\n-  TsnHighXdpWakeupMode: true\n-  TsnHighTxTimeEnabled: false\n-  TsnHighTxTimeOffsetNS: 0\n-  TsnHighTxTimeStampEnabled: false\n-  TsnHighVid: 100\n-  TsnHighNumFramesPerCycle: 1\n-  TsnHighPayloadPattern: |\n-    TsnHighPayloadPattern\n-  TsnHighFrameLength: 128\n-  TsnHighRxQueue: 0\n-  TsnHighTxQueue: 0\n-  TsnHighSocketPriority: 7\n-  TsnHighTxThreadPriority: 98\n-  TsnHighRxThreadPriority: 98\n-  TsnHighTxThreadCpu: 0\n-  TsnHighRxThreadCpu: 0\n-  TsnHighInterface: enp3s0\n-  TsnHighDestination: a8:74:1d:9d:98:d8\n-TSNLow:\n-  TsnLowEnabled: true\n-  TsnLowXdpEnabled: true\n-  TsnLowXdpSkbMode: false\n-  TsnLowXdpZcMode: true\n-  TsnLowXdpWakeupMode: true\n-  TsnLowTxTimeEnabled: false\n-  TsnLowTxTimeOffsetNS: 0\n-  TsnLowTxTimeStampEnabled: false\n-  TsnLowVid: 100\n-  TsnLowNumFramesPerCycle: 1\n-  TsnLowPayloadPattern: |\n-    TsnLowPayloadPattern\n-  TsnLowFrameLength: 128\n-  TsnLowRxQueue: 1\n-  TsnLowTxQueue: 1\n-  TsnLowSocketPriority: 6\n-  TsnLowTxThreadPriority: 98\n-  TsnLowRxThreadPriority: 98\n-  TsnLowTxThreadCpu: 1\n-  TsnLowRxThreadCpu: 1\n-  TsnLowInterface: enp3s0\n-  TsnLowDestination: a8:74:1d:9d:98:d8\n-RTC:\n-  RtcEnabled: true\n-  RtcXdpEnabled: true\n-  RtcXdpSkbMode: false\n-  RtcXdpZcMode: true\n-  RtcXdpWakeupMode: true\n-  RtcTxTimeStampEnabled: false\n-  RtcVid: 100\n-  RtcNumFramesPerCycle: 1\n-  RtcPayloadPattern: |\n-    RtcPayloadPattern\n-  RtcFrameLength: 128\n-  RtcRxQueue: 2\n-  RtcTxQueue: 2\n-  RtcSocketPriority: 5\n-  RtcTxThreadPriority: 98\n-  RtcRxThreadPriority: 98\n-  RtcTxThreadCpu: 2\n-  RtcRxThreadCpu: 2\n-  RtcInterface: enp3s0\n-  RtcDestination: a8:74:1d:9d:98:d8\n-RTA:\n-  RtaEnabled: true\n-  RtaXdpEnabled: true\n-  RtaXdpSkbMode: false\n-  RtaXdpZcMode: false\n-  RtaXdpWakeupMode: true\n-  RtaTxTimeStampEnabled: false\n-  RtaVid: 100\n-  RtaBurstPeriodNS: 200ms\n-  RtaNumFramesPerCycle: 1\n-  RtaPayloadPattern: |\n-    RtaPayloadPattern\n-  RtaFrameLength: 200\n-  RtaRxQueue: 3\n-  RtaTxQueue: 3\n-  RtaSocketPriority: 4\n-  RtaTxThreadPriority: 97\n-  RtaRxThreadPriority: 97\n-  RtaTxThreadCpu: 3\n-  RtaRxThreadCpu: 3\n-  RtaInterface: enp3s0\n-  RtaDestination: a8:74:1d:9d:98:d8\n-DCP:\n-  DcpEnabled: true\n-  DcpVid: 100\n-  DcpBurstPeriodNS: 2s\n-  DcpNumFramesPerCycle: 1\n-  DcpPayloadPattern: |\n-    DcpPayloadPattern\n-  DcpFrameLength: 200\n-  DcpRxQueue: 3\n-  DcpTxQueue: 3\n-  DcpSocketPriority: 3\n-  DcpTxThreadPriority: 53\n-  DcpRxThreadPriority: 53\n-  DcpTxThreadCpu: 4\n-  DcpRxThreadCpu: 4\n-  DcpInterface: enp3s0\n-  DcpDestination: 01:0e:cf:00:00:00\n-LLDP:\n-  LldpEnabled: true\n-  LldpBurstPeriodNS: 5s\n-  LldpNumFramesPerCycle: 1\n-  LldpPayloadPattern: |\n-    LldpPayloadPattern\n-  LldpFrameLength: 200\n-  LldpRxQueue: 3\n-  LldpTxQueue: 3\n-  LldpSocketPriority: 3\n-  LldpTxThreadPriority: 52\n-  LldpRxThreadPriority: 52\n-  LldpTxThreadCpu: 5\n-  LldpRxThreadCpu: 5\n-  LldpInterface: enp3s0\n-  LldpDestination: 01:80:c2:00:00:0e\n-UDPHigh:\n-  UdpHighEnabled: true\n-  UdpHighBurstPeriodNS: 1s\n-  UdpHighNumFramesPerCycle: 1\n-  UdpHighPayloadPattern: |\n-    UdpHighPayloadPattern\n-  UdpHighFrameLength: 1400\n-  UdpHighRxQueue: 3\n-  UdpHighTxQueue: 3\n-  UdpHighSocketPriority: 3\n-  UdpHighTxThreadPriority: 51\n-  UdpHighRxThreadPriority: 51\n-  UdpHighTxThreadCpu: 6\n-  UdpHighRxThreadCpu: 6\n-  UdpHighInterface: enp3s0\n-  UdpHighPort: 6666\n-  UdpHighDestination: 192.168.1.2\n-  UdpHighSource: 192.168.1.1\n-UDPLow:\n-  UdpLowEnabled: true\n-  UdpLowBurstPeriodNS: 1s\n-  UdpLowNumFramesPerCycle: 1\n-  UdpLowPayloadPattern: |\n-    UdpLowPayloadPattern\n-  UdpLowFrameLength: 1400\n-  UdpLowRxQueue: 3\n-  UdpLowTxQueue: 3\n-  UdpLowSocketPriority: 3\n-  UdpLowTxThreadPriority: 51\n-  UdpLowRxThreadPriority: 51\n-  UdpLowTxThreadCpu: 7\n-  UdpLowRxThreadCpu: 7\n-  UdpLowInterface: enp3s0\n-  UdpLowPort: 6667\n-  UdpLowDestination: 192.168.1.2\n-  UdpLowSource: 192.168.1.1\n-Log:\n-  LogThreadPriority: 1\n-  LogThreadCpu: 7\n-  LogFile: /var/log/reference_vid100.log\n-  LogLevel: Info\n-Debug:\n-  DebugStopTraceOnOutlier: false\n-  DebugStopTraceOnError: false\n-  DebugMonitorMode: false\n-  DebugMonitorDestination: 44:44:44:44:44:44\ndiff --git a/tests/working_clock_2/flow.sh b/tests/working_clock_2/flow.sh\ndeleted file mode 100755\nindex fb22259..0000000\n--- a/tests/working_clock_2/flow.sh\n+++ /dev/null\n@@ -1,74 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2023 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Setup the Tx and Rx traffic flows for Intel i225 for profinet scenario.\n-#\n-\n-set -e\n-\n-source ../lib/common.sh\n-source ../lib/igc.sh\n-\n-#\n-# Command line arguments.\n-#\n-INTERFACE=$1\n-CYCLETIME_NS=$2\n-BASETIME=$3\n-\n-[ -z $INTERFACE ] && INTERFACE=\"enp3s0\"        # default: enp3s0\n-[ -z $CYCLETIME_NS ] && CYCLETIME_NS=\"1000000\" # default: 1ms\n-[ -z $BASETIME ] && BASETIME=0                 # default: next cycle\n-\n-load_kernel_modules\n-\n-setup_threaded_napi \"${INTERFACE}\"\n-\n-igc_start \"${INTERFACE}\"\n-\n-#\n-# Split traffic between TSN streams, real time and everything else.\n-#\n-ENTRY1_NS=\"50000\"  # TSN High\n-ENTRY2_NS=\"50000\"  # TSN Low\n-ENTRY3_NS=\"100000\" # RTC\n-ENTRY4_NS=\"800000\" # RTA and everything else\n-\n-#\n-# Tx Assignment with Qbv and full hardware offload.\n-#\n-# PCP 6   - Tx Q 0 - TSN High\n-# PCP 5   - Tx Q 1 - TSN Low\n-# PCP 4   - Tx Q 2 - RTC\n-# PCP 3/X - Tx Q 3 - RTA and Everything else\n-#\n-tc qdisc replace dev ${INTERFACE} handle 100 parent root taprio num_tc 4 \\\n-  map 3 3 3 3 3 2 1 0 3 3 3 3 3 3 3 3 \\\n-  queues 1@0 1@1 1@2 1@3 \\\n-  base-time ${BASETIME} \\\n-  sched-entry S 0x01 ${ENTRY1_NS} \\\n-  sched-entry S 0x02 ${ENTRY2_NS} \\\n-  sched-entry S 0x04 ${ENTRY3_NS} \\\n-  sched-entry S 0x08 ${ENTRY4_NS} \\\n-  flags 0x02\n-\n-#\n-# Rx Queues Assignment.\n-#\n-# Rx Q 3 - All Traffic\n-# Rx Q 2 - RTC\n-# Rx Q 1 - TSN Low\n-# Rx Q 0 - TSN High\n-#\n-RXQUEUES=(3 0 1 2 3 3 3 3 3 3)\n-igc_rx_queues_assign \"${INTERFACE}\" RXQUEUES\n-\n-igc_end \"${INTERFACE}\"\n-\n-setup_irqs \"${INTERFACE}\"\n-\n-exit 0\ndiff --git a/tests/working_clock_2/mirror.sh b/tests/working_clock_2/mirror.sh\ndeleted file mode 100755\nindex 546d4d1..0000000\n--- a/tests/working_clock_2/mirror.sh\n+++ /dev/null\n@@ -1,25 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2023 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-\n-set -e\n-\n-cd \"$(dirname \"$0\")\"\n-\n-# Start PTP\n-./ptp.sh enp3s0\n-sleep 30\n-\n-# Configure flow\n-./flow.sh enp3s0\n-sleep 30\n-\n-# Start one instance of mirror application\n-cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n-\n-exit 0\ndiff --git a/tests/working_clock_2/mirror.yaml b/tests/working_clock_2/mirror.yaml\ndeleted file mode 100644\nindex dfa15ac..0000000\n--- a/tests/working_clock_2/mirror.yaml\n+++ /dev/null\n@@ -1,185 +0,0 @@\n----\n-#\n-# Copyright (C) 2023 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Sample reference YAML configuration file for Intel Comet Lake platforms with\n-# i225 NIC(s).\n-#\n-Application:\n-  ApplicationClockId: CLOCK_AUX0\n-  # ApplicationBaseStartTimeNS: 0\n-  ApplicationBaseCycleTimeNS: 1ms\n-  ApplicationTxBaseOffsetNS: 800us\n-  ApplicationRxBaseOffsetNS: 300us\n-  ApplicationXdpProgram: xdp_kern_profinet_vid100.o\n-TSNHigh:\n-  TsnHighEnabled: true\n-  TsnHighXdpEnabled: true\n-  TsnHighXdpSkbMode: false\n-  TsnHighXdpZcMode: true\n-  TsnHighXdpWakeupMode: true\n-  TsnHighTxTimeEnabled: false\n-  TsnHighTxTimeOffsetNS: 0\n-  TsnHighTxTimeStampEnabled: false\n-  TsnHighVid: 100\n-  TsnHighNumFramesPerCycle: 1\n-  TsnHighPayloadPattern: |\n-    TsnHighPayloadPattern\n-  TsnHighFrameLength: 128\n-  TsnHighRxQueue: 0\n-  TsnHighTxQueue: 0\n-  TsnHighSocketPriority: 7\n-  TsnHighTxThreadPriority: 98\n-  TsnHighRxThreadPriority: 98\n-  TsnHighTxThreadCpu: 0\n-  TsnHighRxThreadCpu: 0\n-  TsnHighInterface: enp3s0\n-  TsnHighDestination: a8:74:1d:9d:36:14\n-TSNLow:\n-  TsnLowEnabled: true\n-  TsnLowXdpEnabled: true\n-  TsnLowXdpSkbMode: false\n-  TsnLowXdpZcMode: true\n-  TsnLowXdpWakeupMode: true\n-  TsnLowTxTimeEnabled: false\n-  TsnLowTxTimeOffsetNS: 0\n-  TsnLowTxTimeStampEnabled: false\n-  TsnLowVid: 100\n-  TsnLowNumFramesPerCycle: 1\n-  TsnLowPayloadPattern: |\n-    TsnLowPayloadPattern\n-  TsnLowFrameLength: 128\n-  TsnLowRxQueue: 1\n-  TsnLowTxQueue: 1\n-  TsnLowSocketPriority: 6\n-  TsnLowTxThreadPriority: 98\n-  TsnLowRxThreadPriority: 98\n-  TsnLowTxThreadCpu: 1\n-  TsnLowRxThreadCpu: 1\n-  TsnLowInterface: enp3s0\n-  TsnLowDestination: a8:74:1d:9d:36:14\n-RTC:\n-  RtcEnabled: true\n-  RtcXdpEnabled: true\n-  RtcXdpSkbMode: false\n-  RtcXdpZcMode: true\n-  RtcXdpWakeupMode: true\n-  RtcTxTimeStampEnabled: false\n-  RtcVid: 100\n-  RtcNumFramesPerCycle: 1\n-  RtcPayloadPattern: |\n-    RtcPayloadPattern\n-  RtcFrameLength: 128\n-  RtcRxQueue: 2\n-  RtcTxQueue: 2\n-  RtcSocketPriority: 5\n-  RtcTxThreadPriority: 98\n-  RtcRxThreadPriority: 98\n-  RtcTxThreadCpu: 2\n-  RtcRxThreadCpu: 2\n-  RtcInterface: enp3s0\n-  RtcDestination: a8:74:1d:9d:36:14\n-RTA:\n-  RtaEnabled: true\n-  RtaXdpEnabled: true\n-  RtaXdpSkbMode: false\n-  RtaXdpZcMode: false\n-  RtaXdpWakeupMode: true\n-  RtaTxTimeStampEnabled: false\n-  RtaVid: 100\n-  RtaBurstPeriodNS: 200ms\n-  RtaNumFramesPerCycle: 1\n-  RtaPayloadPattern: |\n-    RtaPayloadPattern\n-  RtaFrameLength: 200\n-  RtaRxQueue: 3\n-  RtaTxQueue: 3\n-  RtaSocketPriority: 4\n-  RtaTxThreadPriority: 97\n-  RtaRxThreadPriority: 97\n-  RtaTxThreadCpu: 3\n-  RtaRxThreadCpu: 3\n-  RtaInterface: enp3s0\n-  RtaDestination: a8:74:1d:9d:36:14\n-DCP:\n-  DcpEnabled: true\n-  DcpVid: 100\n-  DcpBurstPeriodNS: 2s\n-  DcpNumFramesPerCycle: 1\n-  DcpPayloadPattern: |\n-    DcpPayloadPattern\n-  DcpFrameLength: 200\n-  DcpRxQueue: 3\n-  DcpTxQueue: 3\n-  DcpSocketPriority: 3\n-  DcpTxThreadPriority: 53\n-  DcpRxThreadPriority: 53\n-  DcpTxThreadCpu: 4\n-  DcpRxThreadCpu: 4\n-  DcpInterface: enp3s0\n-  DcpDestination: a8:74:1d:9d:36:14\n-LLDP:\n-  LldpEnabled: true\n-  LldpBurstPeriodNS: 5s\n-  LldpNumFramesPerCycle: 1\n-  LldpPayloadPattern: |\n-    LldpPayloadPattern\n-  LldpFrameLength: 200\n-  LldpRxQueue: 3\n-  LldpTxQueue: 3\n-  LldpSocketPriority: 3\n-  LldpTxThreadPriority: 52\n-  LldpRxThreadPriority: 52\n-  LldpTxThreadCpu: 5\n-  LldpRxThreadCpu: 5\n-  LldpInterface: enp3s0\n-  LldpDestination: 01:80:c2:00:00:0e\n-UDPHigh:\n-  UdpHighEnabled: true\n-  UdpHighBurstPeriodNS: 1s\n-  UdpHighNumFramesPerCycle: 1\n-  UdpHighPayloadPattern: |\n-    UdpHighPayloadPattern\n-  UdpHighFrameLength: 1400\n-  UdpHighRxQueue: 3\n-  UdpHighTxQueue: 3\n-  UdpHighSocketPriority: 3\n-  UdpHighTxThreadPriority: 51\n-  UdpHighRxThreadPriority: 51\n-  UdpHighTxThreadCpu: 6\n-  UdpHighRxThreadCpu: 6\n-  UdpHighInterface: enp3s0\n-  UdpHighPort: 6666\n-  UdpHighDestination: 192.168.1.1\n-  UdpHighSource: 192.168.1.2\n-UDPLow:\n-  UdpLowEnabled: true\n-  UdpLowBurstPeriodNS: 1s\n-  UdpLowNumFramesPerCycle: 1\n-  UdpLowPayloadPattern: |\n-    UdpLowPayloadPattern\n-  UdpLowFrameLength: 1400\n-  UdpLowRxQueue: 3\n-  UdpLowTxQueue: 3\n-  UdpLowSocketPriority: 3\n-  UdpLowTxThreadPriority: 51\n-  UdpLowRxThreadPriority: 51\n-  UdpLowTxThreadCpu: 7\n-  UdpLowRxThreadCpu: 7\n-  UdpLowInterface: enp3s0\n-  UdpLowPort: 6667\n-  UdpLowDestination: 192.168.1.1\n-  UdpLowSource: 192.168.1.2\n-Log:\n-  LogThreadPriority: 1\n-  LogThreadCpu: 7\n-  LogFile: /var/log/mirror_vid100.log\n-  LogLevel: Info\n-Debug:\n-  DebugStopTraceOnOutlier: false\n-  DebugStopTraceOnError: false\n-  DebugMonitorMode: false\n-  DebugMonitorDestination: 44:44:44:44:44:44\ndiff --git a/tests/working_clock_2/ptp.sh b/tests/working_clock_2/ptp.sh\ndeleted file mode 100755\nindex 492ade8..0000000\n--- a/tests/working_clock_2/ptp.sh\n+++ /dev/null\n@@ -1,92 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2021-2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# This testcase has two time domains:\n-#\n-#   - Global time:\n-#       Synchronized by gPTP domain A. Corresponds to CLOCK_REALTIME and CLOCK_TAI in Linux.\n-#\n-#   - Working clock:\n-#       High precision clock synchronized by gPTP domain B used for real time scheduling.\n-#       Corresponds to CLOCK_AUX0 in Linux.\n-#\n-# The point is CLOCK_AUX0 is an arbitrary time domain (e.g. starting at zero), whereas\n-# CLOCK_REALTIME points to valid UTC time. Both time domains are independent of each other.\n-#\n-# Note 1: This test case requires an update-to-date Linux kernel >= 7.2 for clock_gettime(2) and\n-#         clock_nanosleep(2) support on CLOCK_AUX*.\n-#\n-# Note 2: This example does not use CMLDS.\n-#\n-# Note 3: This example does not align the Testbench threads with the physical free running clock,\n-#         which is used for Qbv in i226. One idea would be to apply the required offset adjustments\n-#         to the TAPRIO schedules.\n-#\n-\n-set -e\n-\n-cd \"$(dirname \"$0\")\"\n-\n-# Interface\n-INTERFACE=$1\n-[ -z $INTERFACE ] && INTERFACE=\"eth0\"\n-\n-# Kill already running daemons\n-pkill ptp4l || true\n-pkill phc2sys || true\n-\n-# Stop ntpd\n-systemctl stop systemd-timesyncd || true\n-systemctl stop ntpd || true\n-systemctl stop chrony || true\n-\n-# Set NTP time into PHC\n-phc_ctl ${INTERFACE} set\n-\n-# Create two vClocks on top of physical PHC\n-PHC_PHYSICAL=$(ls -1 /sys/class/net/${INTERFACE}/device/ptp)\n-echo 2 >/sys/class/net/${INTERFACE}/device/ptp/${PHC_PHYSICAL}/n_vclocks\n-\n-# Create two ptp4l configurations for two domains\n-cp /etc/gPTP.cfg /etc/gPTP-domain0.cfg\n-cp /etc/gPTP.cfg /etc/gPTP-domain1.cfg\n-\n-echo 'domainNumber\t0' >>/etc/gPTP-domain0.cfg\n-echo 'domainNumber\t1' >>/etc/gPTP-domain1.cfg\n-\n-PHCINDEX_VCLOCK1=$(ls -1 /sys/class/net/${INTERFACE}/device/ptp/${PHC_PHYSICAL} | grep ptp | head -n1 | sed -e 's/ptp//g')\n-PHCINDEX_VCLOCK2=$(ls -1 /sys/class/net/${INTERFACE}/device/ptp/${PHC_PHYSICAL} | grep ptp | tail -n1 | sed -e 's/ptp//g')\n-\n-# Checkout and build phc2sys capable handling CLOCK_AUX0\n-if ! [ -d linuxptp ]; then\n-  git clone https://github.com/richardcochran/linuxptp\n-  pushd linuxptp\n-  make -j$(nproc)\n-  popd\n-fi\n-\n-# Start global time ptp4l instance with 802.1AS-2011 endstation profile\n-phc_ctl /dev/ptp${PHCINDEX_VCLOCK1} set\n-linuxptp/ptp4l -2 -H -i ${INTERFACE} --phc_index ${PHCINDEX_VCLOCK1} --socket_priority=4 --tx_timestamp_timeout=40 -f /etc/gPTP-domain0.cfg &\n-\n-# Start working clock ptp4l instance with 802.1AS-2011 endstation profile\n-phc_ctl /dev/ptp${PHCINDEX_VCLOCK2} set 0\n-linuxptp/ptp4l -2 -H -i ${INTERFACE} --phc_index ${PHCINDEX_VCLOCK2} --socket_priority=4 --tx_timestamp_timeout=40 -f /etc/gPTP-domain1.cfg &\n-\n-# Wait for ptp4l instances\n-sleep 30\n-\n-# Enable CLOCK_AUX0 in kernel timekeeping first\n-echo 1 >/sys/kernel/time/aux_clocks/0/aux_clock_enable\n-\n-# Synchronize CLOCK_REALTIME to global time\n-linuxptp/phc2sys -s /dev/ptp${PHCINDEX_VCLOCK1} -c CLOCK_REALTIME --step_threshold=1 --transportSpecific=1 -O37 &\n-\n-# Synchronize CLOCK_AUX0 to working clock\n-linuxptp/phc2sys -s /dev/ptp${PHCINDEX_VCLOCK2} -c CLOCK_AUX0 --step_threshold=1 --transportSpecific=1 -O0 &\n-\n-exit 0\ndiff --git a/tests/working_clock_2/ref.sh b/tests/working_clock_2/ref.sh\ndeleted file mode 100755\nindex ba03dc0..0000000\n--- a/tests/working_clock_2/ref.sh\n+++ /dev/null\n@@ -1,25 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2023 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-\n-set -e\n-\n-cd \"$(dirname \"$0\")\"\n-\n-# Start PTP\n-./ptp.sh enp3s0\n-sleep 30\n-\n-# Configure flow\n-./flow.sh enp3s0\n-sleep 30\n-\n-# Start one instance of reference application\n-cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n-\n-exit 0\ndiff --git a/tests/working_clock_2/reference.yaml b/tests/working_clock_2/reference.yaml\ndeleted file mode 100644\nindex 79b9d24..0000000\n--- a/tests/working_clock_2/reference.yaml\n+++ /dev/null\n@@ -1,185 +0,0 @@\n----\n-#\n-# Copyright (C) 2023 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Sample reference YAML configuration file for Intel Comet Lake platforms with\n-# i225 NIC(s).\n-#\n-Application:\n-  ApplicationClockId: CLOCK_AUX0\n-  # ApplicationBaseStartTimeNS: 0\n-  ApplicationBaseCycleTimeNS: 1ms\n-  ApplicationTxBaseOffsetNS: 800us\n-  ApplicationRxBaseOffsetNS: 300us\n-  ApplicationXdpProgram: xdp_kern_profinet_vid100.o\n-TSNHigh:\n-  TsnHighEnabled: true\n-  TsnHighXdpEnabled: true\n-  TsnHighXdpSkbMode: false\n-  TsnHighXdpZcMode: true\n-  TsnHighXdpWakeupMode: true\n-  TsnHighTxTimeEnabled: false\n-  TsnHighTxTimeOffsetNS: 0\n-  TsnHighTxTimeStampEnabled: false\n-  TsnHighVid: 100\n-  TsnHighNumFramesPerCycle: 1\n-  TsnHighPayloadPattern: |\n-    TsnHighPayloadPattern\n-  TsnHighFrameLength: 128\n-  TsnHighRxQueue: 0\n-  TsnHighTxQueue: 0\n-  TsnHighSocketPriority: 7\n-  TsnHighTxThreadPriority: 98\n-  TsnHighRxThreadPriority: 98\n-  TsnHighTxThreadCpu: 0\n-  TsnHighRxThreadCpu: 0\n-  TsnHighInterface: enp3s0\n-  TsnHighDestination: a8:74:1d:9d:98:d8\n-TSNLow:\n-  TsnLowEnabled: true\n-  TsnLowXdpEnabled: true\n-  TsnLowXdpSkbMode: false\n-  TsnLowXdpZcMode: true\n-  TsnLowXdpWakeupMode: true\n-  TsnLowTxTimeEnabled: false\n-  TsnLowTxTimeOffsetNS: 0\n-  TsnLowTxTimeStampEnabled: false\n-  TsnLowVid: 100\n-  TsnLowNumFramesPerCycle: 1\n-  TsnLowPayloadPattern: |\n-    TsnLowPayloadPattern\n-  TsnLowFrameLength: 128\n-  TsnLowRxQueue: 1\n-  TsnLowTxQueue: 1\n-  TsnLowSocketPriority: 6\n-  TsnLowTxThreadPriority: 98\n-  TsnLowRxThreadPriority: 98\n-  TsnLowTxThreadCpu: 1\n-  TsnLowRxThreadCpu: 1\n-  TsnLowInterface: enp3s0\n-  TsnLowDestination: a8:74:1d:9d:98:d8\n-RTC:\n-  RtcEnabled: true\n-  RtcXdpEnabled: true\n-  RtcXdpSkbMode: false\n-  RtcXdpZcMode: true\n-  RtcXdpWakeupMode: true\n-  RtcTxTimeStampEnabled: false\n-  RtcVid: 100\n-  RtcNumFramesPerCycle: 1\n-  RtcPayloadPattern: |\n-    RtcPayloadPattern\n-  RtcFrameLength: 128\n-  RtcRxQueue: 2\n-  RtcTxQueue: 2\n-  RtcSocketPriority: 5\n-  RtcTxThreadPriority: 98\n-  RtcRxThreadPriority: 98\n-  RtcTxThreadCpu: 2\n-  RtcRxThreadCpu: 2\n-  RtcInterface: enp3s0\n-  RtcDestination: a8:74:1d:9d:98:d8\n-RTA:\n-  RtaEnabled: true\n-  RtaXdpEnabled: true\n-  RtaXdpSkbMode: false\n-  RtaXdpZcMode: false\n-  RtaXdpWakeupMode: true\n-  RtaTxTimeStampEnabled: false\n-  RtaVid: 100\n-  RtaBurstPeriodNS: 200ms\n-  RtaNumFramesPerCycle: 1\n-  RtaPayloadPattern: |\n-    RtaPayloadPattern\n-  RtaFrameLength: 200\n-  RtaRxQueue: 3\n-  RtaTxQueue: 3\n-  RtaSocketPriority: 4\n-  RtaTxThreadPriority: 97\n-  RtaRxThreadPriority: 97\n-  RtaTxThreadCpu: 3\n-  RtaRxThreadCpu: 3\n-  RtaInterface: enp3s0\n-  RtaDestination: a8:74:1d:9d:98:d8\n-DCP:\n-  DcpEnabled: true\n-  DcpVid: 100\n-  DcpBurstPeriodNS: 2s\n-  DcpNumFramesPerCycle: 1\n-  DcpPayloadPattern: |\n-    DcpPayloadPattern\n-  DcpFrameLength: 200\n-  DcpRxQueue: 3\n-  DcpTxQueue: 3\n-  DcpSocketPriority: 3\n-  DcpTxThreadPriority: 53\n-  DcpRxThreadPriority: 53\n-  DcpTxThreadCpu: 4\n-  DcpRxThreadCpu: 4\n-  DcpInterface: enp3s0\n-  DcpDestination: 01:0e:cf:00:00:00\n-LLDP:\n-  LldpEnabled: true\n-  LldpBurstPeriodNS: 5s\n-  LldpNumFramesPerCycle: 1\n-  LldpPayloadPattern: |\n-    LldpPayloadPattern\n-  LldpFrameLength: 200\n-  LldpRxQueue: 3\n-  LldpTxQueue: 3\n-  LldpSocketPriority: 3\n-  LldpTxThreadPriority: 52\n-  LldpRxThreadPriority: 52\n-  LldpTxThreadCpu: 5\n-  LldpRxThreadCpu: 5\n-  LldpInterface: enp3s0\n-  LldpDestination: 01:80:c2:00:00:0e\n-UDPHigh:\n-  UdpHighEnabled: true\n-  UdpHighBurstPeriodNS: 1s\n-  UdpHighNumFramesPerCycle: 1\n-  UdpHighPayloadPattern: |\n-    UdpHighPayloadPattern\n-  UdpHighFrameLength: 1400\n-  UdpHighRxQueue: 3\n-  UdpHighTxQueue: 3\n-  UdpHighSocketPriority: 3\n-  UdpHighTxThreadPriority: 51\n-  UdpHighRxThreadPriority: 51\n-  UdpHighTxThreadCpu: 6\n-  UdpHighRxThreadCpu: 6\n-  UdpHighInterface: enp3s0\n-  UdpHighPort: 6666\n-  UdpHighDestination: 192.168.1.2\n-  UdpHighSource: 192.168.1.1\n-UDPLow:\n-  UdpLowEnabled: true\n-  UdpLowBurstPeriodNS: 1s\n-  UdpLowNumFramesPerCycle: 1\n-  UdpLowPayloadPattern: |\n-    UdpLowPayloadPattern\n-  UdpLowFrameLength: 1400\n-  UdpLowRxQueue: 3\n-  UdpLowTxQueue: 3\n-  UdpLowSocketPriority: 3\n-  UdpLowTxThreadPriority: 51\n-  UdpLowRxThreadPriority: 51\n-  UdpLowTxThreadCpu: 7\n-  UdpLowRxThreadCpu: 7\n-  UdpLowInterface: enp3s0\n-  UdpLowPort: 6667\n-  UdpLowDestination: 192.168.1.2\n-  UdpLowSource: 192.168.1.1\n-Log:\n-  LogThreadPriority: 1\n-  LogThreadCpu: 7\n-  LogFile: /var/log/reference_vid100.log\n-  LogLevel: Info\n-Debug:\n-  DebugStopTraceOnOutlier: false\n-  DebugStopTraceOnError: false\n-  DebugMonitorMode: false\n-  DebugMonitorDestination: 44:44:44:44:44:44\ndiff --git a/tests/working_clock_3/flow.sh b/tests/working_clock_3/flow.sh\ndeleted file mode 100755\nindex bd194a0..0000000\n--- a/tests/working_clock_3/flow.sh\n+++ /dev/null\n@@ -1,74 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Setup the Tx and Rx traffic flows for Intel i225 for profinet scenario.\n-#\n-\n-set -e\n-\n-source ../lib/common.sh\n-source ../lib/igc.sh\n-\n-#\n-# Command line arguments.\n-#\n-INTERFACE=$1\n-CYCLETIME_NS=$2\n-BASETIME=$3\n-\n-[ -z $INTERFACE ] && INTERFACE=\"enp3s0\"        # default: enp3s0\n-[ -z $CYCLETIME_NS ] && CYCLETIME_NS=\"1000000\" # default: 1ms\n-[ -z $BASETIME ] && BASETIME=0                 # default: next cycle\n-\n-load_kernel_modules\n-\n-setup_threaded_napi \"${INTERFACE}\"\n-\n-igc_start \"${INTERFACE}\"\n-\n-#\n-# Split traffic between TSN streams, real time and everything else.\n-#\n-ENTRY1_NS=\"50000\"  # TSN High\n-ENTRY2_NS=\"50000\"  # TSN Low\n-ENTRY3_NS=\"100000\" # RTC\n-ENTRY4_NS=\"800000\" # RTA and everything else\n-\n-#\n-# Tx Assignment with Qbv and full hardware offload.\n-#\n-# PCP 6   - Tx Q 0 - TSN High\n-# PCP 5   - Tx Q 1 - TSN Low\n-# PCP 4   - Tx Q 2 - RTC\n-# PCP 3/X - Tx Q 3 - RTA and Everything else\n-#\n-tc qdisc replace dev ${INTERFACE} handle 100 parent root taprio num_tc 4 \\\n-  map 3 3 3 3 3 2 1 0 3 3 3 3 3 3 3 3 \\\n-  queues 1@0 1@1 1@2 1@3 \\\n-  base-time ${BASETIME} \\\n-  sched-entry S 0x01 ${ENTRY1_NS} \\\n-  sched-entry S 0x02 ${ENTRY2_NS} \\\n-  sched-entry S 0x04 ${ENTRY3_NS} \\\n-  sched-entry S 0x08 ${ENTRY4_NS} \\\n-  flags 0x02\n-\n-#\n-# Rx Queues Assignment.\n-#\n-# Rx Q 3 - All Traffic\n-# Rx Q 2 - RTC\n-# Rx Q 1 - TSN Low\n-# Rx Q 0 - TSN High\n-#\n-RXQUEUES=(3 0 1 2 3 3 3 3 3 3)\n-igc_rx_queues_assign \"${INTERFACE}\" RXQUEUES\n-\n-igc_end \"${INTERFACE}\"\n-\n-setup_irqs \"${INTERFACE}\"\n-\n-exit 0\ndiff --git a/tests/working_clock_3/mirror.sh b/tests/working_clock_3/mirror.sh\ndeleted file mode 100755\nindex 058c93d..0000000\n--- a/tests/working_clock_3/mirror.sh\n+++ /dev/null\n@@ -1,25 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-\n-set -e\n-\n-cd \"$(dirname \"$0\")\"\n-\n-# Start PTP\n-./ptp.sh enp3s0\n-sleep 30\n-\n-# Configure flow\n-./flow.sh enp3s0\n-sleep 30\n-\n-# Start one instance of mirror application\n-cp ../../build/xdp_kern_*.o .\n-../../build/mirror -c mirror.yaml >mirror.log &\n-\n-exit 0\ndiff --git a/tests/working_clock_3/mirror.yaml b/tests/working_clock_3/mirror.yaml\ndeleted file mode 100644\nindex 37e39ca..0000000\n--- a/tests/working_clock_3/mirror.yaml\n+++ /dev/null\n@@ -1,185 +0,0 @@\n----\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Sample reference YAML configuration file for Intel Comet Lake platforms with\n-# i225 NIC(s).\n-#\n-Application:\n-  ApplicationClockId: CLOCK_AUX0\n-  # ApplicationBaseStartTimeNS: 0\n-  ApplicationBaseCycleTimeNS: 1ms\n-  ApplicationTxBaseOffsetNS: 800us\n-  ApplicationRxBaseOffsetNS: 300us\n-  ApplicationXdpProgram: xdp_kern_profinet_vid100.o\n-TSNHigh:\n-  TsnHighEnabled: true\n-  TsnHighXdpEnabled: true\n-  TsnHighXdpSkbMode: false\n-  TsnHighXdpZcMode: true\n-  TsnHighXdpWakeupMode: true\n-  TsnHighTxTimeEnabled: false\n-  TsnHighTxTimeOffsetNS: 0\n-  TsnHighTxTimeStampEnabled: false\n-  TsnHighVid: 100\n-  TsnHighNumFramesPerCycle: 1\n-  TsnHighPayloadPattern: |\n-    TsnHighPayloadPattern\n-  TsnHighFrameLength: 128\n-  TsnHighRxQueue: 0\n-  TsnHighTxQueue: 0\n-  TsnHighSocketPriority: 7\n-  TsnHighTxThreadPriority: 98\n-  TsnHighRxThreadPriority: 98\n-  TsnHighTxThreadCpu: 0\n-  TsnHighRxThreadCpu: 0\n-  TsnHighInterface: enp3s0\n-  TsnHighDestination: a8:74:1d:9d:36:14\n-TSNLow:\n-  TsnLowEnabled: true\n-  TsnLowXdpEnabled: true\n-  TsnLowXdpSkbMode: false\n-  TsnLowXdpZcMode: true\n-  TsnLowXdpWakeupMode: true\n-  TsnLowTxTimeEnabled: false\n-  TsnLowTxTimeOffsetNS: 0\n-  TsnLowTxTimeStampEnabled: false\n-  TsnLowVid: 100\n-  TsnLowNumFramesPerCycle: 1\n-  TsnLowPayloadPattern: |\n-    TsnLowPayloadPattern\n-  TsnLowFrameLength: 128\n-  TsnLowRxQueue: 1\n-  TsnLowTxQueue: 1\n-  TsnLowSocketPriority: 6\n-  TsnLowTxThreadPriority: 98\n-  TsnLowRxThreadPriority: 98\n-  TsnLowTxThreadCpu: 1\n-  TsnLowRxThreadCpu: 1\n-  TsnLowInterface: enp3s0\n-  TsnLowDestination: a8:74:1d:9d:36:14\n-RTC:\n-  RtcEnabled: true\n-  RtcXdpEnabled: true\n-  RtcXdpSkbMode: false\n-  RtcXdpZcMode: true\n-  RtcXdpWakeupMode: true\n-  RtcTxTimeStampEnabled: false\n-  RtcVid: 100\n-  RtcNumFramesPerCycle: 1\n-  RtcPayloadPattern: |\n-    RtcPayloadPattern\n-  RtcFrameLength: 128\n-  RtcRxQueue: 2\n-  RtcTxQueue: 2\n-  RtcSocketPriority: 5\n-  RtcTxThreadPriority: 98\n-  RtcRxThreadPriority: 98\n-  RtcTxThreadCpu: 2\n-  RtcRxThreadCpu: 2\n-  RtcInterface: enp3s0\n-  RtcDestination: a8:74:1d:9d:36:14\n-RTA:\n-  RtaEnabled: true\n-  RtaXdpEnabled: true\n-  RtaXdpSkbMode: false\n-  RtaXdpZcMode: false\n-  RtaXdpWakeupMode: true\n-  RtaTxTimeStampEnabled: false\n-  RtaVid: 100\n-  RtaBurstPeriodNS: 200ms\n-  RtaNumFramesPerCycle: 1\n-  RtaPayloadPattern: |\n-    RtaPayloadPattern\n-  RtaFrameLength: 200\n-  RtaRxQueue: 3\n-  RtaTxQueue: 3\n-  RtaSocketPriority: 4\n-  RtaTxThreadPriority: 97\n-  RtaRxThreadPriority: 97\n-  RtaTxThreadCpu: 3\n-  RtaRxThreadCpu: 3\n-  RtaInterface: enp3s0\n-  RtaDestination: a8:74:1d:9d:36:14\n-DCP:\n-  DcpEnabled: true\n-  DcpVid: 100\n-  DcpBurstPeriodNS: 2s\n-  DcpNumFramesPerCycle: 1\n-  DcpPayloadPattern: |\n-    DcpPayloadPattern\n-  DcpFrameLength: 200\n-  DcpRxQueue: 3\n-  DcpTxQueue: 3\n-  DcpSocketPriority: 3\n-  DcpTxThreadPriority: 53\n-  DcpRxThreadPriority: 53\n-  DcpTxThreadCpu: 4\n-  DcpRxThreadCpu: 4\n-  DcpInterface: enp3s0\n-  DcpDestination: a8:74:1d:9d:36:14\n-LLDP:\n-  LldpEnabled: true\n-  LldpBurstPeriodNS: 5s\n-  LldpNumFramesPerCycle: 1\n-  LldpPayloadPattern: |\n-    LldpPayloadPattern\n-  LldpFrameLength: 200\n-  LldpRxQueue: 3\n-  LldpTxQueue: 3\n-  LldpSocketPriority: 3\n-  LldpTxThreadPriority: 52\n-  LldpRxThreadPriority: 52\n-  LldpTxThreadCpu: 5\n-  LldpRxThreadCpu: 5\n-  LldpInterface: enp3s0\n-  LldpDestination: 01:80:c2:00:00:0e\n-UDPHigh:\n-  UdpHighEnabled: true\n-  UdpHighBurstPeriodNS: 1s\n-  UdpHighNumFramesPerCycle: 1\n-  UdpHighPayloadPattern: |\n-    UdpHighPayloadPattern\n-  UdpHighFrameLength: 1400\n-  UdpHighRxQueue: 3\n-  UdpHighTxQueue: 3\n-  UdpHighSocketPriority: 3\n-  UdpHighTxThreadPriority: 51\n-  UdpHighRxThreadPriority: 51\n-  UdpHighTxThreadCpu: 6\n-  UdpHighRxThreadCpu: 6\n-  UdpHighInterface: enp3s0\n-  UdpHighPort: 6666\n-  UdpHighDestination: 192.168.1.1\n-  UdpHighSource: 192.168.1.2\n-UDPLow:\n-  UdpLowEnabled: true\n-  UdpLowBurstPeriodNS: 1s\n-  UdpLowNumFramesPerCycle: 1\n-  UdpLowPayloadPattern: |\n-    UdpLowPayloadPattern\n-  UdpLowFrameLength: 1400\n-  UdpLowRxQueue: 3\n-  UdpLowTxQueue: 3\n-  UdpLowSocketPriority: 3\n-  UdpLowTxThreadPriority: 51\n-  UdpLowRxThreadPriority: 51\n-  UdpLowTxThreadCpu: 7\n-  UdpLowRxThreadCpu: 7\n-  UdpLowInterface: enp3s0\n-  UdpLowPort: 6667\n-  UdpLowDestination: 192.168.1.1\n-  UdpLowSource: 192.168.1.2\n-Log:\n-  LogThreadPriority: 1\n-  LogThreadCpu: 7\n-  LogFile: /var/log/mirror_vid100.log\n-  LogLevel: Info\n-Debug:\n-  DebugStopTraceOnOutlier: false\n-  DebugStopTraceOnError: false\n-  DebugMonitorMode: false\n-  DebugMonitorDestination: 44:44:44:44:44:44\ndiff --git a/tests/working_clock_3/ptp.sh b/tests/working_clock_3/ptp.sh\ndeleted file mode 100755\nindex 47a9a31..0000000\n--- a/tests/working_clock_3/ptp.sh\n+++ /dev/null\n@@ -1,91 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2021-2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# This testcase has two time domains:\n-#\n-#   - Global time:\n-#       Synchronized by gPTP domain A. Corresponds to CLOCK_REALTIME and CLOCK_TAI in Linux.\n-#\n-#   - Working clock:\n-#       High precision clock synchronized by gPTP domain B used for real time scheduling.\n-#       Corresponds to CLOCK_AUX0 in Linux.\n-#\n-# The point is CLOCK_AUX0 is an arbitrary time domain (e.g. starting at zero), whereas\n-# CLOCK_REALTIME points to valid UTC time. Both time domains are independent of each other.\n-#\n-# Note 1: This test case requires an update-to-date Linux kernel >= 7.2 for clock_gettime(2) and\n-#         clock_nanosleep(2) support on CLOCK_AUX*.\n-#\n-# Note 2: This example does not use CMLDS.\n-#\n-# Note 3: Working Clock does not use vClock, but rather the physical clock. This allows EST to work.\n-#         This does only work, because igc implements .getcyclesx64 callback using hardware timer 1.\n-#\n-\n-set -e\n-\n-cd \"$(dirname \"$0\")\"\n-\n-# Interface\n-INTERFACE=$1\n-[ -z $INTERFACE ] && INTERFACE=\"eth0\"\n-\n-# Kill already running daemons\n-pkill ptp4l || true\n-pkill phc2sys || true\n-\n-# Stop ntpd\n-systemctl stop systemd-timesyncd || true\n-systemctl stop ntpd || true\n-systemctl stop chrony || true\n-\n-# Set NTP time into PHC\n-phc_ctl ${INTERFACE} set\n-\n-# Create two vClocks on top of physical PHC\n-PHC_PHYSICAL=$(ls -1 /sys/class/net/${INTERFACE}/device/ptp)\n-echo 2 >/sys/class/net/${INTERFACE}/device/ptp/${PHC_PHYSICAL}/n_vclocks\n-\n-# Create two ptp4l configurations for two domains\n-cp /etc/gPTP.cfg /etc/gPTP-domain0.cfg\n-cp /etc/gPTP.cfg /etc/gPTP-domain1.cfg\n-\n-echo 'domainNumber\t0' >>/etc/gPTP-domain0.cfg\n-echo 'domainNumber\t1' >>/etc/gPTP-domain1.cfg\n-\n-PHCINDEX_VCLOCK1=$(ls -1 /sys/class/net/${INTERFACE}/device/ptp/${PHC_PHYSICAL} | grep ptp | head -n1 | sed -e 's/ptp//g')\n-PHCINDEX_VCLOCK2=$(ls -1 /sys/class/net/${INTERFACE}/device/ptp/${PHC_PHYSICAL} | grep ptp | tail -n1 | sed -e 's/ptp//g')\n-\n-# Checkout and build phc2sys capable handling CLOCK_AUX0\n-if ! [ -d linuxptp ]; then\n-  git clone https://github.com/richardcochran/linuxptp\n-  pushd linuxptp\n-  make -j$(nproc)\n-  popd\n-fi\n-\n-# Start global time ptp4l instance with 802.1AS-2011 endstation profile\n-phc_ctl /dev/ptp${PHCINDEX_VCLOCK1} set\n-linuxptp/ptp4l -2 -H -i ${INTERFACE} --phc_index ${PHCINDEX_VCLOCK1} --socket_priority=4 --tx_timestamp_timeout=40 -f /etc/gPTP-domain0.cfg &\n-\n-# Start working clock ptp4l instance with 802.1AS-2011 endstation profile\n-phc_ctl /dev/${PHC_PHYSICAL} set 0\n-linuxptp/ptp4l -2 -H -i ${INTERFACE} --socket_priority=4 --tx_timestamp_timeout=40 -f /etc/gPTP-domain1.cfg &\n-\n-# Wait for ptp4l instances\n-sleep 30\n-\n-# Enable CLOCK_AUX0 in kernel timekeeping first\n-echo 1 >/sys/kernel/time/aux_clocks/0/aux_clock_enable\n-\n-# Synchronize CLOCK_REALTIME to global time\n-linuxptp/phc2sys -s /dev/ptp${PHCINDEX_VCLOCK1} -c CLOCK_REALTIME --step_threshold=1 --transportSpecific=1 -O37 &\n-\n-# Synchronize CLOCK_AUX0 to working clock\n-linuxptp/phc2sys -s /dev/${PHC_PHYSICAL} -c CLOCK_AUX0 --step_threshold=1 --transportSpecific=1 -O0 &\n-\n-exit 0\ndiff --git a/tests/working_clock_3/ref.sh b/tests/working_clock_3/ref.sh\ndeleted file mode 100755\nindex b5a7298..0000000\n--- a/tests/working_clock_3/ref.sh\n+++ /dev/null\n@@ -1,25 +0,0 @@\n-#!/bin/bash\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-\n-set -e\n-\n-cd \"$(dirname \"$0\")\"\n-\n-# Start PTP\n-./ptp.sh enp3s0\n-sleep 30\n-\n-# Configure flow\n-./flow.sh enp3s0\n-sleep 30\n-\n-# Start one instance of reference application\n-cp ../../build/xdp_kern_*.o .\n-../../build/reference -c reference.yaml >ref.log &\n-\n-exit 0\ndiff --git a/tests/working_clock_3/reference.yaml b/tests/working_clock_3/reference.yaml\ndeleted file mode 100644\nindex c3f8a36..0000000\n--- a/tests/working_clock_3/reference.yaml\n+++ /dev/null\n@@ -1,185 +0,0 @@\n----\n-#\n-# Copyright (C) 2026 Linutronix GmbH\n-# Author Kurt Kanzenbach <kurt@linutronix.de>\n-#\n-# SPDX-License-Identifier: BSD-2-Clause\n-#\n-# Sample reference YAML configuration file for Intel Comet Lake platforms with\n-# i225 NIC(s).\n-#\n-Application:\n-  ApplicationClockId: CLOCK_AUX0\n-  # ApplicationBaseStartTimeNS: 0\n-  ApplicationBaseCycleTimeNS: 1ms\n-  ApplicationTxBaseOffsetNS: 800us\n-  ApplicationRxBaseOffsetNS: 300us\n-  ApplicationXdpProgram: xdp_kern_profinet_vid100.o\n-TSNHigh:\n-  TsnHighEnabled: true\n-  TsnHighXdpEnabled: true\n-  TsnHighXdpSkbMode: false\n-  TsnHighXdpZcMode: true\n-  TsnHighXdpWakeupMode: true\n-  TsnHighTxTimeEnabled: false\n-  TsnHighTxTimeOffsetNS: 0\n-  TsnHighTxTimeStampEnabled: false\n-  TsnHighVid: 100\n-  TsnHighNumFramesPerCycle: 1\n-  TsnHighPayloadPattern: |\n-    TsnHighPayloadPattern\n-  TsnHighFrameLength: 128\n-  TsnHighRxQueue: 0\n-  TsnHighTxQueue: 0\n-  TsnHighSocketPriority: 7\n-  TsnHighTxThreadPriority: 98\n-  TsnHighRxThreadPriority: 98\n-  TsnHighTxThreadCpu: 0\n-  TsnHighRxThreadCpu: 0\n-  TsnHighInterface: enp3s0\n-  TsnHighDestination: a8:74:1d:9d:98:d8\n-TSNLow:\n-  TsnLowEnabled: true\n-  TsnLowXdpEnabled: true\n-  TsnLowXdpSkbMode: false\n-  TsnLowXdpZcMode: true\n-  TsnLowXdpWakeupMode: true\n-  TsnLowTxTimeEnabled: false\n-  TsnLowTxTimeOffsetNS: 0\n-  TsnLowTxTimeStampEnabled: false\n-  TsnLowVid: 100\n-  TsnLowNumFramesPerCycle: 1\n-  TsnLowPayloadPattern: |\n-    TsnLowPayloadPattern\n-  TsnLowFrameLength: 128\n-  TsnLowRxQueue: 1\n-  TsnLowTxQueue: 1\n-  TsnLowSocketPriority: 6\n-  TsnLowTxThreadPriority: 98\n-  TsnLowRxThreadPriority: 98\n-  TsnLowTxThreadCpu: 1\n-  TsnLowRxThreadCpu: 1\n-  TsnLowInterface: enp3s0\n-  TsnLowDestination: a8:74:1d:9d:98:d8\n-RTC:\n-  RtcEnabled: true\n-  RtcXdpEnabled: true\n-  RtcXdpSkbMode: false\n-  RtcXdpZcMode: true\n-  RtcXdpWakeupMode: true\n-  RtcTxTimeStampEnabled: false\n-  RtcVid: 100\n-  RtcNumFramesPerCycle: 1\n-  RtcPayloadPattern: |\n-    RtcPayloadPattern\n-  RtcFrameLength: 128\n-  RtcRxQueue: 2\n-  RtcTxQueue: 2\n-  RtcSocketPriority: 5\n-  RtcTxThreadPriority: 98\n-  RtcRxThreadPriority: 98\n-  RtcTxThreadCpu: 2\n-  RtcRxThreadCpu: 2\n-  RtcInterface: enp3s0\n-  RtcDestination: a8:74:1d:9d:98:d8\n-RTA:\n-  RtaEnabled: true\n-  RtaXdpEnabled: true\n-  RtaXdpSkbMode: false\n-  RtaXdpZcMode: false\n-  RtaXdpWakeupMode: true\n-  RtaTxTimeStampEnabled: false\n-  RtaVid: 100\n-  RtaBurstPeriodNS: 200ms\n-  RtaNumFramesPerCycle: 1\n-  RtaPayloadPattern: |\n-    RtaPayloadPattern\n-  RtaFrameLength: 200\n-  RtaRxQueue: 3\n-  RtaTxQueue: 3\n-  RtaSocketPriority: 4\n-  RtaTxThreadPriority: 97\n-  RtaRxThreadPriority: 97\n-  RtaTxThreadCpu: 3\n-  RtaRxThreadCpu: 3\n-  RtaInterface: enp3s0\n-  RtaDestination: a8:74:1d:9d:98:d8\n-DCP:\n-  DcpEnabled: true\n-  DcpVid: 100\n-  DcpBurstPeriodNS: 2s\n-  DcpNumFramesPerCycle: 1\n-  DcpPayloadPattern: |\n-    DcpPayloadPattern\n-  DcpFrameLength: 200\n-  DcpRxQueue: 3\n-  DcpTxQueue: 3\n-  DcpSocketPriority: 3\n-  DcpTxThreadPriority: 53\n-  DcpRxThreadPriority: 53\n-  DcpTxThreadCpu: 4\n-  DcpRxThreadCpu: 4\n-  DcpInterface: enp3s0\n-  DcpDestination: 01:0e:cf:00:00:00\n-LLDP:\n-  LldpEnabled: true\n-  LldpBurstPeriodNS: 5s\n-  LldpNumFramesPerCycle: 1\n-  LldpPayloadPattern: |\n-    LldpPayloadPattern\n-  LldpFrameLength: 200\n-  LldpRxQueue: 3\n-  LldpTxQueue: 3\n-  LldpSocketPriority: 3\n-  LldpTxThreadPriority: 52\n-  LldpRxThreadPriority: 52\n-  LldpTxThreadCpu: 5\n-  LldpRxThreadCpu: 5\n-  LldpInterface: enp3s0\n-  LldpDestination: 01:80:c2:00:00:0e\n-UDPHigh:\n-  UdpHighEnabled: true\n-  UdpHighBurstPeriodNS: 1s\n-  UdpHighNumFramesPerCycle: 1\n-  UdpHighPayloadPattern: |\n-    UdpHighPayloadPattern\n-  UdpHighFrameLength: 1400\n-  UdpHighRxQueue: 3\n-  UdpHighTxQueue: 3\n-  UdpHighSocketPriority: 3\n-  UdpHighTxThreadPriority: 51\n-  UdpHighRxThreadPriority: 51\n-  UdpHighTxThreadCpu: 6\n-  UdpHighRxThreadCpu: 6\n-  UdpHighInterface: enp3s0\n-  UdpHighPort: 6666\n-  UdpHighDestination: 192.168.1.2\n-  UdpHighSource: 192.168.1.1\n-UDPLow:\n-  UdpLowEnabled: true\n-  UdpLowBurstPeriodNS: 1s\n-  UdpLowNumFramesPerCycle: 1\n-  UdpLowPayloadPattern: |\n-    UdpLowPayloadPattern\n-  UdpLowFrameLength: 1400\n-  UdpLowRxQueue: 3\n-  UdpLowTxQueue: 3\n-  UdpLowSocketPriority: 3\n-  UdpLowTxThreadPriority: 51\n-  UdpLowRxThreadPriority: 51\n-  UdpLowTxThreadCpu: 7\n-  UdpLowRxThreadCpu: 7\n-  UdpLowInterface: enp3s0\n-  UdpLowPort: 6667\n-  UdpLowDestination: 192.168.1.2\n-  UdpLowSource: 192.168.1.1\n-Log:\n-  LogThreadPriority: 1\n-  LogThreadCpu: 7\n-  LogFile: /var/log/reference_vid100.log\n-  LogLevel: Info\n-Debug:\n-  DebugStopTraceOnOutlier: false\n-  DebugStopTraceOnError: false\n-  DebugMonitorMode: false\n-  DebugMonitorDestination: 44:44:44:44:44:44\ndiff --git a/tests/workloads/CMakeLists.txt b/tests/workloads/CMakeLists.txt\nindex 16854de..cf5fd78 100644\n--- a/tests/workloads/CMakeLists.txt\n+++ b/tests/workloads/CMakeLists.txt\n@@ -56,8 +56,8 @@ file(GLOB WORKLOAD_DIRS RELATIVE \"${CMAKE_CURRENT_SOURCE_DIR}\" \"*\")\n foreach(WORKLOAD_DIR ${WORKLOAD_DIRS})\n     set(FULL_WORKLOAD_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/${WORKLOAD_DIR}\")\n \n-    # Check if it's a directory (not a file)\n-    if(IS_DIRECTORY \"${FULL_WORKLOAD_DIR}\")\n+    # Check if it's a directory (not a file) and currently only for x86_64\n+    if(IS_DIRECTORY \"${FULL_WORKLOAD_DIR}\" AND CMAKE_SYSTEM_PROCESSOR MATCHES \"x86_64|AMD64\")\n         # Use the directory name as the workload name\n         add_workload_library(\"${WORKLOAD_DIR}\" \"${FULL_WORKLOAD_DIR}\")\n     endif()\ndiff --git a/tests/workloads/README.md b/tests/workloads/README.md\nnew file mode 100644\nindex 0000000..cdc0bf2\n--- /dev/null\n+++ b/tests/workloads/README.md\n@@ -0,0 +1,42 @@\n+# RTC-Testbench Workload Integration Guide\n+\n+This document describes the requirements for implementing a workload that can be dynamically loaded into the RTC-Testbench framework.\n+\n+## Requirements\n+\n+To be compatible with RTC-Testbench, a workload must:\n+\n+1. Be compiled as a shared object with position independent code (using the `-fPIC` GCC compiler flag).\n+2. Implement a runtime function to be called each cycle after completion of the network RX routine.\n+3. (Optional) Implement a setup function to perform initialization tasks, such as memory allocation, to avoid doing them\n+   in the time critical path.\n+\n+## Specifying a workload\n+\n+The following options are used to configure a TsnHigh RX workload:\n+\n+| Configuration Option              | Description                                                                                | Type    |\n+| --------------------------------- | ------------------------------------------------------------------------------------------ | ------- |\n+| `TsnHighRxWorkloadEnabled`        | Enable/disable workload execution for TSN High traffic class                               | Boolean |\n+| `TsnHighRxWorkloadFile`           | Path to the shared library containing the workload                                         | String  |\n+| `TsnHighRxWorkloadSetupFunction`  | Name of the setup function to call during initialization                                   | String  |\n+| `TsnHighRxWorkloadSetupArguments` | Arguments passed to the setup function (space-separated string)                            | String  |\n+| `TsnHighRxWorkloadFunction`       | Name of the runtime function called each cycle                                             | String  |\n+| `TsnHighRxWorkloadArguments`      | Arguments passed to the runtime function                                                   | String  |\n+| `TsnHighRxWorkloadPrewarm`        | Execute workload immediately when threads spawn (true) or wait for network traffic (false) | Boolean |\n+| `TsnHighRxWorkloadSkipCount`      | Skip min/max statistics updates for the first N workload iterations                        | Integer |\n+| `TsnHighRxWorkloadThreadCpu`      | CPU core number to pin the workload thread to                                              | Integer |\n+| `TsnHighRxWorkloadThreadPriority` | Real-time thread priority (1-99, higher values = higher priority)                          | Integer |\n+\n+## Examples\n+\n+The `pointer_chasing` example workload has been provided and can be found in `tests/workloads/pointer_chasing`. A test that uses\n+the pointer_chasing workload can be found in `tests/busypolling_1ms_rtworkload`.\n+\n+## Copyright\n+\n+Copyright (C) 2025 Intel Corporation\n+\n+## License\n+\n+BSD-2 Clause and Dual BSD/GPL for all eBPF programs\ndiff --git a/tests/workloads/jacobi_2d/jacobi_2d.c b/tests/workloads/jacobi_2d/jacobi_2d.c\ndeleted file mode 100644\nindex 0755d79..0000000\n--- a/tests/workloads/jacobi_2d/jacobi_2d.c\n+++ /dev/null\n@@ -1,117 +0,0 @@\n-// SPDX-License-Identifier: BSD-2-Clause\n-/*\n- * Copyright (c) 2026 Linutronix GmbH\n- */\n-#include <errno.h>\n-#include <inttypes.h>\n-#include <stdint.h>\n-#include <stdio.h>\n-#include <stdlib.h>\n-#include <string.h>\n-#include <time.h>\n-\n-#include \"workload.h\"\n-\n-#include \"jacobi_2d.h\"\n-\n-static inline int idx(int i, int j)\n-{\n-\treturn i * GRID_SIZE + j;\n-}\n-\n-static void jacobi_2d_init_grid(double *grid)\n-{\n-\tsrand(time(NULL));\n-\n-\t/* Use random doubles between 0 and 1000 */\n-\tfor (int i = 0; i < GRID_SIZE; i++)\n-\t\tfor (int j = 0; j < GRID_SIZE; j++)\n-\t\t\tgrid[idx(i, j)] = (double)rand() / RAND_MAX * 1000;\n-}\n-\n-static void jacobi_2d_step(double *src, double *dest)\n-{\n-\t/* Lovely code for vectorization. */\n-\tfor (int i = 1; i < GRID_SIZE - 1; i++) {\n-\t\tfor (int j = 1; j < GRID_SIZE - 1; j++) {\n-\t\t\tconst double factor = 1.0 / 5.0;\n-\n-\t\t\tdest[idx(i, j)] =\n-\t\t\t\tfactor * (src[idx(i, j)] + src[idx(i - 1, j)] + src[idx(i + 1, j)] +\n-\t\t\t\t\t  src[idx(i, j - 1)] + src[idx(i, j + 1)]);\n-\t\t}\n-\t}\n-}\n-\n-int jacobi_2d_setup(struct workload_instance *instance, int argc, char *argv[])\n-{\n-\tstruct jacobi_2d *j2;\n-\n-\tif (argc != 2) {\n-\t\tfprintf(stderr, \"[jacobi_2d]: Usage: <iterations>\\n\");\n-\t\treturn -EINVAL;\n-\t}\n-\n-\tj2 = calloc(1, sizeof(*j2));\n-\tif (!j2)\n-\t\treturn -ENOMEM;\n-\n-\tj2->iterations = atoi(argv[1]);\n-\tif (j2->iterations <= 0) {\n-\t\tfprintf(stderr, \"[jacobi_2d]: Usage: <iterations>\\n\");\n-\t\tfree(j2);\n-\t\treturn -EINVAL;\n-\t}\n-\n-\tj2->grid_init = calloc(GRID_SIZE * GRID_SIZE, sizeof(double));\n-\tj2->grid_a = calloc(GRID_SIZE * GRID_SIZE, sizeof(double));\n-\tj2->grid_b = calloc(GRID_SIZE * GRID_SIZE, sizeof(double));\n-\n-\tif (!j2->grid_init || !j2->grid_a || !j2->grid_b) {\n-\t\tfprintf(stderr, \"[jacobi_2d]: Grid allocation failed!\\n\");\n-\t\tfree(j2);\n-\t\treturn -ENOMEM;\n-\t}\n-\n-\tjacobi_2d_init_grid(j2->grid_init);\n-\n-\tinstance->priv = j2;\n-\n-\treturn 0;\n-}\n-\n-int jacobi_2d_run(struct workload_instance *instance, int argc, char *argv[])\n-{\n-\tstruct jacobi_2d *j2 = instance->priv;\n-\t(void)argc;\n-\t(void)argv;\n-\n-\tdouble *src = j2->grid_a;\n-\tdouble *dest = j2->grid_b;\n-\tdouble *init = j2->grid_init;\n-\n-\t/* Initialize src with known state. */\n-\tmemcpy(src, init, GRID_SIZE * GRID_SIZE * sizeof(double));\n-\n-\tfor (int i = 0; i < j2->iterations; i++) {\n-\t\tdouble *tmp;\n-\n-\t\tjacobi_2d_step(src, dest);\n-\n-\t\ttmp = src;\n-\t\tsrc = dest;\n-\t\tdest = tmp;\n-\t}\n-\n-\treturn 0;\n-}\n-\n-void jacobi_2d_teardown(struct workload_instance *instance)\n-{\n-\tstruct jacobi_2d *j2 = instance->priv;\n-\n-\tfree(j2->grid_init);\n-\tfree(j2->grid_a);\n-\tfree(j2->grid_b);\n-\tfree(j2);\n-}\ndiff --git a/tests/workloads/jacobi_2d/jacobi_2d.h b/tests/workloads/jacobi_2d/jacobi_2d.h\ndeleted file mode 100644\nindex cc8777d..0000000\n--- a/tests/workloads/jacobi_2d/jacobi_2d.h\n+++ /dev/null\n@@ -1,38 +0,0 @@\n-// SPDX-License-Identifier: BSD-2-Clause\n-/*\n- * Copyright (c) 2026 Linutronix GmbH\n- */\n-\n-#ifndef JACOBI_2D_H\n-#define JACOBI_2D_H\n-\n-struct workload_instance;\n-\n-struct jacobi_2d {\n-\tdouble *grid_init;\n-\tdouble *grid_a;\n-\tdouble *grid_b;\n-\tint iterations;\n-};\n-\n-/*\n- * Size: 100 x 100 x 8 -> 78.125 Kib.\n- *\n- * Keep the GRID_SIZE small, so that this code can be used with cycles times of 1ms. For larger\n- * compute periods increase the number iterations passed to jacobi_2d_setup().\n- *\n- * Tested on i7-10700TE. Iterations: 1  -> ~23us\n- *                       Iterations: 20 -> ~332us\n- */\n-#define GRID_SIZE 100\n-\n-/* Setup function */\n-int jacobi_2d_setup(struct workload_instance *instance, int argc, char *argv[]);\n-\n-/* Teardown function */\n-void jacobi_2d_teardown(struct workload_instance *instance);\n-\n-/* Run time function */\n-int jacobi_2d_run(struct workload_instance *instance, int argc, char *argv[]);\n-\n-#endif /* JACOBI_2D_H */\ndiff --git a/tests/workloads/pointer_chasing/README.md b/tests/workloads/pointer_chasing/README.md\nnew file mode 100644\nindex 0000000..3e92352\n--- /dev/null\n+++ b/tests/workloads/pointer_chasing/README.md\n@@ -0,0 +1,84 @@\n+# Pointer Chasing Workload\n+\n+## Overview\n+\n+The pointer chasing workload is designed to stress the CPU's memory hierarchy by following a chain\n+of pointers through memory in a pseudo-random pattern.\n+\n+### Core Concept\n+\n+The workload creates a linked list where nodes are distributed randomly throughout a large memory\n+buffer. When executed, the CPU must follow pointer chains, causing cache misses and memory stalls.\n+The buffer and span sizes can be customized to target specific hierarchies in the cache subsystem,\n+or sized sufficiently large for main memory.\n+\n+### Key Components\n+\n+1. **Data Structure (`ptr_node`)**\n+\n+   - 64-byte aligned union containing a pointer to the next node and a value\n+\n+2. **Setup Phase (`ptr_chase_setup`)**\n+\n+   - Parses buffer size and span size from command line arguments\n+   - Allocates memory and creates a randomized linked list\n+   - Buffer size: Total memory allocated\n+   - Span size: Used in conjunction with `CACHE_LINE_SIZE` to determine how many linked list nodes\n+     are traversed\n+\n+3. **Linked List Generation (`createLinkedList`)**\n+\n+   - Creates a pseudo-random chain of pointers within the specified span\n+   - Uses a seeded random number generator for reproducible results\n+   - Ensures each node is visited exactly once\n+\n+4. **Execution (`run_ptr_chasing`)**\n+   - Follows the pointer chain using optimized assembly code\n+   - Assembly loop continues until reaching a NULL pointer\n+\n+### Assembly Implementation\n+\n+The core loop is implemented in assembly for precise control:\n+\n+```assembly\n+__chasing_code_loop:\n+    mov (%rax), %rax    ; Load next pointer\n+    test %rax, %rax     ; Check if NULL\n+    jne __chasing_code_loop ; Continue if not NULL\n+    ret                 ; Return when done\n+```\n+\n+### ptr_chase_setup parameters\n+\n+The `ptr_chase_setup` function expects two arguments:\n+\n+- **Buffer Size**: Total memory allocated (in hexadecimal)\n+- **Span Size**: Used in conjunction with `CACHE_LINE_SIZE` to determine how many linked list\n+  nodes are traversed. As span size approaches buffer size, it will take longer\n+  to create the LinkedList. Recommended to keep buffer size slightly larger than\n+  span size.\n+\n+In the `tests/busypolling_1ms_rtworkload` example, the following values are used:\n+\n+- Buffer size: 0x4A4000 (~4.6MB total allocation)\n+- Span size: 0x129000 (~1.2MB used for linked list)\n+\n+## Example RTC-Testbench configuration file for pointer chasing\n+\n+See `tests/busypolling_1ms_rtworkload/mirror_vid100_cml.yaml`.\n+\n+## Build Instructions\n+\n+From the main project build directory:\n+\n+```bash\n+make pointer_chasing                   # Build shared library\n+```\n+\n+## Copyright\n+\n+Copyright (C) 2025 Intel Corporation\n+\n+## License\n+\n+BSD-2 Clause and Dual BSD/GPL for all eBPF programs\ndiff --git a/tests/workloads/pointer_chasing/pointer_chasing.c b/tests/workloads/pointer_chasing/pointer_chasing.c\nindex 79102df..3e2dbbd 100644\n--- a/tests/workloads/pointer_chasing/pointer_chasing.c\n+++ b/tests/workloads/pointer_chasing/pointer_chasing.c\n@@ -1,179 +1,127 @@\n // SPDX-License-Identifier: BSD-2-Clause\n /*\n  * Copyright (c) 2025 Intel Corporation\n- * Copyright (c) 2026 Linutronix GmbH\n  */\n-#include <errno.h>\n+#include \"pointer_chasing.h\"\n+#include \"log.h\"\n #include <inttypes.h>\n #include <stdint.h>\n #include <stdio.h>\n #include <stdlib.h>\n \n-#include \"workload.h\"\n-\n-#include \"pointer_chasing.h\"\n-\n-/* Get random int in a range of [0, max) */\n-static int random_int(int max)\n-{\n-\treturn rand() % max;\n-}\n-\n-static ptr_node *create_linked_list(ptr_chaser_t *ptr_chaser)\n-{\n-\tptr_node *ptr, *head;\n-\tuint64_t i, nr_nodes;\n-\tint offset;\n-\n-\tnr_nodes = ptr_chaser->span / sizeof(ptr_node);\n-\n-\t/* Allocate large buffer that we will read randomly from */\n-\tptr_chaser->mem = (ptr_node *)malloc(ptr_chaser->buff * sizeof(ptr_node));\n-\tif (!ptr_chaser->mem) {\n-\t\tfprintf(stderr,\n-\t\t\t\"[pointer_chasing]: Memory allocation failed. Consider reducing size \"\n-\t\t\t\"of the buffer?\\n\");\n-\t\treturn NULL;\n-\t}\n-\n-\thead = &ptr_chaser->mem[random_int(ptr_chaser->buff)];\n-\thead->val = 1;\n-\tptr = head;\n-\n-\t/* Create linked list that will fill a buffer */\n-\ti = 1;\n-\twhile (i < nr_nodes) {\n-\t\t/* Pick a random address in the span */\n-\t\toffset = random_int(nr_nodes);\n-\t\tif (ptr_chaser->mem[offset].val == 0) {\n-\t\t\tptr->next = &ptr_chaser->mem[offset];\n-\t\t\tptr = ptr->next;\n-\t\t\tptr->val = 1;\n-\t\t\ti++;\n-\t\t}\n-\t}\n-\n-\treturn head;\n-}\n-\n-static int generate_linked_list(ptr_chaser_t *ptr_chaser, unsigned int seed)\n-{\n-\tptr_node *head;\n-\n-\tsrand(seed);\n-\thead = create_linked_list(ptr_chaser);\n-\tif (!head) {\n-\t\tfprintf(stderr, \"[pointer_chasing]: Creating Linked List failed.\\n\");\n-\t\treturn -ENOMEM;\n-\t}\n-\n-\tptr_chaser->head = (void *)head;\n-\treturn 0;\n-}\n+ptr_chaser_t *ptr_chaser;\n \n-int ptr_chase_setup(struct workload_instance *instance, int argc, char *argv[])\n+int ptr_chase_setup(int argc, char *argv[])\n {\n-\tstruct ptr_chaser *ptr_chaser;\n \tuint64_t buff, span;\n-\tchar *endptr;\n \tint ret = 0;\n \n \tif (argc != 3) {\n-\t\tfprintf(stderr, \"[pointer_chasing]: Usage: <buff_size> <span_size>\\n\");\n-\t\tfprintf(stderr, \"[pointer_chasing]: Example: 0x4A0000 0x129000\\n\");\n-\t\treturn -EINVAL;\n+\t\tlog_message(LOG_LEVEL_INFO,\n+\t\t\t    \"[pointer_chasing]: Usage: <buff_size> <span_size>\\n\");\n+\t\tlog_message(LOG_LEVEL_INFO, \"[pointer_chasing]: Example: 0x4A0000 0x129000\\n\");\n+\t\treturn -1;\n \t}\n \n-\terrno = 0;\n-\tbuff = strtoull(argv[1], &endptr, 16);\n-\tif (errno != 0 || endptr == argv[1] || *endptr != '\\0') {\n-\t\tfprintf(stderr, \"[pointer_chasing]: Invalid buffer size.\\n\");\n-\t\treturn -ERANGE;\n-\t}\n-\n-\tspan = strtoull(argv[2], &endptr, 16);\n-\tif (errno != 0 || endptr == argv[2] || *endptr != '\\0') {\n-\t\tfprintf(stderr, \"[pointer_chasing]: Invalid span size.\\n\");\n-\t\treturn -ERANGE;\n-\t}\n+\tbuff = strtoull(argv[1], NULL, 16);\n+\tspan = strtoull(argv[2], NULL, 16);\n \n-\tfprintf(stderr, \"[pointer_chasing]: buff: 0x%\" PRIx64 \", span: 0x%\" PRIx64 \"\\n\", buff,\n-\t\tspan);\n+\tlog_message(LOG_LEVEL_INFO,\n+\t\t    \"[pointer_chasing]: buff: 0x%\" PRIx64 \", span: 0x%\" PRIx64\n+\t\t    \"\\n\",\n+\t\t    buff, span);\n \n \tif (buff == 0 || span == 0 || (span > buff)) {\n-\t\tfprintf(stderr, \"[pointer_chasing]: Invalid buffer/span sizes.\\n\");\n-\t\treturn -EINVAL;\n+\t\tlog_message(LOG_LEVEL_INFO, \"[pointer_chasing]: Invalid buffer/span sizes.\\n\");\n+\t\treturn -1;\n \t}\n \n \tptr_chaser = (ptr_chaser_t *)calloc(1, sizeof(ptr_chaser_t));\n-\tif (!ptr_chaser) {\n-\t\tfprintf(stderr, \"[pointer_chasing]: Memory allocation failed.\\n\");\n-\t\treturn -ENOMEM;\n+\n+\tif (ptr_chaser == NULL) {\n+\t\tlog_message(LOG_LEVEL_INFO, \"[pointer_chasing]: Memory allocation failed.\\n\");\n+\t\treturn -1;\n \t}\n \n \tptr_chaser->buff = buff;\n \tptr_chaser->span = span;\n \tret = generate_linked_list(ptr_chaser, 0xdeadbeef);\n-\tif (ret) {\n-\t\tfree(ptr_chaser);\n-\t\tptr_chaser = NULL;\n-\t\treturn ret;\n-\t}\n \n-#ifdef __x86_64__\n-\tptr_chaser->workload = (void *)__chasing_code_loop;\n-#endif\n+\tif (ret)\n+\t\treturn -1;\n \n-\tinstance->priv = ptr_chaser;\n+\tptr_chaser->workload = (void *)__chasing_code_loop;\n \n \treturn 0;\n }\n \n-/*\n- * Generic ptr_chasing workload function in plain C:\n- *\n- * Compiles to the following code on x86_64 with gcc 14.2.0:\n- *   ptr_loop:\n- *     movq       (%rax), %rax ; CODE XREF=run_ptr_chasing+38\n- *     testq      %rax, %rax\n- *     jne        ptr_loop\n- *\n- * ARM64 looks similar:\n- *   ptr_loop:\n- *     ldr        x0, [x0]     ; CODE XREF=run_ptr_chasing+24\n- *     cbnz       x0, ptr_loop\n- */\n-static void __attribute__((unused)) * ptr_chasing_run_workload(ptr_chaser_t *ptr_chaser)\n+void run_ptr_chasing(void)\n {\n-\tptr_node *p = ptr_chaser->head;\n-\n-\twhile (p)\n-\t\tp = p->next;\n-\n-\treturn p;\n+\t__ptr_chasing_run_workload(ptr_chaser);\n }\n \n-int run_ptr_chasing(struct workload_instance *instance, int argc, char *argv[])\n+int generate_linked_list(ptr_chaser_t *ptr_chaser, unsigned int seed)\n {\n-\tstruct ptr_chaser *ptr_chaser = instance->priv;\n-\t(void)argc;\n-\t(void)argv;\n-\n-#ifdef __x86_64__\n-\t/* Hand written version for x86_64 */\n-\tptr_chasing_run_workload_x86_64(ptr_chaser);\n-#else\n-\t/* For all other architectures */\n-\tptr_chasing_run_workload(ptr_chaser);\n-#endif\n+\tptr_node *head;\n+\n+\tsrand(seed);\n+\thead = createLinkedList();\n+\n+\tif (head == NULL) {\n+\t\tlog_message(LOG_LEVEL_INFO, \"[pointer_chasing]: Creating Linked List failed.\\n\");\n+\t\treturn -1;\n+\t}\n \n+\tptr_chaser->head = (void *)head;\n \treturn 0;\n }\n \n-void ptr_chase_teardown(struct workload_instance *instance)\n+/* Get random int in a range of [0, max) */\n+int random_int(int max)\n+{\n+\treturn (rand() % max);\n+}\n+\n+ptr_node *createLinkedList(void)\n {\n-\tstruct ptr_chaser *ptr_chaser = instance->priv;\n+\tptr_node *ptr, *head;\n+\tuint64_t i, nr_nodes;\n+\tint offset;\n \n-\tfree(ptr_chaser->mem);\n+\tnr_nodes = ptr_chaser->span / sizeof(ptr_node);\n+\n+\t/* Allocate large buffer that we will read randomly from */\n+\tptr_node *mem = (ptr_node *)malloc(ptr_chaser->buff * sizeof(ptr_node));\n+\n+\tif (mem == NULL) {\n+\t\tlog_message(LOG_LEVEL_INFO,\n+\t\t\t    \"[pointer_chasing]: Memory allocation failed. Consider reducing size \"\n+\t\t\t    \"of the buffer?\\n\");\n+\t\treturn NULL;\n+\t}\n+\n+\thead = &mem[random_int(ptr_chaser->buff)];\n+\thead->val = 1;\n+\tptr = head;\n+\n+\t/* Create linked list that will fill a buffer */\n+\ti = 1;\n+\twhile (i < nr_nodes) {\n+\t\t/* Pick a random address in the span */\n+\t\toffset = random_int(nr_nodes);\n+\t\tif (mem[offset].val == 0) {\n+\t\t\tptr->next = &mem[offset];\n+\t\t\tptr = ptr->next;\n+\t\t\tptr->val = 1;\n+\t\t\ti++;\n+\t\t}\n+\t}\n+\n+\treturn head;\n+}\n+\n+__attribute__((destructor)) int ptr_chaser_finish(void)\n+{\n \tfree(ptr_chaser);\n+\treturn 0;\n }\ndiff --git a/tests/workloads/pointer_chasing/pointer_chasing.h b/tests/workloads/pointer_chasing/pointer_chasing.h\nindex d243693..13adfc8 100644\n--- a/tests/workloads/pointer_chasing/pointer_chasing.h\n+++ b/tests/workloads/pointer_chasing/pointer_chasing.h\n@@ -1,7 +1,6 @@\n // SPDX-License-Identifier: BSD-2-Clause\n /*\n  * Copyright (c) 2025 Intel Corporation\n- * Copyright (c) 2026 Linutronix GmbH\n  */\n \n #ifndef _POINTER_CHASING_H_\n@@ -9,8 +8,6 @@\n \n #include <stdint.h>\n \n-struct workload_instance;\n-\n #define CACHE_LINE_SIZE 64\n \n typedef union ptr_node {\n@@ -18,7 +15,7 @@ typedef union ptr_node {\n \t\tunion ptr_node *next;\n \t\tint val;\n \t};\n-\tuint8_t bytes[CACHE_LINE_SIZE];\n+\tuint8_t bytes[64];\n } ptr_node;\n \n typedef struct ptr_chaser {\n@@ -26,10 +23,8 @@ typedef struct ptr_chaser {\n \tuint64_t span;\n \tvoid *head;\n \tvoid (*workload)(struct ptr_chaser *);\n-\tptr_node *mem;\n } ptr_chaser_t;\n \n-#ifdef __x86_64__\n extern void __chasing_code_loop(void);\n __asm__(\".global __chasing_code_loop        ;\\n\\t\"\n \t\"__chasing_code_loop:               ;\\n\\t\"\n@@ -43,7 +38,7 @@ __asm__(\".global __chasing_code_loop        ;\\n\\t\"\n  * code_ptr: holds a pointer to workload bytecode\n  * %%RAX: holds a pointer to head of data set\n  */\n-static __attribute__((always_inline)) inline void ptr_chasing_run_workload_x86_64(\n+static __attribute__((always_inline)) inline void __ptr_chasing_run_workload(\n \tptr_chaser_t *ptr_chaser)\n {\n \t__asm__ __volatile__(\"call *%[code_ptr]   ;\\n\\t\"\n@@ -51,15 +46,10 @@ static __attribute__((always_inline)) inline void ptr_chasing_run_workload_x86_6\n \t\t\t     : [code_ptr] \"g\"(ptr_chaser->workload), \"a\"(ptr_chaser->head)\n \t\t\t     :);\n }\n-#endif\n-\n-/* Setup function */\n-int ptr_chase_setup(struct workload_instance *instance, int argc, char *argv[]);\n-\n-/* Teardown function */\n-void ptr_chase_teardown(struct workload_instance *instance);\n \n-/* Run time function */\n-int run_ptr_chasing(struct workload_instance *instance, int argc, char *argv[]);\n+int generate_linked_list(ptr_chaser_t *ptr_chaser, unsigned int seed);\n+union ptr_node *createLinkedList(void);\n+int ptr_chase_setup(int argc, char *argv[]);\n+void run_ptr_chasing(void);\n \n #endif /* _POINTER_CHASING_H_ */\ndiff --git a/tools/ci/build.sh b/tools/ci/build.sh\nindex d55c0a5..e38b0a1 100755\n--- a/tools/ci/build.sh\n+++ b/tools/ci/build.sh\n@@ -1,6 +1,6 @@\n #!/bin/bash\n #\n-# Copyright (C) 2024-2026 Linutronix GmbH\n+# Copyright (C) 2024 Linutronix GmbH\n # Author Kurt Kanzenbach <kurt@linutronix.de>\n #\n # SPDX-License-Identifier: BSD-2-Clause\n@@ -13,26 +13,23 @@ set -e\n COMPILER=\"gcc clang\"\n OPTIONS=\"WITH_MQTT RX_TIMESTAMP TX_TIMESTAMP\"\n \n+if [ \"$1\" == \"ubuntu\" ]; then\n+  OPTIONS=\"WITH_MQTT\"\n+fi\n+\n cd $(dirname $0)\n \n pushd ../..\n \n-combos=(\"\")\n-for option in $OPTIONS; do\n-  new_combos=()\n-  for combo in \"${combos[@]}\"; do\n-    new_combos+=(\"$combo -D$option=OFF\")\n-    new_combos+=(\"$combo -D$option=ON\")\n-  done\n-  combos=(\"${new_combos[@]}\")\n-done\n-\n for compiler in $COMPILER; do\n-  for combo in \"${combos[@]}\"; do\n+  for option in $OPTIONS; do\n     mkdir -p build\n     pushd build\n-    echo \"Trying 'CC=$compiler cmake$combo' ...\"\n-    CC=$compiler cmake $combo ..\n+    echo \"Trying 'CC=$compiler cmake -D$option=OFF' ...\"\n+    CC=$compiler cmake -D$option=OFF ..\n+    make -j$(nproc)\n+    echo \"Trying 'CC=$compiler cmake -D$option=ON' ...\"\n+    CC=$compiler cmake -D$option=ON ..\n     make -j$(nproc)\n     popd\n     rm -rf build\ndiff --git a/tools/ci/qa.sh b/tools/ci/qa.sh\nindex f5a129b..cd096f3 100755\n--- a/tools/ci/qa.sh\n+++ b/tools/ci/qa.sh\n@@ -1,11 +1,11 @@\n #!/bin/bash\n #\n-# Copyright (C) 2024-2026 Linutronix GmbH\n+# Copyright (C) 2024 Linutronix GmbH\n # Author Kurt Kanzenbach <kurt@linutronix.de>\n #\n # SPDX-License-Identifier: BSD-2-Clause\n #\n-# Check coding style and unit tests.\n+# Check coding style.\n #\n \n set -e\n@@ -19,12 +19,6 @@ reuse lint\n \n echo \"Checking coding style with clang-format ...\"\n clang-format --Werror --dry-run src/*.c src/*.h\n-clang-format --Werror --dry-run tests/workloads/*/*.c tests/workloads/*/*.h\n-\n-echo \"Checking Perl coding style with perltidy ...\"\n-for f in scripts/*.pl; do\n-  perltidy --assert-tidy -st \"$f\" >/dev/null\n-done\n \n echo \"Checking naming style with clang-tidy ...\"\n mkdir build\n@@ -35,15 +29,6 @@ run-clang-tidy\n popd\n rm -rf build\n \n-echo \"Building and running unit tests ...\"\n-mkdir build\n-pushd build\n-cmake -DWITH_TESTS=ON ..\n-make -j$(nproc)\n-ctest\n-popd\n-rm -rf build\n-\n popd\n \n exit 0\n","remote_name":"debian/unstable","revision":"git-v1:42aeb05bb69f8dad55b6644988cc53173d3c059d","role":"main"}],"result_code":"success","result_tags":[],"revision":"git-v1:42aeb05bb69f8dad55b6644988cc53173d3c059d","start_time":"2026-11-08T05:47:47.076363Z","subpath":"","suite":"control","target_branch_url":null,"vcs_type":"git","worker":"tyr"},"run_id":"b2fbc97b-e394-4ed9-ae82-73a77c64de87","success_probability":1.0,"suite":"control","total_previous_runs":1,"utcnow":"2026-11-09T02:42:54.014595212+00:00","vcs_type":"git","vcs_url":"git+ssh://git@salsa.debian.org/debian/rtc-testbench.git","version":"0.0.0","worker_log_name":"worker.log","worker_name":"tyr"}