net/bnxt: add context list for timers
[dpdk.git] / drivers / net / bnxt / tf_ulp / ulp_ha_mgr.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2019-2021 Broadcom
3  * All rights reserved.
4  */
5
6 #include <rte_common.h>
7 #include <rte_cycles.h>
8 #include <rte_malloc.h>
9 #include <rte_log.h>
10 #include <rte_alarm.h>
11 #include "bnxt.h"
12 #include "bnxt_ulp.h"
13 #include "bnxt_tf_common.h"
14 #include "ulp_ha_mgr.h"
15 #include "ulp_flow_db.h"
16
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)
23
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
27
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);
33 static int32_t
34 ulp_ha_mgr_region_set(struct bnxt_ulp_context *ulp_ctx,
35                       enum ulp_ha_mgr_region region);
36 static int32_t
37 ulp_ha_mgr_state_set(struct bnxt_ulp_context *ulp_ctx,
38                      enum ulp_ha_mgr_state state);
39
40 static int32_t
41 ulp_ha_mgr_state_set(struct bnxt_ulp_context *ulp_ctx,
42                      enum ulp_ha_mgr_state state)
43 {
44         struct tf_set_if_tbl_entry_parms set_parms = { 0 };
45         struct tf *tfp;
46         uint32_t val = 0;
47         int32_t rc = 0;
48
49         if (ulp_ctx == NULL) {
50                 BNXT_TF_DBG(ERR, "Invalid parms in state get.\n");
51                 return -EINVAL;
52         }
53         tfp = bnxt_ulp_cntxt_tfp_get(ulp_ctx, BNXT_ULP_SHARED_SESSION_NO);
54         if (tfp == NULL) {
55                 BNXT_TF_DBG(ERR, "Unable to get the TFP.\n");
56                 return -EINVAL;
57         }
58
59         val = (uint32_t)state;
60
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;
66
67         rc = tf_set_if_tbl_entry(tfp, &set_parms);
68         if (rc)
69                 BNXT_TF_DBG(ERR, "Failed to write the HA state\n");
70
71         return rc;
72 }
73
74 static int32_t
75 ulp_ha_mgr_region_set(struct bnxt_ulp_context *ulp_ctx,
76                       enum ulp_ha_mgr_region region)
77 {
78         struct bnxt_ulp_ha_mgr_info *ha_info;
79
80         if (ulp_ctx == NULL) {
81                 BNXT_TF_DBG(ERR, "Invalid params in ha region get.\n");
82                 return -EINVAL;
83         }
84
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");
88                 return -EINVAL;
89         }
90         ha_info->region = region;
91
92         return 0;
93 }
94
95 static int32_t
96 ulp_ha_mgr_app_type_set(struct bnxt_ulp_context *ulp_ctx,
97                         enum ulp_ha_mgr_app_type app_type)
98 {
99         struct bnxt_ulp_ha_mgr_info *ha_info;
100
101         if (ulp_ctx == NULL) {
102                 BNXT_TF_DBG(ERR, "Invalid Parms.\n");
103                 return -EINVAL;
104         }
105
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");
109                 return -EINVAL;
110         }
111         ha_info->app_type = app_type;
112
113         return 0;
114 }
115
116 /*
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).
119  * In 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
127  */
128 static void
129 ulp_ha_mgr_timer_cb(void *arg __rte_unused)
130 {
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;
134         struct tf *tfp;
135         int32_t rc;
136
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();
141                 return;
142         }
143
144         rc = ulp_ha_mgr_state_get(ulp_ctx, &curr_state);
145         if (rc) {
146                 /*
147                  * This shouldn't happen, if it does, resetart the timer
148                  * and try again next time.
149                  */
150                 BNXT_TF_DBG(ERR, "On HA CB:Failed(%d) to get state.\n", rc);
151                 goto cb_restart;
152         }
153         if (curr_state != ULP_HA_STATE_SEC_TIMER_COPY)
154                 goto cb_restart;
155
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");
160                 goto cb_restart;
161         }
162         /* All paths after this point must release the fdb lock */
163
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.
167          */
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);
172         if (tfp == NULL) {
173                 BNXT_TF_DBG(ERR, "On HA CB: Unable to get the TFP.\n");
174                 goto unlock;
175         }
176
177         rc = tf_move_tcam_shared_entries(tfp, &mparms);
178         if (rc) {
179                 BNXT_TF_DBG(ERR, "On HA_CB: Failed to move entries\n");
180                 goto unlock;
181         }
182
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");
187 unlock:
188         bnxt_ulp_cntxt_release_fdb_lock(ulp_ctx);
189         bnxt_ulp_cntxt_entry_release();
190         return;
191 cb_restart:
192         bnxt_ulp_cntxt_entry_release();
193         ulp_ha_mgr_timer_start();
194 }
195
196 static int32_t
197 ulp_ha_mgr_timer_start(void)
198 {
199         rte_eal_alarm_set(US_PER_S * ULP_HA_TIMER_SEC,
200                           ulp_ha_mgr_timer_cb, NULL);
201         return 0;
202 }
203
204 static void
205 ulp_ha_mgr_timer_cancel(struct bnxt_ulp_context *ulp_ctx)
206 {
207         struct bnxt_ulp_ha_mgr_info *ha_info;
208
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");
212                 return;
213         }
214
215         ha_info->flags &= ~ULP_HA_TIMER_THREAD;
216         rte_eal_alarm_cancel(ulp_ha_mgr_timer_cb, (void *)ulp_ctx);
217 }
218
219 int32_t
220 ulp_ha_mgr_init(struct bnxt_ulp_context *ulp_ctx)
221 {
222         struct bnxt_ulp_ha_mgr_info *ha_info;
223         int32_t rc;
224         ha_info = rte_zmalloc("ulp_ha_mgr_info", sizeof(*ha_info), 0);
225         if (!ha_info)
226                 return -ENOMEM;
227
228         /* Add the HA info tbl to the ulp context. */
229         bnxt_ulp_cntxt_ptr2_ha_info_set(ulp_ctx, ha_info);
230
231         rc = pthread_mutex_init(&ha_info->ha_lock, NULL);
232         if (rc) {
233                 PMD_DRV_LOG(ERR, "Failed to initialize ha mutex\n");
234                 goto cleanup;
235         }
236
237         return 0;
238 cleanup:
239         if (ha_info != NULL)
240                 ulp_ha_mgr_deinit(ulp_ctx);
241         return -ENOMEM;
242 }
243
244 void
245 ulp_ha_mgr_deinit(struct bnxt_ulp_context *ulp_ctx)
246 {
247         struct bnxt_ulp_ha_mgr_info *ha_info;
248
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");
252                 return;
253         }
254
255         pthread_mutex_destroy(&ha_info->ha_lock);
256         rte_free(ha_info);
257
258         bnxt_ulp_cntxt_ptr2_ha_info_set(ulp_ctx, NULL);
259 }
260
261 int32_t
262 ulp_ha_mgr_app_type_get(struct bnxt_ulp_context *ulp_ctx,
263                         enum ulp_ha_mgr_app_type *app_type)
264 {
265         struct bnxt_ulp_ha_mgr_info *ha_info;
266
267         if (ulp_ctx == NULL || app_type == NULL) {
268                 BNXT_TF_DBG(ERR, "Invalid Parms.\n");
269                 return -EINVAL;
270         }
271
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");
275                 return -EINVAL;
276         }
277         *app_type = ha_info->app_type;
278
279         return 0;
280 }
281
282 int32_t
283 ulp_ha_mgr_state_get(struct bnxt_ulp_context *ulp_ctx,
284                      enum ulp_ha_mgr_state *state)
285 {
286         struct tf_get_if_tbl_entry_parms get_parms = { 0 };
287         struct tf *tfp;
288         uint32_t val = 0;
289         int32_t rc = 0;
290
291         if (ulp_ctx == NULL || state == NULL) {
292                 BNXT_TF_DBG(ERR, "Invalid parms in state get.\n");
293                 return -EINVAL;
294         }
295         tfp = bnxt_ulp_cntxt_tfp_get(ulp_ctx, BNXT_ULP_SHARED_SESSION_NO);
296         if (tfp == NULL) {
297                 BNXT_TF_DBG(ERR, "Unable to get the TFP.\n");
298                 return -EINVAL;
299         }
300
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);
306
307         rc = tf_get_if_tbl_entry(tfp, &get_parms);
308         if (rc)
309                 BNXT_TF_DBG(ERR, "Failed to read the HA state\n");
310
311         *state = val;
312         return rc;
313 }
314
315 int32_t
316 ulp_ha_mgr_open(struct bnxt_ulp_context *ulp_ctx)
317 {
318         enum ulp_ha_mgr_state curr_state;
319         int32_t rc;
320
321         rc = ulp_ha_mgr_state_get(ulp_ctx, &curr_state);
322         if (rc) {
323                 BNXT_TF_DBG(ERR, "Failed to get HA state on Open (%d)\n", rc);
324                 return -EINVAL;
325         }
326
327         /*
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.
333          */
334         switch (curr_state) {
335         case ULP_HA_STATE_INIT:
336                 /*
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.
341                  */
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);
345                 if (rc) {
346                         BNXT_TF_DBG(ERR, "On Open: Failed to set PRIM_RUN.\n");
347                         return -EINVAL;
348                 }
349
350                 BNXT_TF_DBG(INFO, "On Open: [INIT] => PRIM[PRIM_RUN]\n");
351                 break;
352         case ULP_HA_STATE_PRIM_RUN:
353                 /*
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.
358                  *
359                  * The timer is started in order to determine when the
360                  * primary has closed.
361                  */
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);
364
365                 /*
366                  * TODO:
367                  * Clear the high region so the secondary can begin overriding
368                  * the current entries.
369                  */
370                 rc = ulp_ha_mgr_timer_start();
371                 if (rc) {
372                         BNXT_TF_DBG(ERR, "Unable to start timer on HA Open.\n");
373                         return -EINVAL;
374                 }
375
376                 rc = ulp_ha_mgr_state_set(ulp_ctx, ULP_HA_STATE_PRIM_SEC_RUN);
377                 if (rc) {
378                         BNXT_TF_DBG(ERR, "On Open: Failed to set PRIM_SEC_RUN\n");
379                         return -EINVAL;
380                 }
381                 BNXT_TF_DBG(INFO, "On Open: [PRIM_RUN] => [PRIM_SEC_RUN]\n");
382                 break;
383         default:
384                 BNXT_TF_DBG(ERR, "On Open: Unknown state 0x%x\n", curr_state);
385                 return -EINVAL;
386         }
387
388         return 0;
389 }
390
391 int32_t
392 ulp_ha_mgr_close(struct bnxt_ulp_context *ulp_ctx)
393 {
394         enum ulp_ha_mgr_state curr_state, next_state, poll_state;
395         enum ulp_ha_mgr_app_type app_type;
396         int32_t timeout;
397         int32_t rc;
398
399         rc = ulp_ha_mgr_state_get(ulp_ctx, &curr_state);
400         if (rc) {
401                 BNXT_TF_DBG(ERR, "On Close: Failed(%d) to get HA state\n", rc);
402                 return -EINVAL;
403         }
404
405         rc = ulp_ha_mgr_app_type_get(ulp_ctx, &app_type);
406         if (rc) {
407                 BNXT_TF_DBG(ERR, "On Close: Failed to get the app type.\n");
408                 return -EINVAL;
409         }
410
411         if (curr_state == ULP_HA_STATE_PRIM_RUN &&
412             app_type == ULP_HA_APP_TYPE_PRIM) {
413                 /*
414                  * Only the primary is running, so a close effectively moves the
415                  * system back to INIT.
416                  */
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) {
422                 /*
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.
426                  */
427                 BNXT_TF_DBG(INFO,
428                             "On Close: PRIM[PRIM_SEC_RUN] flushing flows.\n");
429
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);
432
433                 /*
434                  * TODO: This needs to be bounded in case the other system does
435                  * not move to PRIM_RUN.
436                  */
437                 BNXT_TF_DBG(INFO,
438                             "On Close: PRIM[PRIM_SEC_RUN] => [Copy], enter wait.\n");
439                 timeout = ULP_HA_WAIT_TIMEOUT;
440                 do {
441                         rte_delay_ms(ULP_HA_WAIT_TIME);
442                         rc = ulp_ha_mgr_state_get(ulp_ctx, &poll_state);
443                         if (rc) {
444                                 BNXT_TF_DBG(ERR,
445                                             "Failed to get HA state on Close (%d)\n",
446                                             rc);
447                                 goto cleanup;
448                         }
449                         timeout -= ULP_HA_WAIT_TIME;
450                         BNXT_TF_DBG(INFO,
451                                     "On Close: Waiting %d ms for PRIM_RUN\n",
452                                     timeout);
453                 } while (poll_state != ULP_HA_STATE_PRIM_RUN && timeout > 0);
454
455                 if (timeout <= 0) {
456                         BNXT_TF_DBG(ERR, "On Close: SEC[COPY] Timed out\n");
457                         goto cleanup;
458                 }
459
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) {
463                 /*
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.
467                  */
468                 ulp_ha_mgr_timer_cancel(ulp_ctx);
469                 ulp_ha_mgr_state_set(ulp_ctx, ULP_HA_STATE_PRIM_RUN);
470
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) {
474                 /*
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.
478                  */
479                 BNXT_TF_DBG(INFO, "On Close: SEC[COPY] wait for PRIM_RUN\n");
480
481                 timeout = ULP_HA_WAIT_TIMEOUT;
482                 do {
483                         rte_delay_ms(ULP_HA_WAIT_TIME);
484                         rc = ulp_ha_mgr_state_get(ulp_ctx, &poll_state);
485                         if (rc) {
486                                 BNXT_TF_DBG(ERR,
487                                             "Failed to get HA state on Close (%d)\n",
488                                             rc);
489                                 goto cleanup;
490                         }
491
492                         timeout -= ULP_HA_WAIT_TIME;
493                         BNXT_TF_DBG(INFO,
494                                     "On Close: Waiting %d ms for PRIM_RUN\n",
495                                     timeout);
496                 } while (poll_state != ULP_HA_STATE_PRIM_RUN &&
497                          timeout >= 0);
498
499                 if (timeout <= 0) {
500                         BNXT_TF_DBG(ERR,
501                                     "On Close: SEC[COPY] Timed out\n");
502                         goto cleanup;
503                 }
504
505                 next_state = ULP_HA_STATE_INIT;
506                 rc = ulp_ha_mgr_state_set(ulp_ctx, next_state);
507                 if (rc) {
508                         BNXT_TF_DBG(ERR,
509                                     "On Close: Failed to set state to INIT(%x)\n",
510                                     rc);
511                         goto cleanup;
512                 }
513
514                 BNXT_TF_DBG(INFO,
515                             "On Close: SEC[COPY] => [INIT] after %d ms\n",
516                             ULP_HA_WAIT_TIMEOUT - timeout);
517         } else {
518                 BNXT_TF_DBG(ERR, "On Close: Invalid type/state %d/%d\n",
519                             curr_state, app_type);
520         }
521 cleanup:
522         return rc;
523 }
524
525 int32_t
526 ulp_ha_mgr_region_get(struct bnxt_ulp_context *ulp_ctx,
527                       enum ulp_ha_mgr_region *region)
528 {
529         struct bnxt_ulp_ha_mgr_info *ha_info;
530
531         if (ulp_ctx == NULL || region == NULL) {
532                 BNXT_TF_DBG(ERR, "Invalid params in ha region get.\n");
533                 return -EINVAL;
534         }
535
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");
539                 return -EINVAL;
540         }
541         *region = ha_info->region;
542
543         return 0;
544 }