1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2019-2021 Broadcom
6 #include <rte_common.h>
7 #include <rte_cycles.h>
8 #include <rte_malloc.h>
10 #include <rte_alarm.h>
13 #include "bnxt_tf_common.h"
14 #include "ulp_ha_mgr.h"
15 #include "ulp_flow_db.h"
17 /* Local only MACROs and defines that aren't exported */
18 #define ULP_HA_TIMER_THREAD (1 << 0)
19 #define ULP_HA_TIMER_IS_RUNNING(info) (!!((info)->flags & ULP_HA_TIMER_THREAD))
20 #define ULP_HA_TIMER_SEC 1
21 #define ULP_HA_WAIT_TIME (MS_PER_S / 10)
22 #define ULP_HA_WAIT_TIMEOUT (MS_PER_S * 2)
24 #define ULP_HA_IF_TBL_DIR TF_DIR_RX
25 #define ULP_HA_IF_TBL_TYPE TF_IF_TBL_TYPE_PROF_PARIF_ERR_ACT_REC_PTR
26 #define ULP_HA_IF_TBL_IDX 10
28 static void ulp_ha_mgr_timer_cancel(struct bnxt_ulp_context *ulp_ctx);
29 static int32_t ulp_ha_mgr_timer_start(void);
30 static void ulp_ha_mgr_timer_cb(void *arg);
31 static int32_t ulp_ha_mgr_app_type_set(struct bnxt_ulp_context *ulp_ctx,
32 enum ulp_ha_mgr_app_type app_type);
34 ulp_ha_mgr_region_set(struct bnxt_ulp_context *ulp_ctx,
35 enum ulp_ha_mgr_region region);
37 ulp_ha_mgr_state_set(struct bnxt_ulp_context *ulp_ctx,
38 enum ulp_ha_mgr_state state);
41 ulp_ha_mgr_state_set(struct bnxt_ulp_context *ulp_ctx,
42 enum ulp_ha_mgr_state state)
44 struct tf_set_if_tbl_entry_parms set_parms = { 0 };
49 if (ulp_ctx == NULL) {
50 BNXT_TF_DBG(ERR, "Invalid parms in state get.\n");
53 tfp = bnxt_ulp_cntxt_tfp_get(ulp_ctx, BNXT_ULP_SHARED_SESSION_NO);
55 BNXT_TF_DBG(ERR, "Unable to get the TFP.\n");
59 val = (uint32_t)state;
61 set_parms.dir = ULP_HA_IF_TBL_DIR;
62 set_parms.type = ULP_HA_IF_TBL_TYPE;
63 set_parms.data = (uint8_t *)&val;
64 set_parms.data_sz_in_bytes = sizeof(val);
65 set_parms.idx = ULP_HA_IF_TBL_IDX;
67 rc = tf_set_if_tbl_entry(tfp, &set_parms);
69 BNXT_TF_DBG(ERR, "Failed to write the HA state\n");
75 ulp_ha_mgr_region_set(struct bnxt_ulp_context *ulp_ctx,
76 enum ulp_ha_mgr_region region)
78 struct bnxt_ulp_ha_mgr_info *ha_info;
80 if (ulp_ctx == NULL) {
81 BNXT_TF_DBG(ERR, "Invalid params in ha region get.\n");
85 ha_info = bnxt_ulp_cntxt_ptr2_ha_info_get(ulp_ctx);
86 if (ha_info == NULL) {
87 BNXT_TF_DBG(ERR, "Unable to get ha info\n");
90 ha_info->region = region;
96 ulp_ha_mgr_app_type_set(struct bnxt_ulp_context *ulp_ctx,
97 enum ulp_ha_mgr_app_type app_type)
99 struct bnxt_ulp_ha_mgr_info *ha_info;
101 if (ulp_ctx == NULL) {
102 BNXT_TF_DBG(ERR, "Invalid Parms.\n");
106 ha_info = bnxt_ulp_cntxt_ptr2_ha_info_get(ulp_ctx);
107 if (ha_info == NULL) {
108 BNXT_TF_DBG(ERR, "Unable to get the ha info.\n");
111 ha_info->app_type = app_type;
117 * When a secondary opens, the timer is started and periodically checks for a
118 * close of the primary (state moved to SEC_TIMER_COPY).
120 * - The flow db must be locked to prevent flows from being added to the high
121 * region during a move.
122 * - Move the high entries to low
123 * - Set the region to low for subsequent flows
124 * - Switch our persona to Primary
125 * - Set the state to Primary Run
126 * - Release the flow db lock for flows to continue
129 ulp_ha_mgr_timer_cb(void *arg __rte_unused)
131 struct tf_move_tcam_shared_entries_parms mparms = { 0 };
132 struct bnxt_ulp_context *ulp_ctx;
133 enum ulp_ha_mgr_state curr_state;
137 ulp_ctx = bnxt_ulp_cntxt_entry_acquire();
138 if (ulp_ctx == NULL) {
139 BNXT_TF_DBG(INFO, "could not get the ulp context lock\n");
140 ulp_ha_mgr_timer_start();
144 rc = ulp_ha_mgr_state_get(ulp_ctx, &curr_state);
147 * This shouldn't happen, if it does, resetart the timer
148 * and try again next time.
150 BNXT_TF_DBG(ERR, "On HA CB:Failed(%d) to get state.\n", rc);
153 if (curr_state != ULP_HA_STATE_SEC_TIMER_COPY)
156 /* Protect the flow database during the copy */
157 if (bnxt_ulp_cntxt_acquire_fdb_lock(ulp_ctx)) {
158 /* Should not fail, if we do, restart timer and try again */
159 BNXT_TF_DBG(ERR, "Flow db lock acquire failed\n");
162 /* All paths after this point must release the fdb lock */
164 /* The Primary has issued a close and we are in the timer copy
165 * phase. Become the new Primary, Set state to Primary Run and
166 * move WC entries to Low Region.
168 BNXT_TF_DBG(INFO, "On HA CB: Moving entries HI to LOW\n");
169 mparms.dir = TF_DIR_RX;
170 mparms.tcam_tbl_type = TF_TCAM_TBL_TYPE_WC_TCAM_HIGH;
171 tfp = bnxt_ulp_cntxt_tfp_get(ulp_ctx, BNXT_ULP_SHARED_SESSION_YES);
173 BNXT_TF_DBG(ERR, "On HA CB: Unable to get the TFP.\n");
177 rc = tf_move_tcam_shared_entries(tfp, &mparms);
179 BNXT_TF_DBG(ERR, "On HA_CB: Failed to move entries\n");
183 ulp_ha_mgr_region_set(ulp_ctx, ULP_HA_REGION_LOW);
184 ulp_ha_mgr_app_type_set(ulp_ctx, ULP_HA_APP_TYPE_PRIM);
185 ulp_ha_mgr_state_set(ulp_ctx, ULP_HA_STATE_PRIM_RUN);
186 BNXT_TF_DBG(INFO, "On HA CB: SEC[SEC_TIMER_COPY] => PRIM[PRIM_RUN]\n");
188 bnxt_ulp_cntxt_release_fdb_lock(ulp_ctx);
189 bnxt_ulp_cntxt_entry_release();
192 bnxt_ulp_cntxt_entry_release();
193 ulp_ha_mgr_timer_start();
197 ulp_ha_mgr_timer_start(void)
199 rte_eal_alarm_set(US_PER_S * ULP_HA_TIMER_SEC,
200 ulp_ha_mgr_timer_cb, NULL);
205 ulp_ha_mgr_timer_cancel(struct bnxt_ulp_context *ulp_ctx)
207 struct bnxt_ulp_ha_mgr_info *ha_info;
209 ha_info = bnxt_ulp_cntxt_ptr2_ha_info_get(ulp_ctx);
210 if (ha_info == NULL) {
211 BNXT_TF_DBG(ERR, "Unable to get ha info\n");
215 ha_info->flags &= ~ULP_HA_TIMER_THREAD;
216 rte_eal_alarm_cancel(ulp_ha_mgr_timer_cb, (void *)ulp_ctx);
220 ulp_ha_mgr_init(struct bnxt_ulp_context *ulp_ctx)
222 struct bnxt_ulp_ha_mgr_info *ha_info;
224 ha_info = rte_zmalloc("ulp_ha_mgr_info", sizeof(*ha_info), 0);
228 /* Add the HA info tbl to the ulp context. */
229 bnxt_ulp_cntxt_ptr2_ha_info_set(ulp_ctx, ha_info);
231 rc = pthread_mutex_init(&ha_info->ha_lock, NULL);
233 PMD_DRV_LOG(ERR, "Failed to initialize ha mutex\n");
240 ulp_ha_mgr_deinit(ulp_ctx);
245 ulp_ha_mgr_deinit(struct bnxt_ulp_context *ulp_ctx)
247 struct bnxt_ulp_ha_mgr_info *ha_info;
249 ha_info = bnxt_ulp_cntxt_ptr2_ha_info_get(ulp_ctx);
250 if (ha_info == NULL) {
251 BNXT_TF_DBG(ERR, "Unable to get HA Info for deinit.\n");
255 pthread_mutex_destroy(&ha_info->ha_lock);
258 bnxt_ulp_cntxt_ptr2_ha_info_set(ulp_ctx, NULL);
262 ulp_ha_mgr_app_type_get(struct bnxt_ulp_context *ulp_ctx,
263 enum ulp_ha_mgr_app_type *app_type)
265 struct bnxt_ulp_ha_mgr_info *ha_info;
267 if (ulp_ctx == NULL || app_type == NULL) {
268 BNXT_TF_DBG(ERR, "Invalid Parms.\n");
272 ha_info = bnxt_ulp_cntxt_ptr2_ha_info_get(ulp_ctx);
273 if (ha_info == NULL) {
274 BNXT_TF_DBG(ERR, "Unable to get the HA info.\n");
277 *app_type = ha_info->app_type;
283 ulp_ha_mgr_state_get(struct bnxt_ulp_context *ulp_ctx,
284 enum ulp_ha_mgr_state *state)
286 struct tf_get_if_tbl_entry_parms get_parms = { 0 };
291 if (ulp_ctx == NULL || state == NULL) {
292 BNXT_TF_DBG(ERR, "Invalid parms in state get.\n");
295 tfp = bnxt_ulp_cntxt_tfp_get(ulp_ctx, BNXT_ULP_SHARED_SESSION_NO);
297 BNXT_TF_DBG(ERR, "Unable to get the TFP.\n");
301 get_parms.dir = ULP_HA_IF_TBL_DIR;
302 get_parms.type = ULP_HA_IF_TBL_TYPE;
303 get_parms.idx = ULP_HA_IF_TBL_IDX;
304 get_parms.data = (uint8_t *)&val;
305 get_parms.data_sz_in_bytes = sizeof(val);
307 rc = tf_get_if_tbl_entry(tfp, &get_parms);
309 BNXT_TF_DBG(ERR, "Failed to read the HA state\n");
316 ulp_ha_mgr_open(struct bnxt_ulp_context *ulp_ctx)
318 enum ulp_ha_mgr_state curr_state;
321 rc = ulp_ha_mgr_state_get(ulp_ctx, &curr_state);
323 BNXT_TF_DBG(ERR, "Failed to get HA state on Open (%d)\n", rc);
328 * An Open can only occur during the Init and Primary Run states. During
329 * Init, the system attempting to Open will become the only system
330 * running. During Primary Run, the system attempting to Open will
331 * become the secondary system temporarily, and should eventually be
332 * transitioned to the primary system.
334 switch (curr_state) {
335 case ULP_HA_STATE_INIT:
337 * No system is running, as we are the primary. Since no other
338 * system is running, we start writing into the low region. By
339 * writing into the low region, we save room for the secondary
340 * system to override our entries by using the high region.
342 ulp_ha_mgr_app_type_set(ulp_ctx, ULP_HA_APP_TYPE_PRIM);
343 ulp_ha_mgr_region_set(ulp_ctx, ULP_HA_REGION_LOW);
344 rc = ulp_ha_mgr_state_set(ulp_ctx, ULP_HA_STATE_PRIM_RUN);
346 BNXT_TF_DBG(ERR, "On Open: Failed to set PRIM_RUN.\n");
350 BNXT_TF_DBG(INFO, "On Open: [INIT] => PRIM[PRIM_RUN]\n");
352 case ULP_HA_STATE_PRIM_RUN:
354 * The secondary system is starting in order to take over.
355 * The current primary is expected to eventually close and pass
356 * full control to this system;however, until the primary closes
357 * both are operational.
359 * The timer is started in order to determine when the
360 * primary has closed.
362 ulp_ha_mgr_app_type_set(ulp_ctx, ULP_HA_APP_TYPE_SEC);
363 ulp_ha_mgr_region_set(ulp_ctx, ULP_HA_REGION_HI);
367 * Clear the high region so the secondary can begin overriding
368 * the current entries.
370 rc = ulp_ha_mgr_timer_start();
372 BNXT_TF_DBG(ERR, "Unable to start timer on HA Open.\n");
376 rc = ulp_ha_mgr_state_set(ulp_ctx, ULP_HA_STATE_PRIM_SEC_RUN);
378 BNXT_TF_DBG(ERR, "On Open: Failed to set PRIM_SEC_RUN\n");
381 BNXT_TF_DBG(INFO, "On Open: [PRIM_RUN] => [PRIM_SEC_RUN]\n");
384 BNXT_TF_DBG(ERR, "On Open: Unknown state 0x%x\n", curr_state);
392 ulp_ha_mgr_close(struct bnxt_ulp_context *ulp_ctx)
394 enum ulp_ha_mgr_state curr_state, next_state, poll_state;
395 enum ulp_ha_mgr_app_type app_type;
399 rc = ulp_ha_mgr_state_get(ulp_ctx, &curr_state);
401 BNXT_TF_DBG(ERR, "On Close: Failed(%d) to get HA state\n", rc);
405 rc = ulp_ha_mgr_app_type_get(ulp_ctx, &app_type);
407 BNXT_TF_DBG(ERR, "On Close: Failed to get the app type.\n");
411 if (curr_state == ULP_HA_STATE_PRIM_RUN &&
412 app_type == ULP_HA_APP_TYPE_PRIM) {
414 * Only the primary is running, so a close effectively moves the
415 * system back to INIT.
417 next_state = ULP_HA_STATE_INIT;
418 ulp_ha_mgr_state_set(ulp_ctx, next_state);
419 BNXT_TF_DBG(INFO, "On Close: PRIM[PRIM_RUN] => [INIT]\n");
420 } else if (curr_state == ULP_HA_STATE_PRIM_SEC_RUN &&
421 app_type == ULP_HA_APP_TYPE_PRIM) {
423 * While both are running, the primary received a close.
424 * Cleanup the flows, set the COPY state, and wait for the
425 * secondary to become the Primary.
428 "On Close: PRIM[PRIM_SEC_RUN] flushing flows.\n");
430 ulp_flow_db_flush_flows(ulp_ctx, BNXT_ULP_FDB_TYPE_REGULAR);
431 ulp_ha_mgr_state_set(ulp_ctx, ULP_HA_STATE_SEC_TIMER_COPY);
434 * TODO: This needs to be bounded in case the other system does
435 * not move to PRIM_RUN.
438 "On Close: PRIM[PRIM_SEC_RUN] => [Copy], enter wait.\n");
439 timeout = ULP_HA_WAIT_TIMEOUT;
441 rte_delay_ms(ULP_HA_WAIT_TIME);
442 rc = ulp_ha_mgr_state_get(ulp_ctx, &poll_state);
445 "Failed to get HA state on Close (%d)\n",
449 timeout -= ULP_HA_WAIT_TIME;
451 "On Close: Waiting %d ms for PRIM_RUN\n",
453 } while (poll_state != ULP_HA_STATE_PRIM_RUN && timeout > 0);
456 BNXT_TF_DBG(ERR, "On Close: SEC[COPY] Timed out\n");
460 BNXT_TF_DBG(INFO, "On Close: PRIM[PRIM_SEC_RUN] => [COPY]\n");
461 } else if (curr_state == ULP_HA_STATE_PRIM_SEC_RUN &&
462 app_type == ULP_HA_APP_TYPE_SEC) {
464 * While both are running, the secondary unexpectedly received a
465 * close. Cancel the timer, set the state to Primary RUN since
466 * it is the only one running.
468 ulp_ha_mgr_timer_cancel(ulp_ctx);
469 ulp_ha_mgr_state_set(ulp_ctx, ULP_HA_STATE_PRIM_RUN);
471 BNXT_TF_DBG(INFO, "On Close: SEC[PRIM_SEC_RUN] => [PRIM_RUN]\n");
472 } else if (curr_state == ULP_HA_STATE_SEC_TIMER_COPY &&
473 app_type == ULP_HA_APP_TYPE_SEC) {
475 * While both were running and the Secondary went into copy,
476 * secondary received a close. Wait until the former Primary
477 * clears the copy stage, close, and set to INIT.
479 BNXT_TF_DBG(INFO, "On Close: SEC[COPY] wait for PRIM_RUN\n");
481 timeout = ULP_HA_WAIT_TIMEOUT;
483 rte_delay_ms(ULP_HA_WAIT_TIME);
484 rc = ulp_ha_mgr_state_get(ulp_ctx, &poll_state);
487 "Failed to get HA state on Close (%d)\n",
492 timeout -= ULP_HA_WAIT_TIME;
494 "On Close: Waiting %d ms for PRIM_RUN\n",
496 } while (poll_state != ULP_HA_STATE_PRIM_RUN &&
501 "On Close: SEC[COPY] Timed out\n");
505 next_state = ULP_HA_STATE_INIT;
506 rc = ulp_ha_mgr_state_set(ulp_ctx, next_state);
509 "On Close: Failed to set state to INIT(%x)\n",
515 "On Close: SEC[COPY] => [INIT] after %d ms\n",
516 ULP_HA_WAIT_TIMEOUT - timeout);
518 BNXT_TF_DBG(ERR, "On Close: Invalid type/state %d/%d\n",
519 curr_state, app_type);
526 ulp_ha_mgr_region_get(struct bnxt_ulp_context *ulp_ctx,
527 enum ulp_ha_mgr_region *region)
529 struct bnxt_ulp_ha_mgr_info *ha_info;
531 if (ulp_ctx == NULL || region == NULL) {
532 BNXT_TF_DBG(ERR, "Invalid params in ha region get.\n");
536 ha_info = bnxt_ulp_cntxt_ptr2_ha_info_get(ulp_ctx);
537 if (ha_info == NULL) {
538 BNXT_TF_DBG(ERR, "Unable to get ha info\n");
541 *region = ha_info->region;