Skip to content

feat: add TimeSlave process and libTSClient shm IPC channel#8

Open
gordon9901 wants to merge 12 commits intoeclipse-score:mainfrom
gordon9901:ecarx_gptp
Open

feat: add TimeSlave process and libTSClient shm IPC channel#8
gordon9901 wants to merge 12 commits intoeclipse-score:mainfrom
gordon9901:ecarx_gptp

Conversation

@gordon9901
Copy link
Copy Markdown
Contributor

@gordon9901 gordon9901 commented Mar 27, 2026

feat: introduce TimeSlave process and libTSClient IPC channel

Summary

This PR splits the gPTP protocol stack out of TimeDaemon into a new standalone process — TimeSlave — and introduces a lightweight shared-memory IPC library (libTSClient) so that TimeDaemon can consume PtpTimeInfo snapshots from TimeSlave without running the raw-socket protocol itself.

Changes

score/TimeSlave/ (new)
A self-contained gPTP slave process (score::ts::TimeSlave) that:

  • Runs GptpEngine (existing RxThread + PdelayThread logic) to participate in IEEE 802.1AS protocol.
  • Includes platform implementations for both Linux and QNX (raw_socket, network_identity, phc_adjuster).
  • Exposes instrumentation (probe) and recording (recorder) capabilities.
  • Publishes GptpIpcData snapshots to shared memory via GptpIpcPublisher.

score/libTSClient/ (new)
A process-boundary IPC library providing:

  • GptpIpcData / GptpIpcStatus / GptpIpcSyncFupData / GptpIpcPDelayData
    (gptp_ipc_data.h, namespace score::ts) — IPC-layer data types conveying PTP time, status, and measurement fields.

  • GptpIpcRegion
    (gptp_ipc_channel.h) — 64-byte cache-line-aligned shared memory layout with a seqlock (seq / seq_confirm) for lock-free single-writer / multi-reader access.
    Default channel name: kGptpIpcName = "/gptp_ptp_info" (namespace score::ts::details).

  • GptpIpcPublisher — creates and writes to the POSIX shared memory segment (used by TimeSlave).

  • GptpIpcReceiver — opens and reads from the segment with seqlock retry (used by TimeDaemon via GPTPRealMachine).

  • gptp_ipc.h — convenience header aggregating all of the above.

Both publisher and receiver reside in namespace score::ts::details.

score/TimeDaemon/code/ptp_machine/real/ (new)

  • details::ShmPTPEngine
    (real/details/shm_ptp_engine.h, namespace score::td::details) — a PTPEngine-compatible adapter backed by GptpIpcReceiver; converts GptpIpcData (libTSClient) to PtpTimeInfo (TimeDaemon).

  • GPTPRealMachine — type alias for PTPMachine<details::ShmPTPEngine>, slotting cleanly into the existing TimeDaemon pipeline.

  • CreateGPTPRealMachine(name, ipc_name) — factory function for convenient construction; ipc_name defaults to kGptpIpcName.

score/TimeDaemon/code/common/data_types/BUILD, score/TimeDaemon/code/data_types/BUILD (modified)
Widened Bazel visibility from //score/TimeDaemon:__subpackages__ to //score:__subpackages__ so that TimeSlave and libTSClient can reuse shared data types (PtpTimeInfo, etc.) without duplication.

score/TimeDaemon/code/ptp_machine/BUILD (modified)
Added //score/TimeDaemon/code/ptp_machine/real:unit_test_suite to the aggregated test target.

Architecture

TimeSlave (new process)
  └── GptpEngine (RxThread + PdelayThread)
        └── GptpIpcPublisher  ──[POSIX shm / seqlock]──►  GptpIpcReceiver
                                                                 └── details::ShmPTPEngine
                                                                       └── GPTPRealMachine
                                                                             └── TimeDaemon pipeline

Ticket:eclipse-score/score#2691

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 27, 2026

License Check Results

🚀 The license check job ran with the Bazel command:

bazel run //:license-check

Status: ⚠️ Needs Review

Click to expand output
[License Check Output]
2026/04/28 07:25:41 Downloading https://releases.bazel.build/8.3.0/release/bazel-8.3.0-linux-x86_64...
Extracting Bazel installation...
Starting local Bazel server (8.3.0) and connecting to it...
INFO: Invocation ID: 8bf3ab65-33fd-4535-a8f3-15d3c9a25d29
Computing main repo mapping: 
Computing main repo mapping: 
Computing main repo mapping: 
Computing main repo mapping: 
Computing main repo mapping: 
Computing main repo mapping: 
Computing main repo mapping: 
Computing main repo mapping: 
WARNING: For repository 'aspect_rules_lint', the root module requires module version aspect_rules_lint@1.5.3, but got aspect_rules_lint@2.0.0 in the resolved dependency graph. Please update the version in your MODULE.bazel or set --check_direct_dependencies=off
Computing main repo mapping: 
Computing main repo mapping: 
Computing main repo mapping: 
Loading: 
Loading: 2 packages loaded
Loading: 2 packages loaded
    currently loading: 
Loading: 2 packages loaded
    currently loading: 
Loading: 2 packages loaded
    currently loading: 
Loading: 2 packages loaded
    currently loading: 
Analyzing: target //:license-check (3 packages loaded, 0 targets configured)
Analyzing: target //:license-check (3 packages loaded, 0 targets configured)

Analyzing: target //:license-check (52 packages loaded, 9 targets configured)

Analyzing: target //:license-check (86 packages loaded, 9 targets configured)

Analyzing: target //:license-check (88 packages loaded, 9 targets configured)

Analyzing: target //:license-check (104 packages loaded, 62 targets configured)

Analyzing: target //:license-check (150 packages loaded, 2657 targets configured)

Analyzing: target //:license-check (158 packages loaded, 6558 targets configured)

Analyzing: target //:license-check (169 packages loaded, 8047 targets configured)

Analyzing: target //:license-check (169 packages loaded, 8049 targets configured)

Analyzing: target //:license-check (169 packages loaded, 8049 targets configured)

Analyzing: target //:license-check (169 packages loaded, 8049 targets configured)

Analyzing: target //:license-check (173 packages loaded, 10058 targets configured)

INFO: Analyzed target //:license-check (174 packages loaded, 10184 targets configured).
[7 / 16] Creating runfiles tree bazel-out/k8-opt-exec-ST-d57f47055a04/bin/external/score_tooling+/dash/tool/formatters/dash_format_converter.runfiles [for tool]; 0s local
[9 / 16] checking cached actions
INFO: From Generating Dash formatted dependency file ...:
INFO: Successfully converted 2 packages from Cargo.lock to bazel-out/k8-fastbuild/bin/formatted.txt
[13 / 16] JavaToolchainCompileClasses external/rules_java+/toolchains/platformclasspath_classes; 0s disk-cache, processwrapper-sandbox
[15 / 16] [Prepa] Building license.check.license_check.jar ()
INFO: Found 1 target...
Target //:license.check.license_check up-to-date:
  bazel-bin/license.check.license_check
  bazel-bin/license.check.license_check.jar
INFO: Elapsed time: 37.455s, Critical Path: 2.38s
INFO: 16 processes: 12 internal, 3 processwrapper-sandbox, 1 worker.
INFO: Build completed successfully, 16 total actions
INFO: Running command line: bazel-bin/license.check.license_check ./formatted.txt <args omitted>
usage: org.eclipse.dash.licenses.cli.Main [-batch <int>] [-cd <url>]
       [-confidence <int>] [-ef <url>] [-excludeSources <sources>] [-help] [-lic
       <url>] [-project <shortname>] [-repo <url>] [-review] [-summary <file>]
       [-timeout <seconds>] [-token <token>]

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 1, 2026

The created documentation from the pull request is available at: docu-html

Comment thread score/libTSClient/gptp_ipc_channel.h Outdated
Comment thread score/libTSClient/gptp_ipc.h
Comment thread score/libTSClient/gptp_ipc_channel.h Outdated
Comment thread score/libTSClient/gptp_ipc_publisher.cpp
Comment thread score/libTSClient/gptp_ipc_test.cpp
Comment thread score/TimeSlave/code/application/time_slave.cpp Outdated
Comment thread score/TimeSlave/code/gptp/gptp_engine.cpp
Comment thread score/TimeSlave/code/gptp/gptp_engine.cpp Outdated
Comment thread score/TimeSlave/code/gptp/gptp_engine.cpp Outdated
Comment thread score/TimeSlave/BUILD
Comment thread score/libTSClient/gptp_ipc_publisher.cpp
Comment thread score/TimeDaemon/code/ptp_machine/real/details/BUILD Outdated
Comment thread score/TimeSlave/code/common/logging_contexts.h Outdated
Comment thread score/TimeSlave/code/common/logging_contexts.h Outdated
Comment thread score/TimeSlave/code/gptp/gptp_engine.cpp Outdated
Comment thread score/TimeSlave/code/gptp/gptp_engine.cpp Outdated
Copy link
Copy Markdown
Contributor

@BjoernAtBosch BjoernAtBosch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments for now

Comment on lines +134 to +146
const TmvT t3c = TmvT{t3.ns + c1.ns + c2.ns};
// t4 = BPF_T_BINTIME (PHC) receive timestamp of Pdelay_Resp (main BPF fd)
const TmvT t4 = resp_.recvHardwareTS;

const std::int64_t delay = ((t2.ns - t1.ns) + (t4.ns - t3c.ns)) / 2LL;

if (delay < 0)
return;

PDelayResult r{};
r.path_delay_ns = delay;
r.valid = true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we move all kind of evaluation to the TimeDaemon?
That component will be later ASIL certifiable and should do all kind of timestamp validations ...
TimeSlave cannot be certified (i.e. is QM only). It could induce errors due to interference.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. GptpIpcPDelayData now carries all raw timestamps (t1/t2/t3/t4), so TimeDaemon can recompute and validate independently.

The current checks in TimeSlave are kept as QM best-effort guards to avoid writing invalid data to shared memory. I suggest discussing the ASIL-B validation migration to TimeDaemon in a meeting first; once agreed, we track it as a separate issue, outside this PR. Does that work for you?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool. Yes, valid remark. Accepted

* @param buf Buffer large enough to hold existing payload plus header.
* @param buf_len In/out: payload length → frame length after prepend.
* @param src_mac Source MAC address (should be the port's own MAC).
* @return true on success, false if the buffer would overflow.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Param buf_capacity is not described

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Added @param buf_capacity description in the Doxygen comments of frame_codec.h.

Comment on lines +91 to +100
std::lock_guard<std::mutex> lk(mutex_);
resp_ = msg;
}

void PeerDelayMeasurer::OnResponseFollowUp(const PTPMessage& msg)
{

std::lock_guard<std::mutex> lk(mutex_);
resp_fup_ = msg;
ComputeAndStoreUnlocked();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In those two functions the received messages are just set without checking if the contained sequence id is the expected one. Can we rely on the assumption, that messages of e.g. an earlier request could be received after the next request?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Added early filtering in OnResponse() and OnResponseFollowUp(): messages with non-matching sequence IDs (vs req_.ptpHdr.sequenceId) are ignored and do not update state. The existing 3-way seq ID consistency check in ComputeAndStoreUnlocked() is kept as a second line of defense.


int PeerDelayMeasurer::SendRequest(IRawSocket& socket)
{
PTPMessage req{};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we don't have recognition, if either no responses or multiple responses to a single request are received (to detect non-time-aware switches)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. This is now handled as follows: a resp_count_ counter has been introduced and is reset to 0 together with req_ under lock in SendRequest(). OnResponse() increments it only for matching sequence IDs. If resp_count_ > 1, ComputeAndStoreUnlocked() returns early without producing a result, covering the IEEE 802.1AS non-time-aware switch detection case. The no-response case is implicitly handled by the existing sequence ID consistency check (no computation triggered, last valid result retained).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think that's fine for now. I guess later we will need a more detailed concept how to deal with/behave in that kind of scenarios.

Copy link
Copy Markdown
Contributor

@BjoernAtBosch BjoernAtBosch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM


int PeerDelayMeasurer::SendRequest(IRawSocket& socket)
{
PTPMessage req{};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think that's fine for now. I guess later we will need a more detailed concept how to deal with/behave in that kind of scenarios.

Comment on lines +134 to +146
const TmvT t3c = TmvT{t3.ns + c1.ns + c2.ns};
// t4 = BPF_T_BINTIME (PHC) receive timestamp of Pdelay_Resp (main BPF fd)
const TmvT t4 = resp_.recvHardwareTS;

const std::int64_t delay = ((t2.ns - t1.ns) + (t4.ns - t3c.ns)) / 2LL;

if (delay < 0)
return;

PDelayResult r{};
r.path_delay_ns = delay;
r.valid = true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool. Yes, valid remark. Accepted

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants