diff --git a/docs/features/logging/architecture/DR-002-dlt-network-transport.rst b/docs/features/logging/architecture/DR-002-dlt-network-transport.rst new file mode 100644 index 00000000..9cb4669b --- /dev/null +++ b/docs/features/logging/architecture/DR-002-dlt-network-transport.rst @@ -0,0 +1,127 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +DLT Network Transport Evolution +================================ + +.. dec_rec:: DLT Network Transport Evolution + :id: dec_rec__logging__dlt_transport_evolution + :status: proposed + :version: 1 + :context: See below. + :decision: TBA + + Today the remote/DLT path is split across two processes, as described in + :doc:`index` and :doc:`../../../components/datarouter/index`: + + - `mw::log` (application side) serialises log records and writes + them into a shared-memory buffer. + - `datarouter` (a separate process) reads that buffer, constructs the + DLT protocol headers, and transmits the resulting UDP/IPv4 multicast + packets using the standard BSD Socket API over the platform's default + network stack, shared with all other networked services. + + A second, feature-flagged client backend (`shm_dma_enabled`) + would allow forwarding of records through a GTL client into a + DMA-capable shared-memory region instead of the DataRouter + ring buffer, handing them to a DLT-aware daemon/plugin on the receiving + side. This is not the default today, and it does not by itself change + how that receiving daemon talks to the network stack, so the properties + below still apply regardless of which client backend feeds it. + + This works, but has three structural properties worth revisiting: + + - Every message crosses two IPC hops before it reaches the wire: + one between `mw::log` and `datarouter`, and a second one from + `datarouter` into the network stack itself, since the socket API + it uses is, on this class of platforms, implemented over IPC to a + separate network-stack process rather than executing inline. Each + hop also implies copying the message (application buffer into shared + memory, shared memory into a new buffer with headers prepended, and + again into the network stack's own send buffers). It is this + combination of copies and context switches across both hops, not a + single IPC call, that drives CPU load. + - Where that network-stack process is a single-threaded resource + manager (e.g. QNX's `io-pkt`), it serialises *all* socket traffic + on the system through one queue. This is a separate overhead from + the copies above: it is contention/scheduling cost, so a burst of + log and trace traffic can add latency for every other socket user of + that same instance, and vice versa. Newer, multithreaded stack + implementations (e.g. `io-sock`) reduce this specific contention, + but do not by themselves remove the two IPC hops and copies above. + - log and trace traffic shares the same network stack and send queue as + other service traffic (E.g. Someip communication), so there is no + structural isolation between the two; any queuing or scheduling + behaviour of one can influence the other. + + **Way Forward:** + + Part 1: A GTL-based client backend as the remote-logging path. + + Part 2: Provide a compile-time seam to select between the DLTv1 wire + format and the DLTv2 wire format, analogous to the existing + build-flag pattern for Part 1, rather than replacing one with the + other. Payload serialisation (verbose/non-verbose argument + encoding) is identical between DLTv1 and DLTv2 and does not need a + seam. The concrete DLTv2 protocol implementation behind + this seam is closed-source and maintained outside this repository; + this repository only needs to own the seam/interface, not the DLTv2 + implementation itself. + + Part 3: Move the DLT header-construction + and transmission stage (i.e. the network-writing responsibility + on the daemon receiving GTL records) into a module that is loaded + directly by the network stack, running on a second, dedicated + network-stack instance used exclusively for log and trace traffic: + + - Removes the second IPC hop (and its associated copy) between the + router logic and the network stack for the transmit path. + - Allows direct use of the network stack's native buffer/interface APIs + instead of the generic socket API, removing at least one further + copy and enabling zero-copy transmission where supported by the + driver. + - Structurally isolates log and trace traffic from other network traffic, + since it no longer shares a network-stack instance, queue, or + scheduling domain with it. + - Enables transport-level controls (e.g. egress traffic shaping) to be + applied specifically to the log and trace traffic instance without + affecting other traffic. + + .. uml:: _assets/dlt_plugin.puml + + Out of scope / unaffected: + + - The `mw::log` application-facing logging APIs are unaffected; only + the backend/transport selected underneath it changes. + - The DLT payload serialisation (verbose/non-verbose argument encoding) + is unaffected, since it is identical between DLTv1 and DLTv2. + - The DLTv2 protocol implementation is out of scope: this repository only + provides the compile-time seam to select it (Part 2); the + implementation behind that seam is closed-source and lives in a + separate, non-public `repository `_. + - Freedom-from-interference (FFI) guarantees is unaffected and already + provided by the existing mw::log infrastructure. + + **Trade-offs** + + - Both halves of this evolution targets DMA/zero-copy where the + target hardware happens to support it. Each target platform + needs to be verified and configured individually; where + DMA/zero-copy isn't available, the transport still works, + just without the associated performance benefit. + - Introduces a second network-stack instance that must be configured, + operated, and kept isolated from the default one. + - Requires a feasibility phase to confirm the target network stack + supports loadable modules with the required capabilities for the + supported target platforms. diff --git a/docs/features/logging/architecture/_assets/dlt_plugin.puml b/docs/features/logging/architecture/_assets/dlt_plugin.puml new file mode 100644 index 00000000..e510f388 --- /dev/null +++ b/docs/features/logging/architecture/_assets/dlt_plugin.puml @@ -0,0 +1,146 @@ +' ******************************************************************************* +' Copyright (c) 2026 Contributors to the Eclipse Foundation +' +' See the NOTICE file(s) distributed with this work for additional +' information regarding copyright ownership. +' +' This program and the accompanying materials are made available under the +' terms of the Apache License Version 2.0 which is available at +' https://www.apache.org/licenses/LICENSE-2.0 +' +' SPDX-License-Identifier: Apache-2.0 +' ******************************************************************************* + +@startuml dlt_plugin +title DLT Daemon functionality as a \nPlugin / Loadable Shared Module (a second dedicated Network Stack instance) + +skinparam componentStyle rectangle +skinparam nodesep 40 +skinparam ranksep 60 +skinparam linetype ortho + +interface "mw::diag" as mwdiag +component "DLT Diagnostics\nComponent" as dltdiag <> +dltdiag --> mwdiag + +note top of dltdiag + Independent component registered with the + Diagnostic Manager. +end note + +package "client" <> { + component "mw::log" as mwlog <> + component "GTL" as gtl <> + + mwlog -r-> gtl +} + +note bottom of mwlog + Forwards records to GTL via an owned + DltTraceBackend/ITraceLibrary client. +end note + +file "Client\nConfiguration" as clientconfig +clientconfig -d-> mwlog + +'interface name left blank to avoid overlaps and instead highlighted via note below +interface " " as shmpayload +interface " " as shmmeta +interface " " as shmctrl +interface " " as ctrlchannel + +note top of shmpayload #Wheat : shm\n(payload) +note top of shmmeta #Wheat : shm\n(metadata) +note bottom of shmctrl #Wheat : shm\n(control block) +note top of ctrlchannel #Wheat :DLT QNX\nControl Channel + +gtl -r-> shmpayload: R/W +gtl -r-> shmmeta: R/W +mwlog -r-> shmctrl: R/W +dltdiag --> ctrlchannel + +package "Network Stack (log_and_trace instance)" <> as networkstackinstance { + package "DLT Plugin" { + component "GTL Backend" as gtlbackend + component "Core" as core + component "Statistics\nModule" as stats + component "DLT Router" as router + interface "DLT Wire Format\n(compile-time seam)" as wireformat + component "DLT File\nWriter" as filewriter + component "DLT Network\nWriter" as netwriter + component "Network Stack\nNative APIs" as fbsdapi + component "devs-network-driver.so" as driver + component "gPTP\nLogger Time" as gptp <> + } +} + +package "DLT Wire Format Implementations" as wireformatpkg { + component "DLTv2 \n(closed-source)" as dltv2wire <> #OrangeRed + component "DLTv1 \n(this repository)" as dltv1wire +} + +' Optional/experimental time-sync path +package "mw::time" as mwtime <> { + component "shm\n(logger time)" as shmloggertime <> +} + +dltv2wire -[hidden]r-> dltv1wire + +shmpayload -r-> gtlbackend : R/O +shmmeta -r-> gtlbackend : R/O + +shmctrl <-r-> core : R/W +ctrlchannel -u-> core + +gtlbackend -d-> core +core -d-> router +core -l-> stats +router -d-> filewriter +router -d-> netwriter + +wireformat -u-> filewriter +wireformat -u-> netwriter + +wireformatpkg ..|> wireformat + +note left of wireformat + Compile-time build-flag selection between + DLTv1 and DLTv2 wire formats. +end note + +filewriter -d-> fbsdapi +netwriter -d-> fbsdapi +fbsdapi -d-> driver + +file "Global\nConfiguration" as globalconfig +core <-d- globalconfig + +file "Router\nConfiguration" as routerconfig +routerconfig -d-> router + +database "filesystem\n(devb-*, fs-qnx6.so)" as fsdb +filewriter -d-> fsdb + +' Optional time-sync path +fbsdapi <-r- gptp +driver <-r- gptp +gptp <-r- mwtime +mwlog -r-> shmloggertime + +legend bottom + |= Acronym |= Meaning | + | DLT | Diagnostics Log and Trace | + | GTL | Generic Trace Library | + | QM | Quality Managed (non-ASIL) | + | ASIL | Automotive Safety Integrity Level | + | FFI | Freedom From Interference | + | shm | Shared Memory | + | R/O | Read-Only | + | R/W | Read/Write | + | gPTP | generalized Precision Time Protocol | + | mw::log | Middleware Logging (client-side logging API) | + | mw::diag | Middleware Diagnostics | + | mw::time | Middleware Time (time synchronisation) | +end legend + +@enduml diff --git a/docs/features/logging/index.rst b/docs/features/logging/index.rst index 7538d834..f306a902 100644 --- a/docs/features/logging/index.rst +++ b/docs/features/logging/index.rst @@ -43,4 +43,5 @@ capture the safety and security constraints that any backend implementation must architecture/index.rst architecture/chklst_arc_inspection.rst architecture/DR-001-logging.rst + architecture/DR-002-dlt-network-transport.rst safety_planning/index.rst