|
RTE RBC 2oo2 Reference Application 0.1.0 build 0006685
SIL 4 2oo2 Dual-Site Fault-Tolerant ERTMS Radio Block Centre Reference Architecture
|
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"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) |
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):
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.
| #define _POSIX_C_SOURCE 200809L |
Definition at line 21 of file ab_gp_channel_negotiate.c.
| #define AB_GP_TAKEOVER_CHALLENGE_MS 120000U |
Definition at line 50 of file ab_gp_channel_negotiate.c.
| #define AB_GP_STARTUP_TS_REQUEST_PERIOD_MS 100U |
Definition at line 55 of file ab_gp_channel_negotiate.c.
| 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.
| 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.
|
static |
Definition at line 77 of file ab_gp_channel_negotiate.c.
|
static |
Definition at line 85 of file ab_gp_channel_negotiate.c.
|
static |
Definition at line 92 of file ab_gp_channel_negotiate.c.
|
static |
Definition at line 98 of file ab_gp_channel_negotiate.c.
|
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.
|
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.
|
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.
|
static |
Parses RTE_EXAMPLE_TRANSFER_POLICY_ENV; unset/unrecognized -> CONTINUE.
Definition at line 157 of file ab_gp_channel_negotiate.c.
|
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.
Definition at line 179 of file ab_gp_channel_negotiate.c.
|
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.
|
static |
rte_site_role change callback.
Definition at line 286 of file ab_gp_channel_negotiate.c.
|
static |
Definition at line 304 of file ab_gp_channel_negotiate.c.
|
static |
Definition at line 310 of file ab_gp_channel_negotiate.c.
|
static |
Definition at line 318 of file ab_gp_channel_negotiate.c.
|
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.
|
static |
Definition at line 337 of file ab_gp_channel_negotiate.c.
|
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.
|
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.
| 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().
| ctx | Initialized context; NULL is ignored. |
| payload | The frame's payload (the byte after the kind). |
| size | Number of payload bytes available. |
Definition at line 532 of file ab_gp_channel_negotiate.c.
| 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()).
| ctx | Context to initialize ctx->neg_* fields into (including ctx->is_online, the startup outcome). Must not be NULL. |
Definition at line 554 of file ab_gp_channel_negotiate.c.
| 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).
| ctx | Initialized context. Must not be NULL. |
| cycle | This AB cycle's number, for logging only. |
Definition at line 625 of file ab_gp_channel_negotiate.c.
| 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.
| 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.
| 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.
| 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.
| ctx | Initialized context, with ctx->own_faulted already true. Must not be NULL. |
Definition at line 673 of file ab_gp_channel_negotiate.c.
| 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.