NVIDIA DOCA SDK Data Center on a Chip Framework Documentation
simple_fwd_ft.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2021-2022 NVIDIA CORPORATION AND AFFILIATES. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without modification, are permitted
5  * provided that the following conditions are met:
6  * * Redistributions of source code must retain the above copyright notice, this list of
7  * conditions and the following disclaimer.
8  * * Redistributions in binary form must reproduce the above copyright notice, this list of
9  * conditions and the following disclaimer in the documentation and/or other materials
10  * provided with the distribution.
11  * * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
12  * to endorse or promote products derived from this software without specific prior written
13  * permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
17  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
21  * STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
22  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  *
24  */
25 #include <stdint.h>
26 #include <unistd.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 
30 #include <doca_flow.h>
31 #include <doca_log.h>
32 
33 #include "simple_fwd.h"
34 #include "simple_fwd_ft.h"
35 
36 DOCA_LOG_REGISTER(SIMPLE_FWD_FT);
37 
38 /* Bucket is a struct encomassing the list and the synchronization mechanism used for accessing the flows list */
40  struct simple_fwd_ft_entry_head head; /* The head of the list of the flows */
41  rte_spinlock_t lock; /* Lock, a synchronization mechanism */
42 };
43 
44 /* Stats for the flow table */
46  uint64_t add; /* Number of insertions to the flow table */
47  uint64_t rm; /* Number of removals from the flow table */
48  uint64_t memuse; /* Memory ysage of the flow table */
49 };
50 
51 /* Flow table configuration */
53  uint32_t size; /* Number of maximum flows in a given time while the application is running */
54  uint32_t mask; /* Masking; */
55  uint32_t user_data_size; /* User data size needed for allocation */
56  uint32_t entry_size; /* Size needed for storing a single entry flow */
57 };
58 
59 /* Flow table as represented in the application */
60 struct simple_fwd_ft {
61  struct simple_fwd_ft_cfg cfg; /* Flow table configurations */
62  struct simple_fwd_ft_stats stats; /* Stats for the flow table */
63  bool has_age_thread; /* Whether or not a dedicated thread is used */
64  pthread_t age_thread; /* Thread entity for aging, in case "aging thread" is used */
65  volatile int stop_aging_thread; /* Flag for stopping the aging thread */
66  uint32_t fid_ctr; /* Flow table ID , used for controlling the flow table */
67  void (*simple_fwd_aging_cb)(struct simple_fwd_ft_user_ctx *ctx); /* Callback holder; callback for handling aged
68  flows */
69  void (*simple_fwd_aging_hw_cb)(void); /* HW callback holder; callback for handling aged flows*/
70  struct simple_fwd_ft_bucket buckets[0]; /* Pointer for the Bucket in the flow table; list of entries */
71 };
72 
73 void simple_fwd_ft_update_age_sec(struct simple_fwd_ft_entry *e, uint32_t age_sec)
74 {
75  e->age_sec = age_sec;
76 }
77 
79 {
80  if (e->age_sec)
81  e->expiration = rte_rdtsc() + rte_get_timer_hz() * e->age_sec;
82 }
83 
84 /*
85  * Update a counter of a given entry
86  *
87  * @e [in]: flow entry representation in the application
88  * @return: true on success, false otherwise
89  */
91 {
93  struct doca_flow_resource_query query_stats = {0};
94  bool update = 0;
95 
96  if (doca_flow_resource_query_entry(entry->hw_entry, &query_stats) == DOCA_SUCCESS) {
97  update = !!(query_stats.counter.total_pkts - e->last_counter);
98  e->last_counter = query_stats.counter.total_pkts;
99  }
100  return update;
101 }
102 
103 /*
104  * Destroy flow entry in the flow table
105  *
106  * @ft [in]: the flow table to remove the entry from
107  * @ft_entry [in]: entry flow to remove, as represented in the application
108  */
109 static void _ft_destroy_entry(struct simple_fwd_ft *ft, struct simple_fwd_ft_entry *ft_entry)
110 {
111  LIST_REMOVE(ft_entry, next);
112  ft->simple_fwd_aging_cb(&ft_entry->user_ctx);
113  free(ft_entry);
114  ft->stats.rm++;
115 }
116 
118 {
119  int idx = ft_entry->buckets_index;
120 
121  rte_spinlock_lock(&ft->buckets[idx].lock);
122  _ft_destroy_entry(ft, ft_entry);
123  rte_spinlock_unlock(&ft->buckets[idx].lock);
124 }
125 
126 /*
127  * Start aging handling for a given flow table
128  *
129  * @ft [in]: the flow table to start the aging handling for
130  * @i [in]: the index of the bucket
131  * @return: true on success, false when aging handler still not finished all flows in the table
132  */
133 static bool simple_fwd_ft_aging_ft_entry(struct simple_fwd_ft *ft, unsigned int i)
134 {
135  struct simple_fwd_ft_entry *node, *ptr;
136  bool still_aging = false;
137  uint64_t t = rte_rdtsc();
138 
139  if (rte_spinlock_trylock(&ft->buckets[i].lock)) {
140  node = LIST_FIRST(&ft->buckets[i].head);
141  while (node) {
142  ptr = LIST_NEXT(node, next);
143  if (node->age_sec && node->expiration < t && !simple_fwd_ft_update_counter(node)) {
144  DOCA_LOG_DBG("Aging removing flow");
145  _ft_destroy_entry(ft, node);
146  still_aging = true;
147  break;
148  }
149  node = ptr;
150  }
151  rte_spinlock_unlock(&ft->buckets[i].lock);
152  }
153  return still_aging;
154 }
155 
156 /*
157  * Main function for aging handler
158  *
159  * @void_ptr [in]: the flow table to start the aging for
160  * @return: Next flow table to handle the aging for, NULL value otherwise
161  */
162 static void *simple_fwd_ft_aging_main(void *void_ptr)
163 {
164  struct simple_fwd_ft *ft = (struct simple_fwd_ft *)void_ptr;
165  bool next = false;
166  unsigned int i;
167 
168  if (!ft) {
169  DOCA_LOG_ERR("No ft, abort aging");
170  return NULL;
171  }
172  while (!ft->stop_aging_thread) {
173  if ((int)(ft->stats.add - ft->stats.rm) == 0)
174  continue;
175  DOCA_LOG_DBG("Total entries: %d", (int)(ft->stats.add - ft->stats.rm));
176  DOCA_LOG_DBG("Total adds : %d", (int)(ft->stats.add));
177  for (i = 0; i < ft->cfg.size; i++) {
178  do {
179  next = simple_fwd_ft_aging_ft_entry(ft, i);
180  } while (next);
181  }
182  sleep(1);
183  }
184  return NULL;
185 }
186 
187 /*
188  * Start per flow table aging thread
189  *
190  * @ft [in]: the flow table to start the aging thread for
191  * @thread_id [in]: the dedicated thread identifier for aging handling of the provided flow table
192  * @return: 0 on success and negative value otherwise
193  */
194 static int simple_fwd_ft_aging_thread_start(struct simple_fwd_ft *ft, pthread_t *thread_id)
195 {
196  int ret;
197 
198  /* create a second thread which executes inc_x(&x) */
199  ret = pthread_create(thread_id, NULL, simple_fwd_ft_aging_main, ft);
200  if (ret) {
201  fprintf(stderr, "Error creating thread ret:%d\n", ret);
202  return -1;
203  }
204  return 0;
205 }
206 
207 /*
208  * Build table key according to parsed packet.
209  *
210  * @pinfo [in]: the packet's info
211  * @key [out]: the generated key
212  * @return: 0 on success and negative value otherwise
213  */
214 static int simple_fwd_ft_key_fill(struct simple_fwd_pkt_info *pinfo, struct simple_fwd_ft_key *key)
215 {
216  bool inner = false;
217 
218  if (pinfo->tun_type != DOCA_FLOW_TUN_NONE)
219  inner = true;
220 
221  /* support ipv6 */
222  if (pinfo->outer.l3_type != IPV4)
223  return -1;
224 
225  key->rss_hash = pinfo->rss_hash;
226  /* 5-tuple of inner if there is tunnel or outer if none */
227  key->protocol = inner ? pinfo->inner.l4_type : pinfo->outer.l4_type;
228  key->ipv4_1 = simple_fwd_ft_key_get_ipv4_src(inner, pinfo);
229  key->ipv4_2 = simple_fwd_ft_key_get_ipv4_dst(inner, pinfo);
230  key->port_1 = simple_fwd_ft_key_get_src_port(inner, pinfo);
231  key->port_2 = simple_fwd_ft_key_get_dst_port(inner, pinfo);
232  key->port_id = pinfo->orig_port_id;
233 
234  key->tun_type = pinfo->tun_type;
235  switch (pinfo->tun_type) {
236  case DOCA_FLOW_TUN_VXLAN:
237  key->vni = pinfo->tun.vni;
238  break;
239  case DOCA_FLOW_TUN_GTPU:
240  key->teid = pinfo->tun.teid;
241  break;
242  case DOCA_FLOW_TUN_GRE:
243  key->gre_key = pinfo->tun.gre_key;
244  break;
245  default:
246  break;
247  }
248 
249  return 0;
250 }
251 
252 /*
253  * Compare keys
254  *
255  * @key1 [in]: first key for comparison
256  * @key2 [in]: first key for comparison
257  * @return: true if keys are equal, false otherwise
258  */
259 static bool simple_fwd_ft_key_equal(struct simple_fwd_ft_key *key1, struct simple_fwd_ft_key *key2)
260 {
261  return (memcmp(key1, key2, sizeof(struct simple_fwd_ft_key)) == 0);
262 }
263 
264 struct simple_fwd_ft *simple_fwd_ft_create(int nb_flows,
265  uint32_t user_data_size,
267  void (*simple_fwd_aging_hw_cb)(void),
268  bool age_thread)
269 {
270  struct simple_fwd_ft *ft;
271  uint32_t nb_flows_aligned;
272  uint32_t alloc_size;
273  uint32_t i;
274 
275  if (nb_flows <= 0)
276  return NULL;
277  /* Align to the next power of 2, 32bits integer is enough now */
278  if (!rte_is_power_of_2(nb_flows))
279  nb_flows_aligned = rte_align32pow2(nb_flows);
280  else
281  nb_flows_aligned = nb_flows;
282  /* double the flows to avoid collisions */
283  nb_flows_aligned <<= 1;
284  alloc_size = sizeof(struct simple_fwd_ft) + sizeof(struct simple_fwd_ft_bucket) * nb_flows_aligned;
285  DOCA_LOG_TRC("Malloc size =%d", alloc_size);
286 
287  ft = calloc(1, alloc_size);
288  if (ft == NULL) {
289  DOCA_LOG_ERR("No memory");
290  return NULL;
291  }
292  ft->cfg.entry_size = sizeof(struct simple_fwd_ft_entry) + user_data_size;
293  ft->cfg.user_data_size = user_data_size;
294  ft->cfg.size = nb_flows_aligned;
295  ft->cfg.mask = nb_flows_aligned - 1;
296  ft->simple_fwd_aging_cb = simple_fwd_aging_cb;
297  ft->simple_fwd_aging_hw_cb = simple_fwd_aging_hw_cb;
298 
299  DOCA_LOG_TRC("FT created: flows=%d, user_data_size=%d", nb_flows_aligned, user_data_size);
300  for (i = 0; i < ft->cfg.size; i++)
301  rte_spinlock_init(&ft->buckets[i].lock);
302  if (age_thread && simple_fwd_ft_aging_thread_start(ft, &ft->age_thread) < 0) {
303  free(ft);
304  return NULL;
305  }
306  ft->has_age_thread = age_thread;
307  return ft;
308 }
309 
310 /*
311  * find if there is an existing entry matching the given packet generated key
312  *
313  * @ft [in]: flow table to search in
314  * @key [in]: the packet generated key used for search in the flow table
315  * @return: pointer to the flow entry if found, NULL otherwise
316  */
318 {
319  uint32_t idx;
320  struct simple_fwd_ft_entry_head *first;
321  struct simple_fwd_ft_entry *node;
322 
323  idx = key->rss_hash & ft->cfg.mask;
324  DOCA_LOG_TRC("Looking for index %d", idx);
325  first = &ft->buckets[idx].head;
326  LIST_FOREACH(node, first, next)
327  {
328  if (simple_fwd_ft_key_equal(&node->key, key)) {
330  return node;
331  }
332  }
333  return NULL;
334 }
335 
337  struct simple_fwd_pkt_info *pinfo,
338  struct simple_fwd_ft_user_ctx **ctx)
339 {
341  struct simple_fwd_ft_entry *fe;
342  struct simple_fwd_ft_key key = {0};
343 
344  if (simple_fwd_ft_key_fill(pinfo, &key)) {
346  DOCA_LOG_DBG("Failed to build key for entry in the flow table %s", doca_error_get_descr(result));
347  return result;
348  }
349 
350  fe = _simple_fwd_ft_find(ft, &key);
351  if (fe == NULL) {
353  DOCA_LOG_DBG("Entry not found in flow table %s", doca_error_get_descr(result));
354  return result;
355  }
356 
357  *ctx = &fe->user_ctx;
358  return DOCA_SUCCESS;
359 }
360 
362  struct simple_fwd_pkt_info *pinfo,
363  struct simple_fwd_ft_user_ctx **ctx)
364 {
366  int idx;
367  struct simple_fwd_ft_key key = {0};
368  struct simple_fwd_ft_entry *new_e;
369  struct simple_fwd_ft_entry_head *first;
370 
371  if (!ft)
372  return false;
373 
374  if (simple_fwd_ft_key_fill(pinfo, &key)) {
376  DOCA_LOG_DBG("Failed to build key: %s", doca_error_get_descr(result));
377  return result;
378  }
379 
380  new_e = calloc(1, ft->cfg.entry_size);
381  if (new_e == NULL) {
384  return result;
385  }
386 
388  new_e->user_ctx.fid = ft->fid_ctr++;
389  *ctx = &new_e->user_ctx;
390 
391  DOCA_LOG_TRC("Defined new flow %llu", (unsigned int long long)new_e->user_ctx.fid);
392  memcpy(&new_e->key, &key, sizeof(struct simple_fwd_ft_key));
393  idx = pinfo->rss_hash & ft->cfg.mask;
394  new_e->buckets_index = idx;
395  first = &ft->buckets[idx].head;
396 
397  rte_spinlock_lock(&ft->buckets[idx].lock);
398  LIST_INSERT_HEAD(first, new_e, next);
399  rte_spinlock_unlock(&ft->buckets[idx].lock);
400  ft->stats.add++;
401  return result;
402 }
403 
405 {
406  uint32_t i;
407  struct simple_fwd_ft_entry *node, *ptr;
408 
409  if (ft == NULL)
411  if (ft->has_age_thread) {
412  ft->stop_aging_thread = true;
413  pthread_join(ft->age_thread, NULL);
414  }
415  for (i = 0; i < ft->cfg.size; i++) {
416  node = LIST_FIRST(&ft->buckets[i].head);
417  while (node != NULL) {
418  ptr = LIST_NEXT(node, next);
419  _ft_destroy_entry(ft, node);
420  node = ptr;
421  }
422  }
423  free(ft);
424  return DOCA_SUCCESS;
425 }
#define NULL
Definition: __stddef_null.h:26
int32_t result
static struct doca_flow_pipe_entry * entry[MAX_ENTRIES]
enum doca_error doca_error_t
DOCA API return codes.
DOCA_STABLE const char * doca_error_get_descr(doca_error_t error)
Returns the description string of an error code.
@ DOCA_ERROR_INVALID_VALUE
Definition: doca_error.h:44
@ DOCA_ERROR_UNEXPECTED
Definition: doca_error.h:60
@ DOCA_ERROR_NOT_FOUND
Definition: doca_error.h:54
@ DOCA_SUCCESS
Definition: doca_error.h:38
@ DOCA_ERROR_NO_MEMORY
Definition: doca_error.h:45
@ DOCA_FLOW_TUN_GRE
@ DOCA_FLOW_TUN_GTPU
@ DOCA_FLOW_TUN_VXLAN
@ DOCA_FLOW_TUN_NONE
DOCA_EXPERIMENTAL doca_error_t doca_flow_resource_query_entry(struct doca_flow_pipe_entry *entry, struct doca_flow_resource_query *query_stats)
Extract information about specific entry.
#define DOCA_LOG_ERR(format,...)
Generates an ERROR application log message.
Definition: doca_log.h:466
#define DOCA_LOG_WARN(format,...)
Generates a WARNING application log message.
Definition: doca_log.h:476
#define DOCA_LOG_TRC(format,...)
Generates a TRACE application log message.
Definition: doca_log.h:513
#define DOCA_LOG_DBG(format,...)
Generates a DEBUG application log message.
Definition: doca_log.h:496
void simple_fwd_ft_update_expiration(struct simple_fwd_ft_entry *e)
Definition: simple_fwd_ft.c:78
static void * simple_fwd_ft_aging_main(void *void_ptr)
static int simple_fwd_ft_aging_thread_start(struct simple_fwd_ft *ft, pthread_t *thread_id)
struct simple_fwd_ft * simple_fwd_ft_create(int nb_flows, uint32_t user_data_size, void(*simple_fwd_aging_cb)(struct simple_fwd_ft_user_ctx *ctx), void(*simple_fwd_aging_hw_cb)(void), bool age_thread)
static void _ft_destroy_entry(struct simple_fwd_ft *ft, struct simple_fwd_ft_entry *ft_entry)
doca_error_t simple_fwd_ft_find(struct simple_fwd_ft *ft, struct simple_fwd_pkt_info *pinfo, struct simple_fwd_ft_user_ctx **ctx)
static struct simple_fwd_ft_entry * _simple_fwd_ft_find(struct simple_fwd_ft *ft, struct simple_fwd_ft_key *key)
doca_error_t simple_fwd_ft_destroy(struct simple_fwd_ft *ft)
void simple_fwd_ft_destroy_entry(struct simple_fwd_ft *ft, struct simple_fwd_ft_entry *ft_entry)
static int simple_fwd_ft_key_fill(struct simple_fwd_pkt_info *pinfo, struct simple_fwd_ft_key *key)
static bool simple_fwd_ft_update_counter(struct simple_fwd_ft_entry *e)
Definition: simple_fwd_ft.c:90
static bool simple_fwd_ft_key_equal(struct simple_fwd_ft_key *key1, struct simple_fwd_ft_key *key2)
doca_error_t simple_fwd_ft_add_new(struct simple_fwd_ft *ft, struct simple_fwd_pkt_info *pinfo, struct simple_fwd_ft_user_ctx **ctx)
void simple_fwd_ft_update_age_sec(struct simple_fwd_ft_entry *e, uint32_t age_sec)
Definition: simple_fwd_ft.c:73
DOCA_LOG_REGISTER(SIMPLE_FWD_FT)
static bool simple_fwd_ft_aging_ft_entry(struct simple_fwd_ft *ft, unsigned int i)
#define simple_fwd_ft_key_get_dst_port(inner, pinfo)
Definition: simple_fwd_ft.h:74
#define simple_fwd_ft_key_get_ipv4_src(inner, pinfo)
Definition: simple_fwd_ft.h:62
#define simple_fwd_ft_key_get_ipv4_dst(inner, pinfo)
Definition: simple_fwd_ft.h:66
#define simple_fwd_ft_key_get_src_port(inner, pinfo)
Definition: simple_fwd_ft.h:70
flow resource query
Definition: doca_flow.h:1101
struct doca_flow_resource_query::@115::@117 counter
rte_spinlock_t lock
Definition: simple_fwd_ft.c:41
struct simple_fwd_ft_entry_head head
Definition: simple_fwd_ft.c:40
uint32_t user_data_size
Definition: simple_fwd_ft.c:55
Definition: simple_fwd_ft.h:47
struct simple_fwd_ft_key key
Definition: simple_fwd_ft.h:49
uint16_t buckets_index
Definition: simple_fwd_ft.h:55
uint32_t age_sec
Definition: simple_fwd_ft.h:51
uint64_t expiration
Definition: simple_fwd_ft.h:50
struct simple_fwd_ft_user_ctx user_ctx
Definition: simple_fwd_ft.h:56
doca_be32_t gre_key
doca_be16_t port_2
doca_be32_t ipv4_2
doca_be16_t port_1
doca_be32_t ipv4_1
struct simple_fwd_ft_bucket buckets[0]
Definition: simple_fwd_ft.c:70
pthread_t age_thread
Definition: simple_fwd_ft.c:64
struct simple_fwd_ft_cfg cfg
Definition: simple_fwd_ft.c:61
void(* simple_fwd_aging_cb)(struct simple_fwd_ft_user_ctx *ctx)
Definition: simple_fwd_ft.c:67
struct simple_fwd_ft_stats stats
Definition: simple_fwd_ft.c:62
volatile int stop_aging_thread
Definition: simple_fwd_ft.c:65
void(* simple_fwd_aging_hw_cb)(void)
Definition: simple_fwd_ft.c:69
uint32_t fid_ctr
Definition: simple_fwd_ft.c:66
Definition: simple_fwd.h:57
struct simple_fwd_pkt_format outer
enum doca_flow_tun_type tun_type
struct simple_fwd_pkt_format inner
struct simple_fwd_pkt_tun_format tun
struct upf_accel_ctx * ctx