RTE RBC 2oo2 Reference Application 0.1.0 build 0006685
SIL 4 2oo2 Dual-Site Fault-Tolerant ERTMS Radio Block Centre Reference Architecture
Loading...
Searching...
No Matches
ab_gp_channel_negotiate.c File Reference

This channel's Site Role Client (ADR-040 step 2): the inter-site link, the ONLINE/STANDBY negotiation, partner supervision, takeover handling and warm-standby sync are Platform_RTE's rte_site_role service; this file supplies what is application specific and keeps GP's own entry points (ab_gp_channel_negotiate.h): More...

#include "ab_gp_channel_negotiate.h"
#include "ab_gp_sibling_state.h"
#include "ab_gp_site_status.h"
#include "ab_gp_standby_policy.h"
#include "ab_gp_startup_timestamp.h"
#include "ab_gp_takeover_key.h"
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/select.h>
#include <sys/types.h>
#include <unistd.h>
#include "rte/oal/log/rte_log.h"
#include "rte/oal/memory/rte_mem_util.h"
#include "rte/oal/timer/rte_timer.h"
#include "rte/redundancy/site_role/rte_site_role.h"
#include "rte/redundancy/state_transfer/rte_state_transfer.h"
#include "../../common/common_config.h"
#include "ab_gp_channel.h"
#include "ab_gp_channel_payload.h"
#include "ab_gp_channel_util.h"
Include dependency graph for ab_gp_channel_negotiate.c:

Go to the source code of this file.

Data Structures

struct  negotiate_state_t

Macros

#define _POSIX_C_SOURCE   200809L
#define AB_GP_TAKEOVER_CHALLENGE_MS   120000U
#define AB_GP_STARTUP_TS_REQUEST_PERIOD_MS   100U

Typedefs

typedef char negotiate_session_size_check_t[(RTE_EXAMPLE_TRAIN_SESSION_WIRE_SIZE==RTE_EXAMPLE_SITE_EXTRA_PAYLOAD_SESSION_SIZE) ? 1 :-1]
typedef char negotiate_frame_layout_check_t[((RTE_EXAMPLE_SITE_EXTRA_PAYLOAD_SIZE==(RTE_SITE_ROLE_FRAME_OVERHEAD+((size_t) RTE_EXAMPLE_MAX_TRAINS *(size_t) RTE_EXAMPLE_TRAIN_SESSION_WIRE_SIZE)+(size_t) RTE_EXAMPLE_DB_WIRE_SIZE)) &&(RTE_EXAMPLE_SITE_ROLE_VALID_MARKER==RTE_SITE_ROLE_VALID_MARKER) &&(RTE_EXAMPLE_SINGLE_MODE_FLAG_SINGLE==RTE_SITE_ROLE_SINGLE_MODE_SINGLE)) ? 1 :-1]

Functions

static rte_status_t encode_sessions_field (const void *data, uint8_t *out, size_t size)
static rte_status_t decode_sessions_field (void *data, const uint8_t *in, size_t size)
static rte_status_t encode_db_field (const void *data, uint8_t *out, size_t size)
static rte_status_t decode_db_field (void *data, const uint8_t *in, size_t size)
static void build_state_transfer_registry (rte_state_transfer_registry_t *registry, negotiate_state_t *state)
static rte_status_t encode_state (void *user, uint8_t *out, size_t size, bool live)
static bool receive_state (void *user, const uint8_t *in, size_t size, bool valid)
static rte_example_transfer_policy_t read_transfer_policy (void)
static bool ask_user_restart_or_continue (const ab_gp_channel_context_t *ctx, uint32_t transferred_cycle)
static void apply_state_transfer (ab_gp_channel_context_t *ctx, const rte_site_role_change_t *change)
static void on_role_change (void *user, const rte_site_role_change_t *change)
static void trace (void *user, const char *line)
static void delay_ms (void *user, uint32_t ms)
static void local_facts (const ab_gp_channel_context_t *ctx, rte_site_role_local_t *out)
static bool sibling_source (void *user, rte_site_role_sibling_t *out)
static rte_status_t configure_takeover_key (ab_gp_channel_context_t *ctx, rte_site_role_config_t *cfg)
static void send_startup_ts_frame (ab_gp_channel_context_t *ctx, bool is_reply, rte_timestamp_ms_t ts_ms)
static void exchange_startup_timestamp (ab_gp_channel_context_t *ctx)
void ab_gp_channel_negotiate_on_startup_ts_frame (ab_gp_channel_context_t *ctx, const uint8_t *payload, size_t size)
 Handles a PEER_MSG_KIND_STARTUP_TS frame that arrives after ab_gp_channel_negotiate_init() returned (the A/B sibling restarted and asks for the site's startup timestamp - ADR-040 step 5a, ab_gp_startup_timestamp.h). The sibling's value is cached in ctx->sibling_startup_timestamp_ms; a request is answered with a reply carrying ctx->startup_timestamp_ms (not answered while that is 0: the clock was unavailable at start and rte_site_role took its own clock), a reply is cached only. The cached value is not used by this channel: the agreed timestamp of a running channel never changes (rte_site_role resumes it across reconnects). A malformed payload is dropped. Called from pre_execute.c's on_peer_frame_received() and ab_gp_channel_checkpoint.c's poll_for_checkpoint_frame().
rte_status_t ab_gp_channel_negotiate_init (ab_gp_channel_context_t *ctx)
 Opens this channel's own inter-site negotiation link (WEST LISTENs, EAST CONNECTs), initializes the dual_channel/negotiator, and runs the startup tie-break until it settles - ported from SITE/site.c's own site_init(). Called once from ab_gp_channel_negotiate_setup() (AB/GP/com/ab_gp_channel.c), after the peer link is already up (the C relay links open later, in ab_gp_channel_relay_tasks_setup()).
void ab_gp_channel_negotiate_execute (ab_gp_channel_context_t *ctx, uint32_t cycle)
 Runs one cycle's worth of inter-site negotiation: drives the negotiator's own STATE-frame exchange (diagnostic only), sends this channel's current {faulted, single_mode, state-transfer snapshot} extra payload to its counterpart and receives theirs, then applies the promotion/demotion/both-faulted-deadlock rules - ported from SITE/site.c's own site_execute() body, minus the heartbeat-tick wait (this now runs on ab_gp_channel.c's own AB cycle tick instead of a separate timer).
void ab_gp_channel_negotiate_service_link (ab_gp_channel_context_t *ctx)
 Fast internal-cycle service of the negotiation link (warm/hot standby only).
void ab_gp_channel_negotiate_sync_standby (ab_gp_channel_context_t *ctx, uint32_t cycle)
 ONLINE only: send this cycle's post-compute snapshot to STANDBY and wait (bounded by the link's ack timeout) for its acknowledgement. Called from post_execute, i.e. before the staged outputs are committed at the checkpoint stage.
void ab_gp_channel_negotiate_on_takeover_confirm (ab_gp_channel_context_t *ctx, const char *payload)
 Records a dispatcher confirmation that the other site is not ONLINE (from Gateway, RBC_UTIL_TOPIC_TAKEOVER_CONFIRM). It only enables a promotion while this channel is STANDBY and its counterpart is unresponsive; it is ignored (and dropped) otherwise, and expires after RTE_SITE_ROLE_TAKEOVER_CONFIRM_VALID_CYCLES cycles. A payload token=<hex> is checked as an authenticated token (ADR-040 step 4b, ab_gp_takeover_key.h); any other payload takes the unauthenticated path, refused when RTE_TAKEOVER_KEY_FILE is set.
void ab_gp_channel_negotiate_flush_faulted (ab_gp_channel_context_t *ctx)
 Best-effort, synchronous final send of this channel's own {faulted=true, single_mode} extra payload to its inter-site counterpart, BEFORE the negotiation link is torn down. Call this immediately after setting ctx->own_faulted = true and BEFORE ab_gp_channel_shutdown() (which closes the link) - see ab_gp_channel_crosscompare.c's react_to_fault().
void ab_gp_channel_negotiate_shutdown (ab_gp_channel_context_t *ctx)

Detailed Description

This channel's Site Role Client (ADR-040 step 2): the inter-site link, the ONLINE/STANDBY negotiation, partner supervision, takeover handling and warm-standby sync are Platform_RTE's rte_site_role service; this file supplies what is application specific and keeps GP's own entry points (ab_gp_channel_negotiate.h):

  • the state codec of the frame's state region: this channel's train sessions and route database, packed through an rte_state_transfer registry (wire layout unchanged);
  • what a promotion takes over: the counterpart's snapshot (sessions, db) and its cycle number under the RESTART/CONTINUE/ASK_USER transfer policy (ADR-029, REQ-RBC-006: sessions are always taken over - a promoted site that forgot an in-flight Movement Authority would be actively unsafe);
  • the local facts handed to the service each cycle: FAULTED, single-channel, readiness (Gateway relay flowing); the A/B sibling's site state the service reads itself through the sibling source (sibling_source(), ADR-040 step 5b, REQ-SITEROLE-016): GP stores the raw frame (ab_gp_sibling_state), rte_site_role decodes it and runs the agreement check.

ctx->is_online mirrors rte_site_role_is_online(): set after the startup negotiation and on every change the service reports (on_role_change()).

Definition in file ab_gp_channel_negotiate.c.

Macro Definition Documentation

◆ _POSIX_C_SOURCE

#define _POSIX_C_SOURCE   200809L

Definition at line 21 of file ab_gp_channel_negotiate.c.

◆ AB_GP_TAKEOVER_CHALLENGE_MS

#define AB_GP_TAKEOVER_CHALLENGE_MS   120000U

Definition at line 50 of file ab_gp_channel_negotiate.c.

◆ AB_GP_STARTUP_TS_REQUEST_PERIOD_MS

#define AB_GP_STARTUP_TS_REQUEST_PERIOD_MS   100U

Definition at line 55 of file ab_gp_channel_negotiate.c.

Typedef Documentation

◆ negotiate_session_size_check_t

typedef char negotiate_session_size_check_t[(RTE_EXAMPLE_TRAIN_SESSION_WIRE_SIZE==RTE_EXAMPLE_SITE_EXTRA_PAYLOAD_SESSION_SIZE) ? 1 :-1]

Definition at line 58 of file ab_gp_channel_negotiate.c.

◆ negotiate_frame_layout_check_t

typedef char negotiate_frame_layout_check_t[((RTE_EXAMPLE_SITE_EXTRA_PAYLOAD_SIZE==(RTE_SITE_ROLE_FRAME_OVERHEAD+((size_t) RTE_EXAMPLE_MAX_TRAINS *(size_t) RTE_EXAMPLE_TRAIN_SESSION_WIRE_SIZE)+(size_t) RTE_EXAMPLE_DB_WIRE_SIZE)) &&(RTE_EXAMPLE_SITE_ROLE_VALID_MARKER==RTE_SITE_ROLE_VALID_MARKER) &&(RTE_EXAMPLE_SINGLE_MODE_FLAG_SINGLE==RTE_SITE_ROLE_SINGLE_MODE_SINGLE)) ? 1 :-1]

Definition at line 61 of file ab_gp_channel_negotiate.c.

Function Documentation

◆ encode_sessions_field()

rte_status_t encode_sessions_field ( const void * data,
uint8_t * out,
size_t size )
static

Definition at line 77 of file ab_gp_channel_negotiate.c.

◆ decode_sessions_field()

rte_status_t decode_sessions_field ( void * data,
const uint8_t * in,
size_t size )
static

Definition at line 85 of file ab_gp_channel_negotiate.c.

◆ encode_db_field()

rte_status_t encode_db_field ( const void * data,
uint8_t * out,
size_t size )
static

Definition at line 92 of file ab_gp_channel_negotiate.c.

◆ decode_db_field()

rte_status_t decode_db_field ( void * data,
const uint8_t * in,
size_t size )
static

Definition at line 98 of file ab_gp_channel_negotiate.c.

◆ build_state_transfer_registry()

void build_state_transfer_registry ( rte_state_transfer_registry_t * registry,
negotiate_state_t * state )
static

Registry over state->sessions / state->db. Both fields are always registered, whatever standby_mode says: STANDBY computes nothing of its own, so a promoted site that did not receive them would forget every in-flight MA.

Definition at line 105 of file ab_gp_channel_negotiate.c.

◆ encode_state()

rte_status_t encode_state ( void * user,
uint8_t * out,
size_t size,
bool live )
static

rte_site_role encode callback: live state while ONLINE, an empty state otherwise.

Definition at line 117 of file ab_gp_channel_negotiate.c.

◆ receive_state()

bool receive_state ( void * user,
const uint8_t * in,
size_t size,
bool valid )
static

rte_site_role receive callback: decodes the counterpart's state region and keeps it when it is a valid snapshot.

Definition at line 134 of file ab_gp_channel_negotiate.c.

◆ read_transfer_policy()

rte_example_transfer_policy_t read_transfer_policy ( void )
static

Parses RTE_EXAMPLE_TRANSFER_POLICY_ENV; unset/unrecognized -> CONTINUE.

Definition at line 157 of file ab_gp_channel_negotiate.c.

◆ ask_user_restart_or_continue()

bool ask_user_restart_or_continue ( const ab_gp_channel_context_t * ctx,
uint32_t transferred_cycle )
static

ASK_USER policy: bounded select()-on-stdin prompt for 'r' (restart) / 'c' (continue); no TTY or no answer in RTE_EXAMPLE_TRANSFER_ASK_USER_TIMEOUT_MS -> continue.

Returns
true to restart (zero the transferred cycle), false to continue.

Definition at line 179 of file ab_gp_channel_negotiate.c.

◆ apply_state_transfer()

void apply_state_transfer ( ab_gp_channel_context_t * ctx,
const rte_site_role_change_t * change )
static

Takes over the counterpart's snapshot on promotion (ADR-029): sessions and db always, the cycle number under the configured policy; without a valid snapshot this channel starts cold.

Definition at line 226 of file ab_gp_channel_negotiate.c.

◆ on_role_change()

void on_role_change ( void * user,
const rte_site_role_change_t * change )
static

rte_site_role change callback.

Definition at line 286 of file ab_gp_channel_negotiate.c.

◆ trace()

void trace ( void * user,
const char * line )
static

Definition at line 304 of file ab_gp_channel_negotiate.c.

◆ delay_ms()

void delay_ms ( void * user,
uint32_t ms )
static

Definition at line 310 of file ab_gp_channel_negotiate.c.

◆ local_facts()

void local_facts ( const ab_gp_channel_context_t * ctx,
rte_site_role_local_t * out )
static

Definition at line 318 of file ab_gp_channel_negotiate.c.

◆ sibling_source()

bool sibling_source ( void * user,
rte_site_role_sibling_t * out )
static

rte_site_role_sibling_source_fn (REQ-SITEROLE-016): the sibling's site-state frame stored since the last poll.

Definition at line 330 of file ab_gp_channel_negotiate.c.

◆ configure_takeover_key()

rte_status_t configure_takeover_key ( ab_gp_channel_context_t * ctx,
rte_site_role_config_t * cfg )
static

Definition at line 337 of file ab_gp_channel_negotiate.c.

◆ send_startup_ts_frame()

void send_startup_ts_frame ( ab_gp_channel_context_t * ctx,
bool is_reply,
rte_timestamp_ms_t ts_ms )
static

Sends one PEER_MSG_KIND_STARTUP_TS frame to the A/B sibling. RTE_STATUS_NOT_CONNECTED (A's listen role before B has connected) is expected and not logged: the exchange re-sends its request, a later reply is best effort.

Definition at line 368 of file ab_gp_channel_negotiate.c.

◆ exchange_startup_timestamp()

void exchange_startup_timestamp ( ab_gp_channel_context_t * ctx)
static

Agrees the site's startup timestamp with the A/B sibling over the peer link, before rte_site_role_init() (step 5 of main.c; the per-cycle peer pump is not running yet, so this is the only reader of ctx->com.peer_channel here). Every AB_GP_STARTUP_TS_REQUEST_PERIOD_MS a request carrying this process's own clock is sent, and the link is read in AB_GP_STARTUP_TS_READ_SLICE_MS slices for at most AB_GP_STARTUP_TS_WAIT_MS. The first STARTUP_TS frame ends the wait: ctx->startup_timestamp_ms = ab_gp_startup_timestamp_agree(own, sibling, true), and a request (not a reply) is answered once with the agreed value. SAF_RB / KM114_DIGEST frames seen meanwhile are deferred for pre_execute exactly as poll_for_checkpoint_frame() does; every other kind is per-cycle data and dropped. On timeout the own clock is used. Bounded by construction, since it runs before the watchdog (step 8) exists: the loop ends after AB_GP_STARTUP_TS_MAX_SLICES read slices whatever the clock says, and a failed rte_timer_now() inside the loop ends it at once (one ERROR event, own clock used). Worst case about AB_GP_STARTUP_TS_WAIT_MS plus one RTE_EXAMPLE_LINK_TIMEOUT_MS send/accept window of the lazily opened link plus one slice.

Definition at line 390 of file ab_gp_channel_negotiate.c.

◆ ab_gp_channel_negotiate_on_startup_ts_frame()

void ab_gp_channel_negotiate_on_startup_ts_frame ( ab_gp_channel_context_t * ctx,
const uint8_t * payload,
size_t size )

Handles a PEER_MSG_KIND_STARTUP_TS frame that arrives after ab_gp_channel_negotiate_init() returned (the A/B sibling restarted and asks for the site's startup timestamp - ADR-040 step 5a, ab_gp_startup_timestamp.h). The sibling's value is cached in ctx->sibling_startup_timestamp_ms; a request is answered with a reply carrying ctx->startup_timestamp_ms (not answered while that is 0: the clock was unavailable at start and rte_site_role took its own clock), a reply is cached only. The cached value is not used by this channel: the agreed timestamp of a running channel never changes (rte_site_role resumes it across reconnects). A malformed payload is dropped. Called from pre_execute.c's on_peer_frame_received() and ab_gp_channel_checkpoint.c's poll_for_checkpoint_frame().

Parameters
ctxInitialized context; NULL is ignored.
payloadThe frame's payload (the byte after the kind).
sizeNumber of payload bytes available.

Definition at line 532 of file ab_gp_channel_negotiate.c.

◆ ab_gp_channel_negotiate_init()

rte_status_t ab_gp_channel_negotiate_init ( ab_gp_channel_context_t * ctx)

Opens this channel's own inter-site negotiation link (WEST LISTENs, EAST CONNECTs), initializes the dual_channel/negotiator, and runs the startup tie-break until it settles - ported from SITE/site.c's own site_init(). Called once from ab_gp_channel_negotiate_setup() (AB/GP/com/ab_gp_channel.c), after the peer link is already up (the C relay links open later, in ab_gp_channel_relay_tasks_setup()).

Parameters
ctxContext to initialize ctx->neg_* fields into (including ctx->is_online, the startup outcome). Must not be NULL.
Returns
RTE_STATUS_OK on success; the underlying link/dual_channel/ negotiator failure status otherwise, or RTE_STATUS_TIMEOUT if negotiation did not settle within RTE_EXAMPLE_CONNECT_TIMEOUT_MS.

Definition at line 554 of file ab_gp_channel_negotiate.c.

◆ ab_gp_channel_negotiate_execute()

void ab_gp_channel_negotiate_execute ( ab_gp_channel_context_t * ctx,
uint32_t cycle )

Runs one cycle's worth of inter-site negotiation: drives the negotiator's own STATE-frame exchange (diagnostic only), sends this channel's current {faulted, single_mode, state-transfer snapshot} extra payload to its counterpart and receives theirs, then applies the promotion/demotion/both-faulted-deadlock rules - ported from SITE/site.c's own site_execute() body, minus the heartbeat-tick wait (this now runs on ab_gp_channel.c's own AB cycle tick instead of a separate timer).

Parameters
ctxInitialized context. Must not be NULL.
cycleThis AB cycle's number, for logging only.

Definition at line 625 of file ab_gp_channel_negotiate.c.

◆ ab_gp_channel_negotiate_service_link()

void ab_gp_channel_negotiate_service_link ( ab_gp_channel_context_t * ctx)

Fast internal-cycle service of the negotiation link (warm/hot standby only).

Called every ~10 ms from the wait for the next RBC cycle tick (ab_gp_channel_wait_cycle_tick()), so the peer's frames are received and acknowledged promptly instead of once per RBC cycle. Runs in every standby mode and on both roles; does nothing while the link is down.

Definition at line 637 of file ab_gp_channel_negotiate.c.

◆ ab_gp_channel_negotiate_sync_standby()

void ab_gp_channel_negotiate_sync_standby ( ab_gp_channel_context_t * ctx,
uint32_t cycle )

ONLINE only: send this cycle's post-compute snapshot to STANDBY and wait (bounded by the link's ack timeout) for its acknowledgement. Called from post_execute, i.e. before the staged outputs are committed at the checkpoint stage.

A missing acknowledgement marks STANDBY as not working; ONLINE then stops waiting and retries only every RTE_SITE_ROLE_STANDBY_SYNC_RETRY_CYCLES cycles. Never withholds output. Does nothing in cold mode.

Definition at line 645 of file ab_gp_channel_negotiate.c.

◆ ab_gp_channel_negotiate_on_takeover_confirm()

void ab_gp_channel_negotiate_on_takeover_confirm ( ab_gp_channel_context_t * ctx,
const char * payload )

Records a dispatcher confirmation that the other site is not ONLINE (from Gateway, RBC_UTIL_TOPIC_TAKEOVER_CONFIRM). It only enables a promotion while this channel is STANDBY and its counterpart is unresponsive; it is ignored (and dropped) otherwise, and expires after RTE_SITE_ROLE_TAKEOVER_CONFIRM_VALID_CYCLES cycles. A payload token=<hex> is checked as an authenticated token (ADR-040 step 4b, ab_gp_takeover_key.h); any other payload takes the unauthenticated path, refused when RTE_TAKEOVER_KEY_FILE is set.

Definition at line 653 of file ab_gp_channel_negotiate.c.

◆ ab_gp_channel_negotiate_flush_faulted()

void ab_gp_channel_negotiate_flush_faulted ( ab_gp_channel_context_t * ctx)

Best-effort, synchronous final send of this channel's own {faulted=true, single_mode} extra payload to its inter-site counterpart, BEFORE the negotiation link is torn down. Call this immediately after setting ctx->own_faulted = true and BEFORE ab_gp_channel_shutdown() (which closes the link) - see ab_gp_channel_crosscompare.c's react_to_fault().

Without this, a faulting channel's own promotion signal (ctx->own_faulted) would never reach its counterpart at all: the normal per-cycle negotiate step (ab_gp_channel_negotiate_execute()) never gets another chance to run once RTE_SAFESTATE_LEVEL_REBOOT diverts control away for good - unlike the old standalone SITE role (a separate, non-rebooting process that read this site's NVM role fresh every heartbeat regardless of A/B's own lifecycle), this channel's own negotiation link now shares A/B's lifecycle, so the fault has to be flushed out explicitly, once, right here, or the counterpart's promotion rule never fires. Failure is logged only - there is nothing further to retry from a process that is about to reboot anyway.

Parameters
ctxInitialized context, with ctx->own_faulted already true. Must not be NULL.

Definition at line 673 of file ab_gp_channel_negotiate.c.

◆ ab_gp_channel_negotiate_shutdown()

void ab_gp_channel_negotiate_shutdown ( ab_gp_channel_context_t * ctx)

Idempotent: closes the negotiation link/watchdog. Called from ab_gp_channel_shutdown().

Definition at line 678 of file ab_gp_channel_negotiate.c.