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_checkpoint.c
Go to the documentation of this file.
1
28
29#include "rte/oal/log/rte_log.h"
30#include "rte/oal/timer/rte_timer.h"
31
33#include "ab_gp_channel_types.h"
34#include "ab_gp_channel_util.h"
36#include "ab_gp_sibling_state.h"
37
39
46
50{
51 bool recent = false;
52
53 if ((ctx->last_peer_checkpoint_cycle != 0U) && (ctx->cyc_cycle >= ctx->last_peer_checkpoint_cycle))
54 {
55 uint32_t gap = ctx->cyc_cycle - ctx->last_peer_checkpoint_cycle;
56
57 recent = (gap <= (uint32_t)RTE_EXAMPLE_AB_CHECKPOINT_LOOKBACK_CYCLES);
58 }
59 return recent;
60}
61
63
64rte_status_t ab_gp_channel_checkpoint_backend_send(void *channel, const void *data, size_t data_size)
65{
67 uint8_t frame[PEER_WIRE_FRAME_SIZE] = { 0 };
68 rte_status_t status;
69
70 if (ctx == NULL || data == NULL || data_size != sizeof(rte_vital_message_t))
71 {
72 return RTE_STATUS_INVALID_PARAM;
73 }
74
75 /* Retain this cycle's own arrival verbatim: backend_recv() returns it
76 * to satisfy the lookback path (it verifies against this cycle's
77 * checkpoint_id by construction). */
78 rte_mem_copy(ctx->last_sent_checkpoint_arrival, data, sizeof(rte_vital_message_t));
80
82 rte_mem_copy(&frame[1], data, data_size);
83 status = rte_channel_service_send(&ctx->com.peer_channel, frame, sizeof(frame), RTE_EXAMPLE_LINK_TIMEOUT_MS);
84 rte_log_write_event(RTE_LOG_LEVEL_DEBUG, site_config_name(ctx->site), ctx->cyc_cycle, ctx->role_tag,
85 ctx->is_role_a ? "B" : "A", "CHECKPOINT_REQUEST",
86 (status == RTE_STATUS_OK) ? "sent" : rte_status_to_string(status), NULL);
87 return status;
88}
89
98static rte_status_t poll_for_checkpoint_frame(ab_gp_channel_context_t *ctx, void *data, size_t data_size,
99 uint32_t timeout_ms)
100{
101 rte_timestamp_ms_t start_ms = 0U;
102 rte_timestamp_ms_t now_ms = 0U;
103 uint8_t in_bytes[PEER_WIRE_FRAME_SIZE];
104
105 (void)rte_timer_now(&start_ms);
106 for (;;)
107 {
108 rte_duration_ms_t elapsed;
109 rte_duration_ms_t remaining;
110 rte_duration_ms_t poll_ms;
111 rte_status_t status;
112
113 (void)rte_timer_now(&now_ms);
114 elapsed = (rte_duration_ms_t)(now_ms - start_ms);
115 if (elapsed >= (rte_duration_ms_t)timeout_ms)
116 {
117 return RTE_STATUS_TIMEOUT;
118 }
119 remaining = (rte_duration_ms_t)timeout_ms - elapsed;
120 poll_ms = (remaining > 20U) ? 20U : remaining;
121
122 /* This wait can last most of a cycle (the two channels' cycles are not phase-locked). The inter-site
123 * negotiation link acknowledges a frame only from inside a receive call, so without servicing it here the
124 * other site's 50 ms ack window is missed for as long as this wait lasts, and for some start-up phase
125 * relations that happened every cycle: the link was torn down every third cycle for good (Docker). */
127 status = rte_channel_service_read(&ctx->com.peer_channel, in_bytes, sizeof(in_bytes), poll_ms);
128 if (status == RTE_STATUS_OK)
129 {
130 if (in_bytes[0] == PEER_MSG_KIND_CHECKPOINT_REQUEST || in_bytes[0] == PEER_MSG_KIND_CHECKPOINT_REPLY)
131 {
133 rte_mem_copy(data, &in_bytes[1], data_size);
134 if (in_bytes[0] == PEER_MSG_KIND_CHECKPOINT_REQUEST)
135 {
136 uint8_t reply_frame[PEER_WIRE_FRAME_SIZE] = { 0 };
137 reply_frame[0] = PEER_MSG_KIND_CHECKPOINT_REPLY;
138 rte_mem_copy(&reply_frame[1], &in_bytes[1], sizeof(rte_vital_message_t));
139 (void)rte_channel_service_send(&ctx->com.peer_channel, reply_frame, sizeof(reply_frame), RTE_EXAMPLE_LINK_TIMEOUT_MS);
140 }
141 return RTE_STATUS_OK;
142 }
143 if (in_bytes[0] == PEER_MSG_KIND_SITE_STATE)
144 {
145 (void)ab_gp_sibling_state_store(&ctx->peer_site_state, &in_bytes[1], sizeof(in_bytes) - 1U);
146 }
147 else if (in_bytes[0] == PEER_MSG_KIND_STARTUP_TS)
148 {
149 /* The sibling restarted and asks for the site's startup timestamp (ADR-040 step 5a): answered
150 * here, since the request would otherwise wait until the next pre_execute. */
151 ab_gp_channel_negotiate_on_startup_ts_frame(ctx, &in_bytes[1], sizeof(in_bytes) - 1U);
152 }
153 else if (((in_bytes[0] == PEER_MSG_KIND_SAF_RB) || (in_bytes[0] == PEER_MSG_KIND_KM114_DIGEST)) &&
155 {
156 /* One-off control frames: dropping one here left the other channel without a train's shared RB
157 * (or the KM114 cross-check without its digest) for good. Replayed by pre_execute. */
158 rte_mem_copy(ctx->deferred_peer_frames[ctx->deferred_peer_count], in_bytes, sizeof(in_bytes));
159 ctx->deferred_peer_count++;
160 }
161 else
162 {
163 /* other kinds are per-cycle data a later cycle re-sends */
164 }
165 }
166 else if (status != RTE_STATUS_TIMEOUT)
167 {
168 return status;
169 }
170 }
171}
172
173rte_status_t ab_gp_channel_checkpoint_backend_recv(void *channel, void *data, size_t data_size, uint32_t timeout_ms)
174{
176
177 if (ctx == NULL || data == NULL || data_size != sizeof(rte_vital_message_t))
178 {
179 return RTE_STATUS_INVALID_PARAM;
180 }
181
182 /* 1. Real matching peer frames the per-cycle pump already drained (see
183 * ab_gp_channel_types.h). The REPLY carries this side's id and
184 * confirms any idle cycle; the peer's REQUEST carries the peer's id
185 * and confirms an active cycle when both channels took the same
186 * path. A stale id is rejected by rte_checkpoint.c's
187 * reply_confirms_checkpoint(), which then retries within budget. */
189 {
190 ctx->has_cached_checkpoint_reply = false;
191 rte_mem_copy(data, ctx->cached_checkpoint_reply, data_size);
192 return RTE_STATUS_OK;
193 }
195 {
197 rte_mem_copy(data, ctx->cached_checkpoint_request, data_size);
198 return RTE_STATUS_OK;
199 }
200
201 /* 2. Lookback: a fresh round trip did not land in-window, but the peer
202 * is demonstrably still running its checkpoint stage (it exchanged a
203 * checkpoint frame with us within the last N cycles). Confirm this
204 * cycle using our own arrival - immune to the A/B cycle-phase drift
205 * that a loaded host causes. Bounded: once the peer goes N+1 cycles
206 * without any checkpoint frame this stops firing and the live poll
207 * below runs to its timeout, so a genuinely dead/partitioned peer
208 * still trips SAFE-state (REQ-CHECKPOINT-003), just one to two
209 * cycles later. */
211 {
212 rte_mem_copy(data, ctx->last_sent_checkpoint_arrival, data_size);
213 return RTE_STATUS_OK;
214 }
215
216 /* 3. Last-ditch live poll for whatever remains of this round's budget - see
217 * poll_for_checkpoint_frame()'s own doc. */
218 return poll_for_checkpoint_frame(ctx, data, data_size, timeout_ms);
219}
static rte_status_t poll_for_checkpoint_frame(ab_gp_channel_context_t *ctx, void *data, size_t data_size, uint32_t timeout_ms)
Step 3: last-ditch live poll for whatever remains of this round's budget.
static bool ab_gp_channel_checkpoint_peer_recent(const ab_gp_channel_context_t *ctx)
True if the peer exchanged a checkpoint frame with us within the last RTE_EXAMPLE_AB_CHECKPOINT_LOOKB...
rte_status_t ab_gp_channel_checkpoint_backend_send(void *channel, const void *data, size_t data_size)
rte_channel_send_fn for the checkpoint transport: wraps the given rte_vital_message_t in a PEER_MSG_K...
static void ab_gp_channel_checkpoint_note_peer_frame(ab_gp_channel_context_t *ctx)
Records that a CHECKPOINT_REQUEST/REPLY was just seen from the peer, for the lookback check in ab_gp_...
rte_status_t ab_gp_channel_checkpoint_backend_recv(void *channel, void *data, size_t data_size, uint32_t timeout_ms)
rte_channel_recv_fn for the checkpoint transport: waits (bounded by timeout_ms) for a PEER_MSG_KIND_C...
rte_channel_t OSAdapter for the built-in AppManager checkpoint (ADR-019 addendum, rewired onto rte_vo...
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_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 (...
This channel's own West<->East inter-site negotiation link - see ab_gp_channel.c's file header for ho...
Modular type definitions for AB channel components (Com, Train, DB, Redundancy).
#define PEER_WIRE_FRAME_SIZE
#define PEER_MSG_KIND_KM114_DIGEST
#define PEER_MSG_KIND_CHECKPOINT_REQUEST
#define PEER_MSG_KIND_STARTUP_TS
#define PEER_MSG_KIND_CHECKPOINT_REPLY
#define AB_GP_DEFERRED_PEER_FRAMES
Capacity of ab_gp_channel_context_t::deferred_peer_frames.
#define PEER_MSG_KIND_SITE_STATE
#define PEER_MSG_KIND_SAF_RB
Declares ab_gp_channel_sleep_ms() - see ab_gp_channel_types.h's own doc on why this tiny helper gets ...
bool ab_gp_sibling_state_store(ab_gp_sibling_state_t *st, const uint8_t *payload, size_t size)
Stores a received site-state frame payload: the rule of on_peer_frame_received() (the payload must ca...
The A/B sibling's last site-state frame (PEER_MSG_KIND_SITE_STATE), kept raw and handed to Platform_R...
#define RTE_EXAMPLE_AB_CHECKPOINT_LOOKBACK_CYCLES
#define RTE_EXAMPLE_LINK_TIMEOUT_MS
const char * site_config_name(rte_example_site_t site)
Definition site_config.c:52
Central application context owned by composition root (main.c).
uint8_t deferred_peer_frames[AB_GP_DEFERRED_PEER_FRAMES][PEER_WIRE_FRAME_SIZE]
ab_gp_sibling_state_t peer_site_state
uint8_t last_sent_checkpoint_arrival[sizeof(rte_vital_message_t)]
uint8_t cached_checkpoint_reply[sizeof(rte_vital_message_t)]
uint8_t cached_checkpoint_request[sizeof(rte_vital_message_t)]
rte_channel_service_t peer_channel