1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2020 Intel Corporation
3 */
4 #include <stdlib.h>
5 #include <string.h>
6 #include <stdio.h>
7 #include <sys/queue.h>
8 #include <unistd.h>
9
10 #include <rte_common.h>
11 #include <rte_byteorder.h>
12
13 #include "rte_swx_ctl.h"
14
15 #define CHECK(condition, err_code) \
16 do { \
17 if (!(condition)) \
18 return -(err_code); \
19 } while (0)
20
21 #define ntoh64(x) rte_be_to_cpu_64(x)
22 #define hton64(x) rte_cpu_to_be_64(x)
23
24 #if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
25 #define field_ntoh(val, n_bits) (ntoh64((val) << (64 - n_bits)))
26 #define field_hton(val, n_bits) (hton64((val) << (64 - n_bits)))
27 #else
28 #define field_ntoh(val, n_bits) (val)
29 #define field_hton(val, n_bits) (val)
30 #endif
31
32 struct action {
33 struct rte_swx_ctl_action_info info;
34 struct rte_swx_ctl_action_arg_info *args;
35 uint32_t data_size;
36 };
37
38 struct table {
39 struct rte_swx_ctl_table_info info;
40 struct rte_swx_ctl_table_match_field_info *mf;
41 struct rte_swx_ctl_table_action_info *actions;
42 struct rte_swx_table_ops ops;
43 struct rte_swx_table_params params;
44
45 struct rte_swx_table_entry_list entries;
46 struct rte_swx_table_entry_list pending_add;
47 struct rte_swx_table_entry_list pending_modify0;
48 struct rte_swx_table_entry_list pending_modify1;
49 struct rte_swx_table_entry_list pending_delete;
50 struct rte_swx_table_entry *pending_default;
51
52 int is_stub;
53 uint32_t n_add;
54 uint32_t n_modify;
55 uint32_t n_delete;
56 };
57
58 struct rte_swx_ctl_pipeline {
59 struct rte_swx_ctl_pipeline_info info;
60 struct rte_swx_pipeline *p;
61 struct action *actions;
62 struct table *tables;
63 struct rte_swx_table_state *ts;
64 struct rte_swx_table_state *ts_next;
65 int numa_node;
66 };
67
68 static struct action *
action_find(struct rte_swx_ctl_pipeline * ctl,const char * action_name)69 action_find(struct rte_swx_ctl_pipeline *ctl, const char *action_name)
70 {
71 uint32_t i;
72
73 for (i = 0; i < ctl->info.n_actions; i++) {
74 struct action *a = &ctl->actions[i];
75
76 if (!strcmp(action_name, a->info.name))
77 return a;
78 }
79
80 return NULL;
81 }
82
83 static void
action_free(struct rte_swx_ctl_pipeline * ctl)84 action_free(struct rte_swx_ctl_pipeline *ctl)
85 {
86 uint32_t i;
87
88 if (!ctl->actions)
89 return;
90
91 for (i = 0; i < ctl->info.n_actions; i++) {
92 struct action *action = &ctl->actions[i];
93
94 free(action->args);
95 }
96
97 free(ctl->actions);
98 ctl->actions = NULL;
99 }
100
101 static struct table *
table_find(struct rte_swx_ctl_pipeline * ctl,const char * table_name)102 table_find(struct rte_swx_ctl_pipeline *ctl, const char *table_name)
103 {
104 uint32_t i;
105
106 for (i = 0; i < ctl->info.n_tables; i++) {
107 struct table *table = &ctl->tables[i];
108
109 if (!strcmp(table_name, table->info.name))
110 return table;
111 }
112
113 return NULL;
114 }
115
116 static int
table_params_get(struct rte_swx_ctl_pipeline * ctl,uint32_t table_id)117 table_params_get(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)
118 {
119 struct table *table = &ctl->tables[table_id];
120 uint8_t *key_mask = NULL;
121 enum rte_swx_table_match_type match_type = RTE_SWX_TABLE_MATCH_WILDCARD;
122 uint32_t key_size = 0, key_offset = 0, action_data_size = 0, i;
123
124 if (table->info.n_match_fields) {
125 struct rte_swx_ctl_table_match_field_info *first, *last;
126 uint32_t i;
127
128 first = &table->mf[0];
129 last = &table->mf[table->info.n_match_fields - 1];
130
131 /* match_type. */
132 for (i = 0; i < table->info.n_match_fields; i++) {
133 struct rte_swx_ctl_table_match_field_info *f;
134
135 f = &table->mf[i];
136 if (f->match_type != RTE_SWX_TABLE_MATCH_EXACT)
137 break;
138 }
139
140 if (i == table->info.n_match_fields)
141 match_type = RTE_SWX_TABLE_MATCH_EXACT;
142 else if ((i == table->info.n_match_fields - 1) &&
143 (last->match_type == RTE_SWX_TABLE_MATCH_LPM))
144 match_type = RTE_SWX_TABLE_MATCH_LPM;
145
146 /* key_offset. */
147 key_offset = first->offset / 8;
148
149 /* key_size. */
150 key_size = (last->offset + last->n_bits - first->offset) / 8;
151
152 /* key_mask. */
153 key_mask = calloc(1, key_size);
154 CHECK(key_mask, ENOMEM);
155
156 for (i = 0; i < table->info.n_match_fields; i++) {
157 struct rte_swx_ctl_table_match_field_info *f;
158 uint32_t start;
159 size_t size;
160
161 f = &table->mf[i];
162 start = (f->offset - first->offset) / 8;
163 size = f->n_bits / 8;
164
165 memset(&key_mask[start], 0xFF, size);
166 }
167 }
168
169 /* action_data_size. */
170 for (i = 0; i < table->info.n_actions; i++) {
171 uint32_t action_id = table->actions[i].action_id;
172 struct action *a = &ctl->actions[action_id];
173
174 if (a->data_size > action_data_size)
175 action_data_size = a->data_size;
176 }
177
178 /* Fill in. */
179 table->params.match_type = match_type;
180 table->params.key_size = key_size;
181 table->params.key_offset = key_offset;
182 table->params.key_mask0 = key_mask;
183 table->params.action_data_size = action_data_size;
184 table->params.n_keys_max = table->info.size;
185
186 return 0;
187 }
188
189 static void
table_entry_free(struct rte_swx_table_entry * entry)190 table_entry_free(struct rte_swx_table_entry *entry)
191 {
192 if (!entry)
193 return;
194
195 free(entry->key);
196 free(entry->key_mask);
197 free(entry->action_data);
198 free(entry);
199 }
200
201 static struct rte_swx_table_entry *
table_entry_alloc(struct table * table)202 table_entry_alloc(struct table *table)
203 {
204 struct rte_swx_table_entry *entry;
205
206 entry = calloc(1, sizeof(struct rte_swx_table_entry));
207 if (!entry)
208 goto error;
209
210 /* key, key_mask. */
211 if (!table->is_stub) {
212 entry->key = calloc(1, table->params.key_size);
213 if (!entry->key)
214 goto error;
215
216 if (table->params.match_type != RTE_SWX_TABLE_MATCH_EXACT) {
217 entry->key_mask = calloc(1, table->params.key_size);
218 if (!entry->key_mask)
219 goto error;
220 }
221 }
222
223 /* action_data. */
224 if (table->params.action_data_size) {
225 entry->action_data = calloc(1, table->params.action_data_size);
226 if (!entry->action_data)
227 goto error;
228 }
229
230 return entry;
231
232 error:
233 table_entry_free(entry);
234 return NULL;
235 }
236
237 static int
table_entry_check(struct rte_swx_ctl_pipeline * ctl,uint32_t table_id,struct rte_swx_table_entry * entry,int key_check,int data_check)238 table_entry_check(struct rte_swx_ctl_pipeline *ctl,
239 uint32_t table_id,
240 struct rte_swx_table_entry *entry,
241 int key_check,
242 int data_check)
243 {
244 struct table *table = &ctl->tables[table_id];
245
246 CHECK(entry, EINVAL);
247
248 if (key_check) {
249 if (table->is_stub) {
250 /* key. */
251 CHECK(!entry->key, EINVAL);
252
253 /* key_mask. */
254 CHECK(!entry->key_mask, EINVAL);
255 } else {
256 /* key. */
257 CHECK(entry->key, EINVAL);
258
259 /* key_mask. */
260 switch (table->params.match_type) {
261 case RTE_SWX_TABLE_MATCH_WILDCARD:
262 break;
263
264 case RTE_SWX_TABLE_MATCH_LPM:
265 /* TBD Check that key mask is prefix. */
266 break;
267
268 case RTE_SWX_TABLE_MATCH_EXACT:
269 CHECK(!entry->key_mask, EINVAL);
270 break;
271
272 default:
273 CHECK(0, EINVAL);
274 }
275 }
276 }
277
278 if (data_check) {
279 struct action *a;
280 uint32_t i;
281
282 /* action_id. */
283 for (i = 0; i < table->info.n_actions; i++)
284 if (entry->action_id == table->actions[i].action_id)
285 break;
286
287 CHECK(i < table->info.n_actions, EINVAL);
288
289 /* action_data. */
290 a = &ctl->actions[entry->action_id];
291 CHECK((a->data_size && entry->action_data) ||
292 (!a->data_size && !entry->action_data), EINVAL);
293 }
294
295 return 0;
296 }
297
298 static struct rte_swx_table_entry *
table_entry_duplicate(struct rte_swx_ctl_pipeline * ctl,uint32_t table_id,struct rte_swx_table_entry * entry,int key_duplicate,int data_duplicate)299 table_entry_duplicate(struct rte_swx_ctl_pipeline *ctl,
300 uint32_t table_id,
301 struct rte_swx_table_entry *entry,
302 int key_duplicate,
303 int data_duplicate)
304 {
305 struct table *table = &ctl->tables[table_id];
306 struct rte_swx_table_entry *new_entry = NULL;
307
308 if (!entry)
309 goto error;
310
311 new_entry = calloc(1, sizeof(struct rte_swx_table_entry));
312 if (!new_entry)
313 goto error;
314
315 if (key_duplicate && !table->is_stub) {
316 /* key. */
317 if (!entry->key)
318 goto error;
319
320 new_entry->key = malloc(table->params.key_size);
321 if (!new_entry->key)
322 goto error;
323
324 memcpy(new_entry->key, entry->key, table->params.key_size);
325
326 /* key_signature. */
327 new_entry->key_signature = entry->key_signature;
328
329 /* key_mask. */
330 if (table->params.match_type != RTE_SWX_TABLE_MATCH_EXACT) {
331 if (!entry->key_mask)
332 goto error;
333
334 new_entry->key_mask = malloc(table->params.key_size);
335 if (!new_entry->key_mask)
336 goto error;
337
338 memcpy(new_entry->key_mask,
339 entry->key_mask,
340 table->params.key_size);
341 }
342 }
343
344 if (data_duplicate) {
345 struct action *a;
346 uint32_t i;
347
348 /* action_id. */
349 for (i = 0; i < table->info.n_actions; i++)
350 if (entry->action_id == table->actions[i].action_id)
351 break;
352
353 if (i >= table->info.n_actions)
354 goto error;
355
356 new_entry->action_id = entry->action_id;
357
358 /* action_data. */
359 a = &ctl->actions[entry->action_id];
360 if (a->data_size) {
361 if (!entry->action_data)
362 goto error;
363
364 new_entry->action_data = malloc(a->data_size);
365 if (!new_entry->action_data)
366 goto error;
367
368 memcpy(new_entry->action_data,
369 entry->action_data,
370 a->data_size);
371 }
372 }
373
374 return new_entry;
375
376 error:
377 table_entry_free(new_entry);
378 return NULL;
379 }
380
381 static int
entry_keycmp_em(struct rte_swx_table_entry * e0,struct rte_swx_table_entry * e1,uint32_t key_size)382 entry_keycmp_em(struct rte_swx_table_entry *e0,
383 struct rte_swx_table_entry *e1,
384 uint32_t key_size)
385 {
386 if (e0->key_signature != e1->key_signature)
387 return 1; /* Not equal. */
388
389 if (memcmp(e0->key, e1->key, key_size))
390 return 1; /* Not equal. */
391
392 return 0; /* Equal */
393 }
394
395 static int
entry_keycmp_wm(struct rte_swx_table_entry * e0 __rte_unused,struct rte_swx_table_entry * e1 __rte_unused,uint32_t key_size __rte_unused)396 entry_keycmp_wm(struct rte_swx_table_entry *e0 __rte_unused,
397 struct rte_swx_table_entry *e1 __rte_unused,
398 uint32_t key_size __rte_unused)
399 {
400 /* TBD */
401
402 return 1; /* Not equal */
403 }
404
405 static int
entry_keycmp_lpm(struct rte_swx_table_entry * e0 __rte_unused,struct rte_swx_table_entry * e1 __rte_unused,uint32_t key_size __rte_unused)406 entry_keycmp_lpm(struct rte_swx_table_entry *e0 __rte_unused,
407 struct rte_swx_table_entry *e1 __rte_unused,
408 uint32_t key_size __rte_unused)
409 {
410 /* TBD */
411
412 return 1; /* Not equal */
413 }
414
415 static int
table_entry_keycmp(struct table * table,struct rte_swx_table_entry * e0,struct rte_swx_table_entry * e1)416 table_entry_keycmp(struct table *table,
417 struct rte_swx_table_entry *e0,
418 struct rte_swx_table_entry *e1)
419 {
420 switch (table->params.match_type) {
421 case RTE_SWX_TABLE_MATCH_EXACT:
422 return entry_keycmp_em(e0, e1, table->params.key_size);
423
424 case RTE_SWX_TABLE_MATCH_WILDCARD:
425 return entry_keycmp_wm(e0, e1, table->params.key_size);
426
427 case RTE_SWX_TABLE_MATCH_LPM:
428 return entry_keycmp_lpm(e0, e1, table->params.key_size);
429
430 default:
431 return 1; /* Not equal. */
432 }
433 }
434
435 static struct rte_swx_table_entry *
table_entries_find(struct table * table,struct rte_swx_table_entry * entry)436 table_entries_find(struct table *table, struct rte_swx_table_entry *entry)
437 {
438 struct rte_swx_table_entry *e;
439
440 TAILQ_FOREACH(e, &table->entries, node)
441 if (!table_entry_keycmp(table, entry, e))
442 return e; /* Found. */
443
444 return NULL; /* Not found. */
445 }
446
447 static void
table_entries_free(struct table * table)448 table_entries_free(struct table *table)
449 {
450 for ( ; ; ) {
451 struct rte_swx_table_entry *entry;
452
453 entry = TAILQ_FIRST(&table->entries);
454 if (!entry)
455 break;
456
457 TAILQ_REMOVE(&table->entries, entry, node);
458 table_entry_free(entry);
459 }
460 }
461
462 static struct rte_swx_table_entry *
table_pending_add_find(struct table * table,struct rte_swx_table_entry * entry)463 table_pending_add_find(struct table *table, struct rte_swx_table_entry *entry)
464 {
465 struct rte_swx_table_entry *e;
466
467 TAILQ_FOREACH(e, &table->pending_add, node)
468 if (!table_entry_keycmp(table, entry, e))
469 return e; /* Found. */
470
471 return NULL; /* Not found. */
472 }
473
474 static void
table_pending_add_admit(struct table * table)475 table_pending_add_admit(struct table *table)
476 {
477 TAILQ_CONCAT(&table->entries, &table->pending_add, node);
478 }
479
480 static void
table_pending_add_free(struct table * table)481 table_pending_add_free(struct table *table)
482 {
483 for ( ; ; ) {
484 struct rte_swx_table_entry *entry;
485
486 entry = TAILQ_FIRST(&table->pending_add);
487 if (!entry)
488 break;
489
490 TAILQ_REMOVE(&table->pending_add, entry, node);
491 table_entry_free(entry);
492 }
493 }
494
495 static struct rte_swx_table_entry *
table_pending_modify0_find(struct table * table,struct rte_swx_table_entry * entry)496 table_pending_modify0_find(struct table *table,
497 struct rte_swx_table_entry *entry)
498 {
499 struct rte_swx_table_entry *e;
500
501 TAILQ_FOREACH(e, &table->pending_modify0, node)
502 if (!table_entry_keycmp(table, entry, e))
503 return e; /* Found. */
504
505 return NULL; /* Not found. */
506 }
507
508 static void
table_pending_modify0_admit(struct table * table)509 table_pending_modify0_admit(struct table *table)
510 {
511 TAILQ_CONCAT(&table->entries, &table->pending_modify0, node);
512 }
513
514 static void
table_pending_modify0_free(struct table * table)515 table_pending_modify0_free(struct table *table)
516 {
517 for ( ; ; ) {
518 struct rte_swx_table_entry *entry;
519
520 entry = TAILQ_FIRST(&table->pending_modify0);
521 if (!entry)
522 break;
523
524 TAILQ_REMOVE(&table->pending_modify0, entry, node);
525 table_entry_free(entry);
526 }
527 }
528
529 static struct rte_swx_table_entry *
table_pending_modify1_find(struct table * table,struct rte_swx_table_entry * entry)530 table_pending_modify1_find(struct table *table,
531 struct rte_swx_table_entry *entry)
532 {
533 struct rte_swx_table_entry *e;
534
535 TAILQ_FOREACH(e, &table->pending_modify1, node)
536 if (!table_entry_keycmp(table, entry, e))
537 return e; /* Found. */
538
539 return NULL; /* Not found. */
540 }
541
542 static void
table_pending_modify1_admit(struct table * table)543 table_pending_modify1_admit(struct table *table)
544 {
545 TAILQ_CONCAT(&table->entries, &table->pending_modify1, node);
546 }
547
548 static void
table_pending_modify1_free(struct table * table)549 table_pending_modify1_free(struct table *table)
550 {
551 for ( ; ; ) {
552 struct rte_swx_table_entry *entry;
553
554 entry = TAILQ_FIRST(&table->pending_modify1);
555 if (!entry)
556 break;
557
558 TAILQ_REMOVE(&table->pending_modify1, entry, node);
559 table_entry_free(entry);
560 }
561 }
562
563 static struct rte_swx_table_entry *
table_pending_delete_find(struct table * table,struct rte_swx_table_entry * entry)564 table_pending_delete_find(struct table *table,
565 struct rte_swx_table_entry *entry)
566 {
567 struct rte_swx_table_entry *e;
568
569 TAILQ_FOREACH(e, &table->pending_delete, node)
570 if (!table_entry_keycmp(table, entry, e))
571 return e; /* Found. */
572
573 return NULL; /* Not found. */
574 }
575
576 static void
table_pending_delete_admit(struct table * table)577 table_pending_delete_admit(struct table *table)
578 {
579 TAILQ_CONCAT(&table->entries, &table->pending_delete, node);
580 }
581
582 static void
table_pending_delete_free(struct table * table)583 table_pending_delete_free(struct table *table)
584 {
585 for ( ; ; ) {
586 struct rte_swx_table_entry *entry;
587
588 entry = TAILQ_FIRST(&table->pending_delete);
589 if (!entry)
590 break;
591
592 TAILQ_REMOVE(&table->pending_delete, entry, node);
593 table_entry_free(entry);
594 }
595 }
596
597 static void
table_pending_default_free(struct table * table)598 table_pending_default_free(struct table *table)
599 {
600 if (!table->pending_default)
601 return;
602
603 free(table->pending_default->action_data);
604 free(table->pending_default);
605 table->pending_default = NULL;
606 }
607
608 static void
table_free(struct rte_swx_ctl_pipeline * ctl)609 table_free(struct rte_swx_ctl_pipeline *ctl)
610 {
611 uint32_t i;
612
613 if (!ctl->tables)
614 return;
615
616 for (i = 0; i < ctl->info.n_tables; i++) {
617 struct table *table = &ctl->tables[i];
618
619 free(table->mf);
620 free(table->actions);
621 free(table->params.key_mask0);
622
623 table_entries_free(table);
624 table_pending_add_free(table);
625 table_pending_modify0_free(table);
626 table_pending_modify1_free(table);
627 table_pending_delete_free(table);
628 table_pending_default_free(table);
629 }
630
631 free(ctl->tables);
632 ctl->tables = NULL;
633 }
634
635 static void
table_state_free(struct rte_swx_ctl_pipeline * ctl)636 table_state_free(struct rte_swx_ctl_pipeline *ctl)
637 {
638 uint32_t i;
639
640 if (!ctl->ts_next)
641 return;
642
643 /* For each table, free its table state. */
644 for (i = 0; i < ctl->info.n_tables; i++) {
645 struct table *table = &ctl->tables[i];
646 struct rte_swx_table_state *ts = &ctl->ts_next[i];
647
648 /* Default action data. */
649 free(ts->default_action_data);
650
651 /* Table object. */
652 if (!table->is_stub && table->ops.free && ts->obj)
653 table->ops.free(ts->obj);
654 }
655
656 free(ctl->ts_next);
657 ctl->ts_next = NULL;
658 }
659
660 static int
table_state_create(struct rte_swx_ctl_pipeline * ctl)661 table_state_create(struct rte_swx_ctl_pipeline *ctl)
662 {
663 int status = 0;
664 uint32_t i;
665
666 ctl->ts_next = calloc(ctl->info.n_tables,
667 sizeof(struct rte_swx_table_state));
668 if (!ctl->ts_next) {
669 status = -ENOMEM;
670 goto error;
671 }
672
673 for (i = 0; i < ctl->info.n_tables; i++) {
674 struct table *table = &ctl->tables[i];
675 struct rte_swx_table_state *ts = &ctl->ts[i];
676 struct rte_swx_table_state *ts_next = &ctl->ts_next[i];
677
678 /* Table object. */
679 if (!table->is_stub) {
680 ts_next->obj = table->ops.create(&table->params,
681 &table->entries,
682 table->info.args,
683 ctl->numa_node);
684 if (!ts_next->obj) {
685 status = -ENODEV;
686 goto error;
687 }
688 }
689
690 /* Default action data: duplicate from current table state. */
691 ts_next->default_action_data =
692 malloc(table->params.action_data_size);
693 if (!ts_next->default_action_data) {
694 status = -ENOMEM;
695 goto error;
696 }
697
698 memcpy(ts_next->default_action_data,
699 ts->default_action_data,
700 table->params.action_data_size);
701
702 ts_next->default_action_id = ts->default_action_id;
703 }
704
705 return 0;
706
707 error:
708 table_state_free(ctl);
709 return status;
710 }
711
712 void
rte_swx_ctl_pipeline_free(struct rte_swx_ctl_pipeline * ctl)713 rte_swx_ctl_pipeline_free(struct rte_swx_ctl_pipeline *ctl)
714 {
715 if (!ctl)
716 return;
717
718 action_free(ctl);
719
720 table_state_free(ctl);
721
722 table_free(ctl);
723
724 free(ctl);
725 }
726
727 struct rte_swx_ctl_pipeline *
rte_swx_ctl_pipeline_create(struct rte_swx_pipeline * p)728 rte_swx_ctl_pipeline_create(struct rte_swx_pipeline *p)
729 {
730 struct rte_swx_ctl_pipeline *ctl = NULL;
731 uint32_t i;
732 int status;
733
734 if (!p)
735 goto error;
736
737 ctl = calloc(1, sizeof(struct rte_swx_ctl_pipeline));
738 if (!ctl)
739 goto error;
740
741 /* info. */
742 status = rte_swx_ctl_pipeline_info_get(p, &ctl->info);
743 if (status)
744 goto error;
745
746 /* numa_node. */
747 status = rte_swx_ctl_pipeline_numa_node_get(p, &ctl->numa_node);
748 if (status)
749 goto error;
750
751 /* p. */
752 ctl->p = p;
753
754 /* actions. */
755 ctl->actions = calloc(ctl->info.n_actions, sizeof(struct action));
756 if (!ctl->actions)
757 goto error;
758
759 for (i = 0; i < ctl->info.n_actions; i++) {
760 struct action *a = &ctl->actions[i];
761 uint32_t j;
762
763 /* info. */
764 status = rte_swx_ctl_action_info_get(p, i, &a->info);
765 if (status)
766 goto error;
767
768 /* args. */
769 a->args = calloc(a->info.n_args,
770 sizeof(struct rte_swx_ctl_action_arg_info));
771 if (!a->args)
772 goto error;
773
774 for (j = 0; j < a->info.n_args; j++) {
775 status = rte_swx_ctl_action_arg_info_get(p,
776 i,
777 j,
778 &a->args[j]);
779 if (status)
780 goto error;
781 }
782
783 /* data_size. */
784 for (j = 0; j < a->info.n_args; j++) {
785 struct rte_swx_ctl_action_arg_info *info = &a->args[j];
786
787 a->data_size += info->n_bits;
788 }
789
790 a->data_size = (a->data_size + 7) / 8;
791 }
792
793 /* tables. */
794 ctl->tables = calloc(ctl->info.n_tables, sizeof(struct table));
795 if (!ctl->tables)
796 goto error;
797
798 for (i = 0; i < ctl->info.n_tables; i++) {
799 struct table *t = &ctl->tables[i];
800
801 TAILQ_INIT(&t->entries);
802 TAILQ_INIT(&t->pending_add);
803 TAILQ_INIT(&t->pending_modify0);
804 TAILQ_INIT(&t->pending_modify1);
805 TAILQ_INIT(&t->pending_delete);
806 }
807
808 for (i = 0; i < ctl->info.n_tables; i++) {
809 struct table *t = &ctl->tables[i];
810 uint32_t j;
811
812 /* info. */
813 status = rte_swx_ctl_table_info_get(p, i, &t->info);
814 if (status)
815 goto error;
816
817 /* mf. */
818 t->mf = calloc(t->info.n_match_fields,
819 sizeof(struct rte_swx_ctl_table_match_field_info));
820 if (!t->mf)
821 goto error;
822
823 for (j = 0; j < t->info.n_match_fields; j++) {
824 status = rte_swx_ctl_table_match_field_info_get(p,
825 i,
826 j,
827 &t->mf[j]);
828 if (status)
829 goto error;
830 }
831
832 /* actions. */
833 t->actions = calloc(t->info.n_actions,
834 sizeof(struct rte_swx_ctl_table_action_info));
835 if (!t->actions)
836 goto error;
837
838 for (j = 0; j < t->info.n_actions; j++) {
839 status = rte_swx_ctl_table_action_info_get(p,
840 i,
841 j,
842 &t->actions[j]);
843 if (status ||
844 t->actions[j].action_id >= ctl->info.n_actions)
845 goto error;
846 }
847
848 /* ops, is_stub. */
849 status = rte_swx_ctl_table_ops_get(p, i, &t->ops, &t->is_stub);
850 if (status)
851 goto error;
852
853 if ((t->is_stub && t->info.n_match_fields) ||
854 (!t->is_stub && !t->info.n_match_fields))
855 goto error;
856
857 /* params. */
858 status = table_params_get(ctl, i);
859 if (status)
860 goto error;
861 }
862
863 /* ts. */
864 status = rte_swx_pipeline_table_state_get(p, &ctl->ts);
865 if (status)
866 goto error;
867
868 /* ts_next. */
869 status = table_state_create(ctl);
870 if (status)
871 goto error;
872
873 return ctl;
874
875 error:
876 rte_swx_ctl_pipeline_free(ctl);
877 return NULL;
878 }
879
880 int
rte_swx_ctl_pipeline_table_entry_add(struct rte_swx_ctl_pipeline * ctl,const char * table_name,struct rte_swx_table_entry * entry)881 rte_swx_ctl_pipeline_table_entry_add(struct rte_swx_ctl_pipeline *ctl,
882 const char *table_name,
883 struct rte_swx_table_entry *entry)
884 {
885 struct table *table;
886 struct rte_swx_table_entry *new_entry, *existing_entry;
887 uint32_t table_id;
888
889 CHECK(ctl, EINVAL);
890 CHECK(table_name && table_name[0], EINVAL);
891
892 table = table_find(ctl, table_name);
893 CHECK(table, EINVAL);
894 table_id = table - ctl->tables;
895
896 new_entry = table_entry_duplicate(ctl, table_id, entry, 1, 1);
897 CHECK(new_entry, ENOMEM);
898
899 /* The new entry is found in the table->entries list:
900 * - Add the new entry to the table->pending_modify1 list;
901 * - Move the existing entry from the table->entries list to the
902 * table->pending_modify0 list.
903 */
904 existing_entry = table_entries_find(table, entry);
905 if (existing_entry) {
906 TAILQ_INSERT_TAIL(&table->pending_modify1,
907 new_entry,
908 node);
909
910 TAILQ_REMOVE(&table->entries,
911 existing_entry,
912 node);
913
914 TAILQ_INSERT_TAIL(&table->pending_modify0,
915 existing_entry,
916 node);
917
918 return 0;
919 }
920
921 /* The new entry is found in the table->pending_add list:
922 * - Replace the entry in the table->pending_add list with the new entry
923 * (and free the replaced entry).
924 */
925 existing_entry = table_pending_add_find(table, entry);
926 if (existing_entry) {
927 TAILQ_INSERT_AFTER(&table->pending_add,
928 existing_entry,
929 new_entry,
930 node);
931
932 TAILQ_REMOVE(&table->pending_add,
933 existing_entry,
934 node);
935
936 table_entry_free(existing_entry);
937
938 return 0;
939 }
940
941 /* The new entry is found in the table->pending_modify1 list:
942 * - Replace the entry in the table->pending_modify1 list with the new
943 * entry (and free the replaced entry).
944 */
945 existing_entry = table_pending_modify1_find(table, entry);
946 if (existing_entry) {
947 TAILQ_INSERT_AFTER(&table->pending_modify1,
948 existing_entry,
949 new_entry,
950 node);
951
952 TAILQ_REMOVE(&table->pending_modify1,
953 existing_entry,
954 node);
955
956 table_entry_free(existing_entry);
957
958 return 0;
959 }
960
961 /* The new entry is found in the table->pending_delete list:
962 * - Add the new entry to the table->pending_modify1 list;
963 * - Move the existing entry from the table->pending_delete list to the
964 * table->pending_modify0 list.
965 */
966 existing_entry = table_pending_delete_find(table, entry);
967 if (existing_entry) {
968 TAILQ_INSERT_TAIL(&table->pending_modify1,
969 new_entry,
970 node);
971
972 TAILQ_REMOVE(&table->pending_delete,
973 existing_entry,
974 node);
975
976 TAILQ_INSERT_TAIL(&table->pending_modify0,
977 existing_entry,
978 node);
979
980 return 0;
981 }
982
983 /* The new entry is not found in any of the above lists:
984 * - Add the new entry to the table->pending_add list.
985 */
986 TAILQ_INSERT_TAIL(&table->pending_add, new_entry, node);
987
988 return 0;
989 }
990
991 int
rte_swx_ctl_pipeline_table_entry_delete(struct rte_swx_ctl_pipeline * ctl,const char * table_name,struct rte_swx_table_entry * entry)992 rte_swx_ctl_pipeline_table_entry_delete(struct rte_swx_ctl_pipeline *ctl,
993 const char *table_name,
994 struct rte_swx_table_entry *entry)
995 {
996 struct table *table;
997 struct rte_swx_table_entry *existing_entry;
998 uint32_t table_id;
999
1000 CHECK(ctl, EINVAL);
1001
1002 CHECK(table_name && table_name[0], EINVAL);
1003 table = table_find(ctl, table_name);
1004 CHECK(table, EINVAL);
1005 table_id = table - ctl->tables;
1006
1007 CHECK(entry, EINVAL);
1008 CHECK(!table_entry_check(ctl, table_id, entry, 1, 0), EINVAL);
1009
1010 /* The entry is found in the table->entries list:
1011 * - Move the existing entry from the table->entries list to to the
1012 * table->pending_delete list.
1013 */
1014 existing_entry = table_entries_find(table, entry);
1015 if (existing_entry) {
1016 TAILQ_REMOVE(&table->entries,
1017 existing_entry,
1018 node);
1019
1020 TAILQ_INSERT_TAIL(&table->pending_delete,
1021 existing_entry,
1022 node);
1023
1024 return 0;
1025 }
1026
1027 /* The entry is found in the table->pending_add list:
1028 * - Remove the entry from the table->pending_add list and free it.
1029 */
1030 existing_entry = table_pending_add_find(table, entry);
1031 if (existing_entry) {
1032 TAILQ_REMOVE(&table->pending_add,
1033 existing_entry,
1034 node);
1035
1036 table_entry_free(existing_entry);
1037 }
1038
1039 /* The entry is found in the table->pending_modify1 list:
1040 * - Free the entry in the table->pending_modify1 list;
1041 * - Move the existing entry from the table->pending_modify0 list to the
1042 * table->pending_delete list.
1043 */
1044 existing_entry = table_pending_modify1_find(table, entry);
1045 if (existing_entry) {
1046 struct rte_swx_table_entry *real_existing_entry;
1047
1048 TAILQ_REMOVE(&table->pending_modify1,
1049 existing_entry,
1050 node);
1051
1052 table_entry_free(existing_entry);
1053
1054 real_existing_entry = table_pending_modify0_find(table, entry);
1055 CHECK(real_existing_entry, EINVAL); /* Coverity. */
1056
1057 TAILQ_REMOVE(&table->pending_modify0,
1058 real_existing_entry,
1059 node);
1060
1061 TAILQ_INSERT_TAIL(&table->pending_delete,
1062 real_existing_entry,
1063 node);
1064
1065 return 0;
1066 }
1067
1068 /* The entry is found in the table->pending_delete list:
1069 * - Do nothing: the existing entry is already in the
1070 * table->pending_delete list, i.e. already marked for delete, so
1071 * simply keep it there as it is.
1072 */
1073
1074 /* The entry is not found in any of the above lists:
1075 * - Do nothing: no existing entry to delete.
1076 */
1077
1078 return 0;
1079 }
1080
1081 int
rte_swx_ctl_pipeline_table_default_entry_add(struct rte_swx_ctl_pipeline * ctl,const char * table_name,struct rte_swx_table_entry * entry)1082 rte_swx_ctl_pipeline_table_default_entry_add(struct rte_swx_ctl_pipeline *ctl,
1083 const char *table_name,
1084 struct rte_swx_table_entry *entry)
1085 {
1086 struct table *table;
1087 struct rte_swx_table_entry *new_entry;
1088 uint32_t table_id;
1089
1090 CHECK(ctl, EINVAL);
1091
1092 CHECK(table_name && table_name[0], EINVAL);
1093 table = table_find(ctl, table_name);
1094 CHECK(table, EINVAL);
1095 table_id = table - ctl->tables;
1096 CHECK(!table->info.default_action_is_const, EINVAL);
1097
1098 new_entry = table_entry_duplicate(ctl, table_id, entry, 0, 1);
1099 CHECK(new_entry, ENOMEM);
1100
1101 table_pending_default_free(table);
1102
1103 table->pending_default = new_entry;
1104 return 0;
1105 }
1106
1107 static int
table_rollfwd0(struct rte_swx_ctl_pipeline * ctl,uint32_t table_id)1108 table_rollfwd0(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)
1109 {
1110 struct table *table = &ctl->tables[table_id];
1111 struct rte_swx_table_state *ts_next = &ctl->ts_next[table_id];
1112 struct rte_swx_table_entry *entry;
1113
1114 /* Reset counters. */
1115 table->n_add = 0;
1116 table->n_modify = 0;
1117 table->n_delete = 0;
1118
1119 /* Add pending rules. */
1120 TAILQ_FOREACH(entry, &table->pending_add, node) {
1121 int status;
1122
1123 status = table->ops.add(ts_next->obj, entry);
1124 if (status)
1125 return status;
1126
1127 table->n_add++;
1128 }
1129
1130 /* Modify pending rules. */
1131 TAILQ_FOREACH(entry, &table->pending_modify1, node) {
1132 int status;
1133
1134 status = table->ops.add(ts_next->obj, entry);
1135 if (status)
1136 return status;
1137
1138 table->n_modify++;
1139 }
1140
1141 /* Delete pending rules. */
1142 TAILQ_FOREACH(entry, &table->pending_delete, node) {
1143 int status;
1144
1145 status = table->ops.del(ts_next->obj, entry);
1146 if (status)
1147 return status;
1148
1149 table->n_delete++;
1150 }
1151
1152 return 0;
1153 }
1154
1155 static void
table_rollfwd1(struct rte_swx_ctl_pipeline * ctl,uint32_t table_id)1156 table_rollfwd1(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)
1157 {
1158 struct table *table = &ctl->tables[table_id];
1159 struct rte_swx_table_state *ts_next = &ctl->ts_next[table_id];
1160 struct action *a;
1161 uint8_t *action_data;
1162 uint64_t action_id;
1163
1164 /* Copy the pending default entry. */
1165 if (!table->pending_default)
1166 return;
1167
1168 action_id = table->pending_default->action_id;
1169 action_data = table->pending_default->action_data;
1170 a = &ctl->actions[action_id];
1171
1172 memcpy(ts_next->default_action_data,
1173 action_data,
1174 a->data_size);
1175
1176 ts_next->default_action_id = action_id;
1177 }
1178
1179 static void
table_rollfwd2(struct rte_swx_ctl_pipeline * ctl,uint32_t table_id)1180 table_rollfwd2(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)
1181 {
1182 struct table *table = &ctl->tables[table_id];
1183
1184 /* Move all the pending add entries to the table, as they are now part
1185 * of the table.
1186 */
1187 table_pending_add_admit(table);
1188
1189 /* Move all the pending modify1 entries to table, are they are now part
1190 * of the table. Free up all the pending modify0 entries, as they are no
1191 * longer part of the table.
1192 */
1193 table_pending_modify1_admit(table);
1194 table_pending_modify0_free(table);
1195
1196 /* Free up all the pending delete entries, as they are no longer part of
1197 * the table.
1198 */
1199 table_pending_delete_free(table);
1200
1201 /* Free up the pending default entry, as it is now part of the table. */
1202 table_pending_default_free(table);
1203 }
1204
1205 static void
table_rollback(struct rte_swx_ctl_pipeline * ctl,uint32_t table_id)1206 table_rollback(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)
1207 {
1208 struct table *table = &ctl->tables[table_id];
1209 struct rte_swx_table_state *ts_next = &ctl->ts_next[table_id];
1210 struct rte_swx_table_entry *entry;
1211
1212 /* Add back all the entries that were just deleted. */
1213 TAILQ_FOREACH(entry, &table->pending_delete, node) {
1214 if (!table->n_delete)
1215 break;
1216
1217 table->ops.add(ts_next->obj, entry);
1218 table->n_delete--;
1219 }
1220
1221 /* Add back the old copy for all the entries that were just
1222 * modified.
1223 */
1224 TAILQ_FOREACH(entry, &table->pending_modify0, node) {
1225 if (!table->n_modify)
1226 break;
1227
1228 table->ops.add(ts_next->obj, entry);
1229 table->n_modify--;
1230 }
1231
1232 /* Delete all the entries that were just added. */
1233 TAILQ_FOREACH(entry, &table->pending_add, node) {
1234 if (!table->n_add)
1235 break;
1236
1237 table->ops.del(ts_next->obj, entry);
1238 table->n_add--;
1239 }
1240 }
1241
1242 static void
table_abort(struct rte_swx_ctl_pipeline * ctl,uint32_t table_id)1243 table_abort(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)
1244 {
1245 struct table *table = &ctl->tables[table_id];
1246
1247 /* Free up all the pending add entries, as none of them is part of the
1248 * table.
1249 */
1250 table_pending_add_free(table);
1251
1252 /* Free up all the pending modify1 entries, as none of them made it to
1253 * the table. Add back all the pending modify0 entries, as none of them
1254 * was deleted from the table.
1255 */
1256 table_pending_modify1_free(table);
1257 table_pending_modify0_admit(table);
1258
1259 /* Add back all the pending delete entries, as none of them was deleted
1260 * from the table.
1261 */
1262 table_pending_delete_admit(table);
1263
1264 /* Free up the pending default entry, as it is no longer going to be
1265 * added to the table.
1266 */
1267 table_pending_default_free(table);
1268 }
1269
1270 int
rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline * ctl,int abort_on_fail)1271 rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail)
1272 {
1273 struct rte_swx_table_state *ts;
1274 int status = 0;
1275 uint32_t i;
1276
1277 CHECK(ctl, EINVAL);
1278
1279 /* Operate the changes on the current ts_next before it becomes the new
1280 * ts.
1281 */
1282 for (i = 0; i < ctl->info.n_tables; i++) {
1283 status = table_rollfwd0(ctl, i);
1284 if (status)
1285 goto rollback;
1286 }
1287
1288 for (i = 0; i < ctl->info.n_tables; i++)
1289 table_rollfwd1(ctl, i);
1290
1291 /* Swap the table state for the data plane. The current ts and ts_next
1292 * become the new ts_next and ts, respectively.
1293 */
1294 rte_swx_pipeline_table_state_set(ctl->p, ctl->ts_next);
1295 usleep(100);
1296 ts = ctl->ts;
1297 ctl->ts = ctl->ts_next;
1298 ctl->ts_next = ts;
1299
1300 /* Operate the changes on the current ts_next, which is the previous ts.
1301 */
1302 for (i = 0; i < ctl->info.n_tables; i++) {
1303 table_rollfwd0(ctl, i);
1304 table_rollfwd1(ctl, i);
1305 table_rollfwd2(ctl, i);
1306 }
1307
1308 return 0;
1309
1310 rollback:
1311 for (i = 0; i < ctl->info.n_tables; i++) {
1312 table_rollback(ctl, i);
1313 if (abort_on_fail)
1314 table_abort(ctl, i);
1315 }
1316
1317 return status;
1318 }
1319
1320 void
rte_swx_ctl_pipeline_abort(struct rte_swx_ctl_pipeline * ctl)1321 rte_swx_ctl_pipeline_abort(struct rte_swx_ctl_pipeline *ctl)
1322 {
1323 uint32_t i;
1324
1325 if (!ctl)
1326 return;
1327
1328 for (i = 0; i < ctl->info.n_tables; i++)
1329 table_abort(ctl, i);
1330 }
1331
1332 #define RTE_SWX_CTL_ENTRY_TOKENS_MAX 256
1333
1334 struct rte_swx_table_entry *
rte_swx_ctl_pipeline_table_entry_read(struct rte_swx_ctl_pipeline * ctl,const char * table_name,const char * string)1335 rte_swx_ctl_pipeline_table_entry_read(struct rte_swx_ctl_pipeline *ctl,
1336 const char *table_name,
1337 const char *string)
1338 {
1339 char *tokens[RTE_SWX_CTL_ENTRY_TOKENS_MAX];
1340 struct table *table;
1341 struct action *action;
1342 struct rte_swx_table_entry *entry = NULL;
1343 char *s0 = NULL, *s;
1344 uint32_t n_tokens = 0, arg_offset = 0, i;
1345
1346 /* Check input arguments. */
1347 if (!ctl)
1348 goto error;
1349
1350 if (!table_name || !table_name[0])
1351 goto error;
1352
1353 table = table_find(ctl, table_name);
1354 if (!table)
1355 goto error;
1356
1357 if (!string || !string[0])
1358 goto error;
1359
1360 /* Memory allocation. */
1361 s0 = strdup(string);
1362 if (!s0)
1363 goto error;
1364
1365 entry = table_entry_alloc(table);
1366 if (!entry)
1367 goto error;
1368
1369 /* Parse the string into tokens. */
1370 for (s = s0; ; ) {
1371 char *token;
1372
1373 token = strtok_r(s, " \f\n\r\t\v", &s);
1374 if (!token)
1375 break;
1376
1377 if (n_tokens >= RTE_SWX_CTL_ENTRY_TOKENS_MAX)
1378 goto error;
1379
1380 tokens[n_tokens] = token;
1381 n_tokens++;
1382 }
1383
1384 if ((n_tokens < 3 + table->info.n_match_fields) ||
1385 strcmp(tokens[0], "match") ||
1386 strcmp(tokens[1 + table->info.n_match_fields], "action"))
1387 goto error;
1388
1389 action = action_find(ctl, tokens[2 + table->info.n_match_fields]);
1390 if (!action)
1391 goto error;
1392
1393 if (n_tokens != 3 + table->info.n_match_fields +
1394 action->info.n_args * 2)
1395 goto error;
1396
1397 /*
1398 * Match.
1399 */
1400 for (i = 0; i < table->info.n_match_fields; i++) {
1401 struct rte_swx_ctl_table_match_field_info *mf = &table->mf[i];
1402 char *mf_val = tokens[1 + i];
1403 uint64_t val;
1404
1405 val = strtoull(mf_val, &mf_val, 0);
1406 if (mf_val[0])
1407 goto error;
1408
1409 /* Endianness conversion. */
1410 if (mf->is_header)
1411 val = field_hton(val, mf->n_bits);
1412
1413 /* Copy key and key_mask to entry. */
1414 memcpy(&entry->key[(mf->offset - table->mf[0].offset) / 8],
1415 (uint8_t *)&val,
1416 mf->n_bits / 8);
1417
1418 /* TBD Set entry->key_mask for wildcard and LPM tables. */
1419 }
1420
1421 /*
1422 * Action.
1423 */
1424 /* action_id. */
1425 entry->action_id = action - ctl->actions;
1426
1427 /* action_data. */
1428 for (i = 0; i < action->info.n_args; i++) {
1429 struct rte_swx_ctl_action_arg_info *arg = &action->args[i];
1430 char *arg_name, *arg_val;
1431 uint64_t val;
1432 int is_nbo = 0;
1433
1434 arg_name = tokens[3 + table->info.n_match_fields + i * 2];
1435 arg_val = tokens[3 + table->info.n_match_fields + i * 2 + 1];
1436
1437 if (strcmp(arg_name, arg->name) ||
1438 (strlen(arg_val) < 4) ||
1439 ((arg_val[0] != 'H') && (arg_val[0] != 'N')) ||
1440 (arg_val[1] != '(') ||
1441 (arg_val[strlen(arg_val) - 1] != ')'))
1442 goto error;
1443
1444 if (arg_val[0] == 'N')
1445 is_nbo = 1;
1446
1447 arg_val[strlen(arg_val) - 1] = 0; /* Remove the ')'. */
1448 arg_val += 2; /* Remove the "H(" or "N(". */
1449
1450 val = strtoull(arg_val, &arg_val, 0);
1451 if (arg_val[0])
1452 goto error;
1453
1454 /* Endianness conversion. */
1455 if (is_nbo)
1456 val = field_hton(val, arg->n_bits);
1457
1458 /* Copy to entry. */
1459 memcpy(&entry->action_data[arg_offset],
1460 (uint8_t *)&val,
1461 arg->n_bits / 8);
1462
1463 arg_offset += arg->n_bits / 8;
1464 }
1465
1466 free(s0);
1467 return entry;
1468
1469 error:
1470 table_entry_free(entry);
1471 free(s0);
1472 return NULL;
1473 }
1474
1475 int
rte_swx_ctl_pipeline_table_fprintf(FILE * f,struct rte_swx_ctl_pipeline * ctl,const char * table_name)1476 rte_swx_ctl_pipeline_table_fprintf(FILE *f,
1477 struct rte_swx_ctl_pipeline *ctl,
1478 const char *table_name)
1479 {
1480 struct table *table;
1481 struct rte_swx_table_entry *entry;
1482 uint32_t n_entries = 0, i;
1483
1484 if (!f || !ctl || !table_name || !table_name[0])
1485 return -EINVAL;
1486
1487 table = table_find(ctl, table_name);
1488 if (!table)
1489 return -EINVAL;
1490
1491 /* Table. */
1492 fprintf(f, "# Table %s: key size %u bytes, key offset %u, key mask [",
1493 table->info.name,
1494 table->params.key_size,
1495 table->params.key_offset);
1496
1497 for (i = 0; i < table->params.key_size; i++)
1498 fprintf(f, "%02x", table->params.key_mask0[i]);
1499
1500 fprintf(f, "], action data size %u bytes\n",
1501 table->params.action_data_size);
1502
1503 /* Table entries. */
1504 TAILQ_FOREACH(entry, &table->entries, node) {
1505 struct action *action = &ctl->actions[entry->action_id];
1506
1507 fprintf(f, "match ");
1508 for (i = 0; i < table->params.key_size; i++)
1509 fprintf(f, "%02x", entry->key[i]);
1510
1511 fprintf(f, " action %s ", action->info.name);
1512 for (i = 0; i < action->data_size; i++)
1513 fprintf(f, "%02x", entry->action_data[i]);
1514
1515 fprintf(f, "\n");
1516 n_entries++;
1517 }
1518
1519 TAILQ_FOREACH(entry, &table->pending_modify0, node) {
1520 struct action *action = &ctl->actions[entry->action_id];
1521
1522 fprintf(f, "match ");
1523 for (i = 0; i < table->params.key_size; i++)
1524 fprintf(f, "%02x", entry->key[i]);
1525
1526 fprintf(f, " action %s ", action->info.name);
1527 for (i = 0; i < action->data_size; i++)
1528 fprintf(f, "%02x", entry->action_data[i]);
1529
1530 fprintf(f, "\n");
1531 n_entries++;
1532 }
1533
1534 TAILQ_FOREACH(entry, &table->pending_delete, node) {
1535 struct action *action = &ctl->actions[entry->action_id];
1536
1537 fprintf(f, "match ");
1538 for (i = 0; i < table->params.key_size; i++)
1539 fprintf(f, "%02x", entry->key[i]);
1540
1541 fprintf(f, " action %s ", action->info.name);
1542 for (i = 0; i < action->data_size; i++)
1543 fprintf(f, "%02x", entry->action_data[i]);
1544
1545 fprintf(f, "\n");
1546 n_entries++;
1547 }
1548
1549 fprintf(f, "# Table %s currently has %u entries.\n",
1550 table_name,
1551 n_entries);
1552 return 0;
1553 }
1554