12#include "rte/oal/log/rte_log.h"
13#include "rte/oal/memory/rte_mem_util.h"
14#include "rte/utils/safestate/rte_safestate.h"
49#define XCOMPARE_PAYLOAD_SIZE ((size_t)RTE_EXAMPLE_MAX_TRAINS * RTE_EXAMPLE_TRAIN_SESSION_WIRE_SIZE)
67#define RTE_EXAMPLE_XCOMPARE_FAULT_CYCLE_ENV "RTE_EXAMPLE_XCOMPARE_FAULT_CYCLE"
75 static bool parsed =
false;
76 static uint32_t cycle = UINT32_MAX;
85 long val = strtol(env, &end, 10);
87 if ((end != env) && (val >= 0))
89 cycle = (uint32_t)val;
105 RTE_EXAMPLE_LOG(
"[%s] cycle %u: RTE_EXAMPLE_XCOMPARE_FAULT_CYCLE reached - "
106 "corrupting own cross-compare payload (test-only fault injection)\n",
161 RTE_SAFESTATE(RTE_SAFESTATE_LEVEL_REBOOT, reason);
204 if (result != RTE_VOTING_DISAGREED)
212 RTE_EXAMPLE_LOG(
"[%s] cycle %u: unexpected cross-compare result %d (not DISAGREED) - ignoring\n", ctx->
role_tag,
215 ctx->
is_role_a ?
"B" :
"A",
"XCOMPARE",
"unexpected non-DISAGREED result from comparator",
223 RTE_EXAMPLE_LOG(
"[%s] cycle %u: DISAGREE (first differing train index=%d) - this site's role -> FAULTED, entering REBOOT\n",
227 (void)snprintf(extra,
sizeof(extra),
"train_index=%d", bad_index);
229 ctx->
is_role_a ?
"B" :
"A",
"DISAGREE",
"FAULTED, entering REBOOT", extra);
239 rte_cross_comparator_config_t xcompare_cfg;
241 rte_mem_set(&xcompare_cfg, 0,
sizeof(xcompare_cfg));
242 xcompare_cfg.log_disagreements =
false;
248 xcompare_cfg.safestate_level = RTE_SAFESTATE_LEVEL_REBOOT;
251 xcompare_cfg.disagreement_context = ctx;
257 return rte_cross_comparator_init(&ctx->
xcompare_cmp, &xcompare_cfg);
301 if ((!local_session->
in_use) && (!peer_session->
in_use))
328 RTE_EXAMPLE_LOG(
"[%s] cycle %u: train index %u missing here but held by the A/B sibling - adopted its session "
329 "(joined late)\n", ctx->
role_tag, cycle, (
unsigned int)i);
331 ctx->
is_role_a ?
"B" :
"A",
"XCOMPARE",
"adopted a missed train session from the sibling", NULL);
342 fields_match = (local_session->
in_use == peer_session->
in_use)
344 && (local_session->
cycle == peer_session->
cycle)
350 RTE_EXAMPLE_LOG(
"[%s] cycle %u: xcompare train index %u: local(id=%u,pos=%u,ma_len=%u,seq=%u,ack=%d) "
351 "vs peer(id=%u,pos=%u,ma_len=%u,seq=%u,ack=%d) -> %s\n",
352 ctx->
role_tag, cycle, (
unsigned int)i,
353 (
unsigned int)local_session->
train_id, (
unsigned int)local_session->
d_lrbg,
355 (
unsigned int)peer_session->
train_id, (
unsigned int)peer_session->
d_lrbg,
357 fields_match ?
"MATCH" :
"DIFF");
367 RTE_EXAMPLE_LOG(
"[%s] cycle %u: train index %u not yet synchronized with peer (skip %u/%u) - "
368 "skipping cross-compare this cycle\n",
373 "train not yet synchronized with peer - skipping cross-compare", NULL);
376 RTE_EXAMPLE_LOG(
"[%s] cycle %u: train index %u still not synchronized with peer after %u skips - "
377 "treating as a genuine divergence, not a timing race\n",
378 ctx->
role_tag, cycle, (
unsigned int)i,
401 size_t agreed_size = 0U;
402 rte_voting_result_t result = RTE_VOTING_AGREED;
407 status = rte_cross_comparator_execute_buffers(&ctx->
xcompare_cmp, local_bytes, peer_bytes,
410 if (status == RTE_STATUS_OK)
413 RTE_EXAMPLE_LOG(
"[%s] cycle %u: cross-compare consensus result: AGREE (%u train session(s))\n", ctx->
role_tag, cycle,
416 ctx->
is_role_a ?
"B" :
"A",
"AGREE",
"sessions match", NULL);
438 if ((!ctx->
is_online) || (!have_peer_data))
440 RTE_EXAMPLE_LOG(
"[%s] cycle %u: not online and/or no peer data yet - skipping cross-compare this cycle\n",
443 ctx->
is_role_a ?
"B" :
"A",
"XCOMPARE",
"not online/no peer data yet this cycle - skipping cross-compare",
445 return RTE_STATUS_OK;
455 return RTE_STATUS_OK;
465 RTE_EXAMPLE_LOG(
"[%s] cycle %u: peer answered for a too-different cycle (%u) - skipping cross-compare this cycle\n",
469 (void)snprintf(extra,
sizeof(extra),
"PeerCycle=%u", peer_cycle);
472 "peer answered for a too-different cycle - skipping cross-compare", extra);
480 RTE_EXAMPLE_LOG(
"[%s] cycle %u: peer cycle number off by more than the tolerance for %u cycles - "
481 "cross-compare impossible, entering REBOOT\n",
485 return RTE_STATUS_OK;
492 return RTE_STATUS_OK;
498 return RTE_STATUS_OK;
506 if (rte_site_role_sibling_verdict(&ctx->
site_role) == RTE_SITE_ROLE_SIBLING_FAULT)
rte_status_t ab_gp_channel_shutdown(void *context)
rte_appmanager_operations_t::shutdown hook.
void ab_gp_channel_raise_ctc_alarm(ab_gp_channel_handle_t handle, rbc_alarm_severity_t severity, uint32_t code, const char *log_message)
Raises a severity-leveled operational alarm: logs it locally (rte_log_write_event(),...
static void execute_cross_comparator(ab_gp_channel_context_t *ctx, uint32_t cycle)
Route the actual AGREED/DISAGREED decision through the real rte_cross_comparator_t (ADR-025).
rte_status_t ab_gp_channel_crosscompare_init(ab_gp_channel_context_t *ctx)
Creates this channel's rte_cross_comparator_t. Called once from ab_gp_channel_crosscompare_setup() (A...
#define XCOMPARE_PAYLOAD_SIZE
static void xcompare_on_disagreement(void *context, rte_voting_result_t result)
static void react_to_fault(ab_gp_channel_context_t *ctx, rte_safestate_reason_t reason)
static void fault_cleanup(ab_gp_channel_context_t *ctx, rte_safestate_reason_t reason)
static int first_disagreeing_train_index(const ab_gp_channel_context_t *ctx)
static void encode_local_payload(const ab_gp_channel_context_t *ctx, uint8_t out[XCOMPARE_PAYLOAD_SIZE])
static bool per_train_readiness_gate(ab_gp_channel_context_t *ctx, uint32_t cycle)
ADR-029 per-train readiness gate: skip cross-compare this cycle for any train whose local/peer sessio...
rte_status_t ab_gp_channel_crosscompare_execute(ab_gp_channel_context_t *ctx)
Runs one cycle's cross-compare decision - see this file's own header. This is ab_gp_channel....
static uint32_t xcompare_fault_cycle(void)
#define RTE_EXAMPLE_XCOMPARE_FAULT_CYCLE_ENV
static void encode_peer_payload(const ab_gp_channel_context_t *ctx, uint8_t out[XCOMPARE_PAYLOAD_SIZE])
void ab_gp_channel_crosscompare_check_site_state(ab_gp_channel_context_t *ctx)
Reacts to the A/B role agreement verdict: this channel's own independently-negotiated {online,...
The actual A-vs-B cross-compare decision: does this cycle's own M24/M15 answer match what the peer in...
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}...
This channel's own West<->East inter-site negotiation link - see ab_gp_channel.c's file header for ho...
void ab_gp_channel_payload_encode_collector(ab_gp_channel_payload_collector_id_t id, uint8_t *out, const void *items)
Encodes one collector's own items into its own slice of a combined buffer - used both standalone (e....
Generic, data-blind wire framing for the A<->B peer link's AB_SAMPLE frame (CRC-64-protected,...
bool ab_gp_session_adopt_allowed(uint32_t join_left, bool local_in_use, bool peer_in_use)
Whether a channel that has just become ONLINE adopts one train session from its A/B sibling.
GP-specific standby and site-link constants, the session-adoption rule and the stall rule.
uint32_t ab_gp_train_payload_collector_id(void)
#define RTE_EXAMPLE_TRAIN_SESSION_WIRE_SIZE
#define RTE_EXAMPLE_REASON_AB_PEER_LINK_DOWN
#define RTE_EXAMPLE_LOG(...)
#define RTE_EXAMPLE_REASON_AB_SITE_STATE_MISMATCH
#define RTE_EXAMPLE_AB_CYCLE_SKEW_TOLERANCE
#define RTE_EXAMPLE_MAX_TRAINS
#define RTE_EXAMPLE_XCOMPARE_SYNC_SKIP_LIMIT
#define RTE_EXAMPLE_REASON_AB_MTYPE_DISAGREE
@ RBC_ALARM_SEVERITY_ERROR
const char * site_config_name(rte_example_site_t site)
Central application context owned by composition root (main.c).
rte_site_role_t site_role
ab_gp_train_session_t sessions[RTE_EXAMPLE_MAX_TRAINS]
uint8_t adopt_pending[RTE_EXAMPLE_MAX_TRAINS]
uint32_t join_window_cycles_left
rte_cross_comparator_t xcompare_cmp
uint32_t xcompare_skew_skips
ab_gp_train_session_t cyc_peer_sessions[RTE_EXAMPLE_MAX_TRAINS]
uint8_t xcompare_skip_count[RTE_EXAMPLE_MAX_TRAINS]