xref: /dpdk/lib/pipeline/rte_swx_ctl.c (revision 8b6502f28685eb6395292470ff28e1ff3271aa32)
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_table_selector.h>
14 
15 #include "rte_swx_ctl.h"
16 
17 #define CHECK(condition, err_code)                                             \
18 do {                                                                           \
19 	if (!(condition))                                                      \
20 		return -(err_code);                                            \
21 } while (0)
22 
23 #define ntoh64(x) rte_be_to_cpu_64(x)
24 #define hton64(x) rte_cpu_to_be_64(x)
25 
26 #if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
27 #define field_ntoh(val, n_bits) (ntoh64((val) << (64 - n_bits)))
28 #define field_hton(val, n_bits) (hton64((val) << (64 - n_bits)))
29 #else
30 #define field_ntoh(val, n_bits) (val)
31 #define field_hton(val, n_bits) (val)
32 #endif
33 
34 struct action {
35 	struct rte_swx_ctl_action_info info;
36 	struct rte_swx_ctl_action_arg_info *args;
37 	uint32_t data_size;
38 };
39 
40 struct table {
41 	struct rte_swx_ctl_table_info info;
42 	struct rte_swx_ctl_table_match_field_info *mf;
43 
44 	/* Match field with the smallest offset. */
45 	struct rte_swx_ctl_table_match_field_info *mf_first;
46 
47 	/* Match field with the biggest offset. */
48 	struct rte_swx_ctl_table_match_field_info *mf_last;
49 
50 	struct rte_swx_ctl_table_action_info *actions;
51 	struct rte_swx_table_ops ops;
52 	struct rte_swx_table_params params;
53 
54 	/* Set of "stable" keys: these keys are currently part of the table;
55 	 * these keys will be preserved with no action data changes after the
56 	 * next commit.
57 	 */
58 	struct rte_swx_table_entry_list entries;
59 
60 	/* Set of new keys: these keys are currently NOT part of the table;
61 	 * these keys will be added to the table on the next commit, if
62 	 * the commit operation is successful.
63 	 */
64 	struct rte_swx_table_entry_list pending_add;
65 
66 	/* Set of keys to be modified: these keys are currently part of the
67 	 * table; these keys are still going to be part of the table after the
68 	 * next commit, but their action data will be modified if the commit
69 	 * operation is successful. The modify0 list contains the keys with the
70 	 * current action data, the modify1 list contains the keys with the
71 	 * modified action data.
72 	 */
73 	struct rte_swx_table_entry_list pending_modify0;
74 	struct rte_swx_table_entry_list pending_modify1;
75 
76 	/* Set of keys to be deleted: these keys are currently part of the
77 	 * table; these keys are to be deleted from the table on the next
78 	 * commit, if the commit operation is successful.
79 	 */
80 	struct rte_swx_table_entry_list pending_delete;
81 
82 	/* The pending default action: this is NOT the current default action;
83 	 * this will be the new default action after the next commit, if the
84 	 * next commit operation is successful.
85 	 */
86 	struct rte_swx_table_entry *pending_default;
87 
88 	int is_stub;
89 	uint32_t n_add;
90 	uint32_t n_modify;
91 	uint32_t n_delete;
92 };
93 
94 struct selector {
95 	/* Selector table info. */
96 	struct rte_swx_ctl_selector_info info;
97 
98 	/* group_id field. */
99 	struct rte_swx_ctl_table_match_field_info group_id_field;
100 
101 	/* selector fields. */
102 	struct rte_swx_ctl_table_match_field_info *selector_fields;
103 
104 	/* member_id field. */
105 	struct rte_swx_ctl_table_match_field_info member_id_field;
106 
107 	/* Current selector table. Array of info.n_groups_max elements.*/
108 	struct rte_swx_table_selector_group **groups;
109 
110 	/* Pending selector table subject to the next commit. Array of info.n_groups_max elements.
111 	 */
112 	struct rte_swx_table_selector_group **pending_groups;
113 
114 	/* Valid flag per group. Array of n_groups_max elements. */
115 	int *groups_added;
116 
117 	/* Pending delete flag per group. Group deletion is subject to the next commit. Array of
118 	 * info.n_groups_max elements.
119 	 */
120 	int *groups_pending_delete;
121 
122 	/* Params. */
123 	struct rte_swx_table_selector_params params;
124 };
125 
126 struct learner {
127 	struct rte_swx_ctl_learner_info info;
128 	struct rte_swx_ctl_table_match_field_info *mf;
129 	struct rte_swx_ctl_table_action_info *actions;
130 	uint32_t action_data_size;
131 
132 	/* The pending default action: this is NOT the current default action;
133 	 * this will be the new default action after the next commit, if the
134 	 * next commit operation is successful.
135 	 */
136 	struct rte_swx_table_entry *pending_default;
137 };
138 
139 struct rte_swx_ctl_pipeline {
140 	struct rte_swx_ctl_pipeline_info info;
141 	struct rte_swx_pipeline *p;
142 	struct action *actions;
143 	struct table *tables;
144 	struct selector *selectors;
145 	struct learner *learners;
146 	struct rte_swx_table_state *ts;
147 	struct rte_swx_table_state *ts_next;
148 	int numa_node;
149 };
150 
151 static struct action *
152 action_find(struct rte_swx_ctl_pipeline *ctl, const char *action_name)
153 {
154 	uint32_t i;
155 
156 	for (i = 0; i < ctl->info.n_actions; i++) {
157 		struct action *a = &ctl->actions[i];
158 
159 		if (!strcmp(action_name, a->info.name))
160 			return a;
161 	}
162 
163 	return NULL;
164 }
165 
166 static void
167 action_free(struct rte_swx_ctl_pipeline *ctl)
168 {
169 	uint32_t i;
170 
171 	if (!ctl->actions)
172 		return;
173 
174 	for (i = 0; i < ctl->info.n_actions; i++) {
175 		struct action *action = &ctl->actions[i];
176 
177 		free(action->args);
178 	}
179 
180 	free(ctl->actions);
181 	ctl->actions = NULL;
182 }
183 
184 static struct table *
185 table_find(struct rte_swx_ctl_pipeline *ctl, const char *table_name)
186 {
187 	uint32_t i;
188 
189 	for (i = 0; i < ctl->info.n_tables; i++) {
190 		struct table *table = &ctl->tables[i];
191 
192 		if (!strcmp(table_name, table->info.name))
193 			return table;
194 	}
195 
196 	return NULL;
197 }
198 
199 static int
200 table_params_get(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)
201 {
202 	struct table *table = &ctl->tables[table_id];
203 	struct rte_swx_ctl_table_match_field_info *first = NULL, *last = NULL;
204 	uint8_t *key_mask = NULL;
205 	enum rte_swx_table_match_type match_type = RTE_SWX_TABLE_MATCH_WILDCARD;
206 	uint32_t key_size = 0, key_offset = 0, action_data_size = 0, i;
207 
208 	if (table->info.n_match_fields) {
209 		uint32_t n_match_fields_em = 0, i;
210 
211 		/* Find first (smallest offset) and last (biggest offset) match fields. */
212 		first = &table->mf[0];
213 		last = &table->mf[0];
214 
215 		for (i = 1; i < table->info.n_match_fields; i++) {
216 			struct rte_swx_ctl_table_match_field_info *f = &table->mf[i];
217 
218 			if (f->offset < first->offset)
219 				first = f;
220 
221 			if (f->offset > last->offset)
222 				last = f;
223 		}
224 
225 		/* match_type. */
226 		for (i = 0; i < table->info.n_match_fields; i++) {
227 			struct rte_swx_ctl_table_match_field_info *f = &table->mf[i];
228 
229 			if (f->match_type == RTE_SWX_TABLE_MATCH_EXACT)
230 				n_match_fields_em++;
231 		}
232 
233 		if (n_match_fields_em == table->info.n_match_fields)
234 			match_type = RTE_SWX_TABLE_MATCH_EXACT;
235 
236 		/* key_offset. */
237 		key_offset = first->offset / 8;
238 
239 		/* key_size. */
240 		key_size = (last->offset + last->n_bits - first->offset) / 8;
241 
242 		/* key_mask. */
243 		key_mask = calloc(1, key_size);
244 		CHECK(key_mask, ENOMEM);
245 
246 		for (i = 0; i < table->info.n_match_fields; i++) {
247 			struct rte_swx_ctl_table_match_field_info *f = &table->mf[i];
248 			uint32_t start;
249 			size_t size;
250 
251 			start = (f->offset - first->offset) / 8;
252 			size = f->n_bits / 8;
253 
254 			memset(&key_mask[start], 0xFF, size);
255 		}
256 	}
257 
258 	/* action_data_size. */
259 	for (i = 0; i < table->info.n_actions; i++) {
260 		uint32_t action_id = table->actions[i].action_id;
261 		struct action *a = &ctl->actions[action_id];
262 
263 		if (a->data_size > action_data_size)
264 			action_data_size = a->data_size;
265 	}
266 
267 	/* Fill in. */
268 	table->params.match_type = match_type;
269 	table->params.key_size = key_size;
270 	table->params.key_offset = key_offset;
271 	table->params.key_mask0 = key_mask;
272 	table->params.action_data_size = action_data_size;
273 	table->params.n_keys_max = table->info.size;
274 
275 	table->mf_first = first;
276 	table->mf_last = last;
277 
278 	return 0;
279 }
280 
281 static void
282 table_entry_free(struct rte_swx_table_entry *entry)
283 {
284 	if (!entry)
285 		return;
286 
287 	free(entry->key);
288 	free(entry->key_mask);
289 	free(entry->action_data);
290 	free(entry);
291 }
292 
293 static struct rte_swx_table_entry *
294 table_entry_alloc(struct table *table)
295 {
296 	struct rte_swx_table_entry *entry;
297 
298 	entry = calloc(1, sizeof(struct rte_swx_table_entry));
299 	if (!entry)
300 		goto error;
301 
302 	/* key, key_mask. */
303 	if (!table->is_stub) {
304 		entry->key = calloc(1, table->params.key_size);
305 		if (!entry->key)
306 			goto error;
307 
308 		if (table->params.match_type != RTE_SWX_TABLE_MATCH_EXACT) {
309 			entry->key_mask = calloc(1, table->params.key_size);
310 			if (!entry->key_mask)
311 				goto error;
312 		}
313 	}
314 
315 	/* action_data. */
316 	if (table->params.action_data_size) {
317 		entry->action_data = calloc(1, table->params.action_data_size);
318 		if (!entry->action_data)
319 			goto error;
320 	}
321 
322 	return entry;
323 
324 error:
325 	table_entry_free(entry);
326 	return NULL;
327 }
328 
329 static int
330 table_entry_key_check_em(struct table *table, struct rte_swx_table_entry *entry)
331 {
332 	uint8_t *key_mask0 = table->params.key_mask0;
333 	uint32_t key_size = table->params.key_size, i;
334 
335 	if (!entry->key_mask)
336 		return 0;
337 
338 	for (i = 0; i < key_size; i++) {
339 		uint8_t km0 = key_mask0[i];
340 		uint8_t km = entry->key_mask[i];
341 
342 		if ((km & km0) != km0)
343 			return -EINVAL;
344 	}
345 
346 	return 0;
347 }
348 
349 static int
350 table_entry_check(struct rte_swx_ctl_pipeline *ctl,
351 		  uint32_t table_id,
352 		  struct rte_swx_table_entry *entry,
353 		  int key_check,
354 		  int data_check)
355 {
356 	struct table *table = &ctl->tables[table_id];
357 	int status;
358 
359 	CHECK(entry, EINVAL);
360 
361 	if (key_check && !table->is_stub) {
362 		/* key. */
363 		CHECK(entry->key, EINVAL);
364 
365 		/* key_mask. */
366 		if (table->params.match_type == RTE_SWX_TABLE_MATCH_EXACT) {
367 			status = table_entry_key_check_em(table, entry);
368 			if (status)
369 				return status;
370 		}
371 	}
372 
373 	if (data_check) {
374 		struct action *a;
375 		struct rte_swx_ctl_table_action_info *tai;
376 		uint32_t i;
377 
378 		/* action_id. */
379 		for (i = 0; i < table->info.n_actions; i++) {
380 			tai = &table->actions[i];
381 
382 			if (entry->action_id == tai->action_id)
383 				break;
384 		}
385 
386 		CHECK(i < table->info.n_actions, EINVAL);
387 
388 		/* action_data. */
389 		a = &ctl->actions[entry->action_id];
390 		CHECK(!(a->data_size && !entry->action_data), EINVAL);
391 
392 		/* When both key_check and data_check are true, we are interested in both the entry
393 		 * key and data, which means the operation is _regular_ table entry add.
394 		 */
395 		if (key_check && !tai->action_is_for_table_entries)
396 			return -EINVAL;
397 
398 		/* When key_check is false while data_check is true, we are only interested in the
399 		 * entry data, which means the operation is _default_ table entry add.
400 		 */
401 		if (!key_check && !tai->action_is_for_default_entry)
402 			return -EINVAL;
403 	}
404 
405 	return 0;
406 }
407 
408 static struct rte_swx_table_entry *
409 table_entry_duplicate(struct rte_swx_ctl_pipeline *ctl,
410 		      uint32_t table_id,
411 		      struct rte_swx_table_entry *entry,
412 		      int key_duplicate,
413 		      int data_duplicate)
414 {
415 	struct table *table = &ctl->tables[table_id];
416 	struct rte_swx_table_entry *new_entry = NULL;
417 
418 	if (!entry)
419 		goto error;
420 
421 	new_entry = calloc(1, sizeof(struct rte_swx_table_entry));
422 	if (!new_entry)
423 		goto error;
424 
425 	if (key_duplicate && !table->is_stub) {
426 		/* key. */
427 		if (!entry->key)
428 			goto error;
429 
430 		new_entry->key = malloc(table->params.key_size);
431 		if (!new_entry->key)
432 			goto error;
433 
434 		memcpy(new_entry->key, entry->key, table->params.key_size);
435 
436 		/* key_signature. */
437 		new_entry->key_signature = entry->key_signature;
438 
439 		/* key_mask. */
440 		if (entry->key_mask) {
441 			new_entry->key_mask = malloc(table->params.key_size);
442 			if (!new_entry->key_mask)
443 				goto error;
444 
445 			memcpy(new_entry->key_mask,
446 			       entry->key_mask,
447 			       table->params.key_size);
448 		}
449 
450 		/* key_priority. */
451 		new_entry->key_priority = entry->key_priority;
452 	}
453 
454 	if (data_duplicate) {
455 		struct action *a;
456 		uint32_t i;
457 
458 		/* action_id. */
459 		for (i = 0; i < table->info.n_actions; i++)
460 			if (entry->action_id == table->actions[i].action_id)
461 				break;
462 
463 		if (i >= table->info.n_actions)
464 			goto error;
465 
466 		new_entry->action_id = entry->action_id;
467 
468 		/* action_data. */
469 		a = &ctl->actions[entry->action_id];
470 		if (a->data_size && !entry->action_data)
471 			goto error;
472 
473 		/* The table layer provisions a constant action data size per
474 		 * entry, which should be the largest data size for all the
475 		 * actions enabled for the current table, and attempts to copy
476 		 * this many bytes each time a table entry is added, even if the
477 		 * specific action requires less data or even no data at all,
478 		 * hence we always have to allocate the max.
479 		 */
480 		new_entry->action_data = calloc(1, table->params.action_data_size);
481 		if (!new_entry->action_data)
482 			goto error;
483 
484 		if (a->data_size)
485 			memcpy(new_entry->action_data,
486 			       entry->action_data,
487 			       a->data_size);
488 	}
489 
490 	return new_entry;
491 
492 error:
493 	table_entry_free(new_entry);
494 	return NULL;
495 }
496 
497 static int
498 table_entry_keycmp(struct table *table,
499 		   struct rte_swx_table_entry *e0,
500 		   struct rte_swx_table_entry *e1)
501 {
502 	uint32_t key_size = table->params.key_size;
503 	uint32_t i;
504 
505 	for (i = 0; i < key_size; i++) {
506 		uint8_t *key_mask0 = table->params.key_mask0;
507 		uint8_t km0, km[2], k[2];
508 
509 		km0 = key_mask0 ? key_mask0[i] : 0xFF;
510 
511 		km[0] = e0->key_mask ? e0->key_mask[i] : 0xFF;
512 		km[1] = e1->key_mask ? e1->key_mask[i] : 0xFF;
513 
514 		k[0] = e0->key[i];
515 		k[1] = e1->key[i];
516 
517 		/* Mask comparison. */
518 		if ((km[0] & km0) != (km[1] & km0))
519 			return 1; /* Not equal. */
520 
521 		/* Value comparison. */
522 		if ((k[0] & km[0] & km0) != (k[1] & km[1] & km0))
523 			return 1; /* Not equal. */
524 	}
525 
526 	return 0; /* Equal. */
527 }
528 
529 static struct rte_swx_table_entry *
530 table_entries_find(struct table *table, struct rte_swx_table_entry *entry)
531 {
532 	struct rte_swx_table_entry *e;
533 
534 	TAILQ_FOREACH(e, &table->entries, node)
535 		if (!table_entry_keycmp(table, entry, e))
536 			return e; /* Found. */
537 
538 	return NULL; /* Not found. */
539 }
540 
541 static void
542 table_entries_free(struct table *table)
543 {
544 	for ( ; ; ) {
545 		struct rte_swx_table_entry *entry;
546 
547 		entry = TAILQ_FIRST(&table->entries);
548 		if (!entry)
549 			break;
550 
551 		TAILQ_REMOVE(&table->entries, entry, node);
552 		table_entry_free(entry);
553 	}
554 }
555 
556 static struct rte_swx_table_entry *
557 table_pending_add_find(struct table *table, struct rte_swx_table_entry *entry)
558 {
559 	struct rte_swx_table_entry *e;
560 
561 	TAILQ_FOREACH(e, &table->pending_add, node)
562 		if (!table_entry_keycmp(table, entry, e))
563 			return e; /* Found. */
564 
565 	return NULL; /* Not found. */
566 }
567 
568 static void
569 table_pending_add_admit(struct table *table)
570 {
571 	TAILQ_CONCAT(&table->entries, &table->pending_add, node);
572 }
573 
574 static void
575 table_pending_add_free(struct table *table)
576 {
577 	for ( ; ; ) {
578 		struct rte_swx_table_entry *entry;
579 
580 		entry = TAILQ_FIRST(&table->pending_add);
581 		if (!entry)
582 			break;
583 
584 		TAILQ_REMOVE(&table->pending_add, entry, node);
585 		table_entry_free(entry);
586 	}
587 }
588 
589 static struct rte_swx_table_entry *
590 table_pending_modify0_find(struct table *table,
591 			   struct rte_swx_table_entry *entry)
592 {
593 	struct rte_swx_table_entry *e;
594 
595 	TAILQ_FOREACH(e, &table->pending_modify0, node)
596 		if (!table_entry_keycmp(table, entry, e))
597 			return e; /* Found. */
598 
599 	return NULL; /* Not found. */
600 }
601 
602 static void
603 table_pending_modify0_admit(struct table *table)
604 {
605 	TAILQ_CONCAT(&table->entries, &table->pending_modify0, node);
606 }
607 
608 static void
609 table_pending_modify0_free(struct table *table)
610 {
611 	for ( ; ; ) {
612 		struct rte_swx_table_entry *entry;
613 
614 		entry = TAILQ_FIRST(&table->pending_modify0);
615 		if (!entry)
616 			break;
617 
618 		TAILQ_REMOVE(&table->pending_modify0, entry, node);
619 		table_entry_free(entry);
620 	}
621 }
622 
623 static struct rte_swx_table_entry *
624 table_pending_modify1_find(struct table *table,
625 			   struct rte_swx_table_entry *entry)
626 {
627 	struct rte_swx_table_entry *e;
628 
629 	TAILQ_FOREACH(e, &table->pending_modify1, node)
630 		if (!table_entry_keycmp(table, entry, e))
631 			return e; /* Found. */
632 
633 	return NULL; /* Not found. */
634 }
635 
636 static void
637 table_pending_modify1_admit(struct table *table)
638 {
639 	TAILQ_CONCAT(&table->entries, &table->pending_modify1, node);
640 }
641 
642 static void
643 table_pending_modify1_free(struct table *table)
644 {
645 	for ( ; ; ) {
646 		struct rte_swx_table_entry *entry;
647 
648 		entry = TAILQ_FIRST(&table->pending_modify1);
649 		if (!entry)
650 			break;
651 
652 		TAILQ_REMOVE(&table->pending_modify1, entry, node);
653 		table_entry_free(entry);
654 	}
655 }
656 
657 static struct rte_swx_table_entry *
658 table_pending_delete_find(struct table *table,
659 			  struct rte_swx_table_entry *entry)
660 {
661 	struct rte_swx_table_entry *e;
662 
663 	TAILQ_FOREACH(e, &table->pending_delete, node)
664 		if (!table_entry_keycmp(table, entry, e))
665 			return e; /* Found. */
666 
667 	return NULL; /* Not found. */
668 }
669 
670 static void
671 table_pending_delete_admit(struct table *table)
672 {
673 	TAILQ_CONCAT(&table->entries, &table->pending_delete, node);
674 }
675 
676 static void
677 table_pending_delete_free(struct table *table)
678 {
679 	for ( ; ; ) {
680 		struct rte_swx_table_entry *entry;
681 
682 		entry = TAILQ_FIRST(&table->pending_delete);
683 		if (!entry)
684 			break;
685 
686 		TAILQ_REMOVE(&table->pending_delete, entry, node);
687 		table_entry_free(entry);
688 	}
689 }
690 
691 static void
692 table_pending_default_free(struct table *table)
693 {
694 	if (!table->pending_default)
695 		return;
696 
697 	free(table->pending_default->action_data);
698 	free(table->pending_default);
699 	table->pending_default = NULL;
700 }
701 
702 static int
703 table_is_update_pending(struct table *table, int consider_pending_default)
704 {
705 	struct rte_swx_table_entry *e;
706 	uint32_t n = 0;
707 
708 	/* Pending add. */
709 	TAILQ_FOREACH(e, &table->pending_add, node)
710 		n++;
711 
712 	/* Pending modify. */
713 	TAILQ_FOREACH(e, &table->pending_modify1, node)
714 		n++;
715 
716 	/* Pending delete. */
717 	TAILQ_FOREACH(e, &table->pending_delete, node)
718 		n++;
719 
720 	/* Pending default. */
721 	if (consider_pending_default && table->pending_default)
722 		n++;
723 
724 	return n;
725 }
726 
727 static void
728 table_free(struct rte_swx_ctl_pipeline *ctl)
729 {
730 	uint32_t i;
731 
732 	if (!ctl->tables)
733 		return;
734 
735 	for (i = 0; i < ctl->info.n_tables; i++) {
736 		struct table *table = &ctl->tables[i];
737 
738 		free(table->mf);
739 		free(table->actions);
740 		free(table->params.key_mask0);
741 
742 		table_entries_free(table);
743 		table_pending_add_free(table);
744 		table_pending_modify0_free(table);
745 		table_pending_modify1_free(table);
746 		table_pending_delete_free(table);
747 		table_pending_default_free(table);
748 	}
749 
750 	free(ctl->tables);
751 	ctl->tables = NULL;
752 }
753 
754 static void
755 selector_group_members_free(struct selector *s, uint32_t group_id)
756 {
757 	struct rte_swx_table_selector_group *group = s->groups[group_id];
758 
759 	if (!group)
760 		return;
761 
762 	for ( ; ; ) {
763 		struct rte_swx_table_selector_member *m;
764 
765 		m = TAILQ_FIRST(&group->members);
766 		if (!m)
767 			break;
768 
769 		TAILQ_REMOVE(&group->members, m, node);
770 		free(m);
771 	}
772 
773 	free(group);
774 	s->groups[group_id] = NULL;
775 }
776 
777 static void
778 selector_pending_group_members_free(struct selector *s, uint32_t group_id)
779 {
780 	struct rte_swx_table_selector_group *group = s->pending_groups[group_id];
781 
782 	if (!group)
783 		return;
784 
785 	for ( ; ; ) {
786 		struct rte_swx_table_selector_member *m;
787 
788 		m = TAILQ_FIRST(&group->members);
789 		if (!m)
790 			break;
791 
792 		TAILQ_REMOVE(&group->members, m, node);
793 		free(m);
794 	}
795 
796 	free(group);
797 	s->pending_groups[group_id] = NULL;
798 }
799 
800 static int
801 selector_group_duplicate_to_pending(struct selector *s, uint32_t group_id)
802 {
803 	struct rte_swx_table_selector_group *g, *gp;
804 	struct rte_swx_table_selector_member *m;
805 
806 	selector_pending_group_members_free(s, group_id);
807 
808 	g = s->groups[group_id];
809 	gp = s->pending_groups[group_id];
810 
811 	if (!gp) {
812 		gp = calloc(1, sizeof(struct rte_swx_table_selector_group));
813 		if (!gp)
814 			goto error;
815 
816 		TAILQ_INIT(&gp->members);
817 
818 		s->pending_groups[group_id] = gp;
819 	}
820 
821 	if (!g)
822 		return 0;
823 
824 	TAILQ_FOREACH(m, &g->members, node) {
825 		struct rte_swx_table_selector_member *mp;
826 
827 		mp = calloc(1, sizeof(struct rte_swx_table_selector_member));
828 		if (!mp)
829 			goto error;
830 
831 		memcpy(mp, m, sizeof(struct rte_swx_table_selector_member));
832 
833 		TAILQ_INSERT_TAIL(&gp->members, mp, node);
834 	}
835 
836 	return 0;
837 
838 error:
839 	selector_pending_group_members_free(s, group_id);
840 	return -ENOMEM;
841 }
842 
843 static void
844 selector_free(struct rte_swx_ctl_pipeline *ctl)
845 {
846 	uint32_t i;
847 
848 	if (!ctl->selectors)
849 		return;
850 
851 	for (i = 0; i < ctl->info.n_selectors; i++) {
852 		struct selector *s = &ctl->selectors[i];
853 		uint32_t i;
854 
855 		/* selector_fields. */
856 		free(s->selector_fields);
857 
858 		/* groups. */
859 		if (s->groups)
860 			for (i = 0; i < s->info.n_groups_max; i++)
861 				selector_group_members_free(s, i);
862 
863 		free(s->groups);
864 
865 		/* pending_groups. */
866 		if (s->pending_groups)
867 			for (i = 0; i < s->info.n_groups_max; i++)
868 				selector_pending_group_members_free(s, i);
869 
870 		free(s->pending_groups);
871 
872 		/* groups_added. */
873 		free(s->groups_added);
874 
875 		/* groups_pending_delete. */
876 		free(s->groups_pending_delete);
877 
878 		/* params. */
879 		free(s->params.selector_mask);
880 	}
881 
882 	free(ctl->selectors);
883 	ctl->selectors = NULL;
884 }
885 
886 static struct selector *
887 selector_find(struct rte_swx_ctl_pipeline *ctl, const char *selector_name)
888 {
889 	uint32_t i;
890 
891 	for (i = 0; i < ctl->info.n_selectors; i++) {
892 		struct selector *s = &ctl->selectors[i];
893 
894 		if (!strcmp(selector_name, s->info.name))
895 			return s;
896 	}
897 
898 	return NULL;
899 }
900 
901 static int
902 selector_params_get(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id)
903 {
904 	struct selector *s = &ctl->selectors[selector_id];
905 	struct rte_swx_ctl_table_match_field_info *first = NULL, *last = NULL;
906 	uint8_t *selector_mask = NULL;
907 	uint32_t selector_size = 0, selector_offset = 0, i;
908 
909 	/* Find first (smallest offset) and last (biggest offset) match fields. */
910 	first = &s->selector_fields[0];
911 	last = &s->selector_fields[0];
912 
913 	for (i = 1; i < s->info.n_selector_fields; i++) {
914 		struct rte_swx_ctl_table_match_field_info *f = &s->selector_fields[i];
915 
916 		if (f->offset < first->offset)
917 			first = f;
918 
919 		if (f->offset > last->offset)
920 			last = f;
921 	}
922 
923 	/* selector_offset. */
924 	selector_offset = first->offset / 8;
925 
926 	/* selector_size. */
927 	selector_size = (last->offset + last->n_bits - first->offset) / 8;
928 
929 	/* selector_mask. */
930 	selector_mask = calloc(1, selector_size);
931 	if (!selector_mask)
932 		return -ENOMEM;
933 
934 	for (i = 0; i < s->info.n_selector_fields; i++) {
935 		struct rte_swx_ctl_table_match_field_info *f = &s->selector_fields[i];
936 		uint32_t start;
937 		size_t size;
938 
939 		start = (f->offset - first->offset) / 8;
940 		size = f->n_bits / 8;
941 
942 		memset(&selector_mask[start], 0xFF, size);
943 	}
944 
945 	/* Fill in. */
946 	s->params.group_id_offset = s->group_id_field.offset / 8;
947 	s->params.selector_size = selector_size;
948 	s->params.selector_offset = selector_offset;
949 	s->params.selector_mask = selector_mask;
950 	s->params.member_id_offset = s->member_id_field.offset / 8;
951 	s->params.n_groups_max = s->info.n_groups_max;
952 	s->params.n_members_per_group_max = s->info.n_members_per_group_max;
953 
954 	return 0;
955 }
956 
957 static void
958 learner_pending_default_free(struct learner *l)
959 {
960 	if (!l->pending_default)
961 		return;
962 
963 	free(l->pending_default->action_data);
964 	free(l->pending_default);
965 	l->pending_default = NULL;
966 }
967 
968 
969 static void
970 learner_free(struct rte_swx_ctl_pipeline *ctl)
971 {
972 	uint32_t i;
973 
974 	if (!ctl->learners)
975 		return;
976 
977 	for (i = 0; i < ctl->info.n_learners; i++) {
978 		struct learner *l = &ctl->learners[i];
979 
980 		free(l->mf);
981 		free(l->actions);
982 
983 		learner_pending_default_free(l);
984 	}
985 
986 	free(ctl->learners);
987 	ctl->learners = NULL;
988 }
989 
990 static struct learner *
991 learner_find(struct rte_swx_ctl_pipeline *ctl, const char *learner_name)
992 {
993 	uint32_t i;
994 
995 	for (i = 0; i < ctl->info.n_learners; i++) {
996 		struct learner *l = &ctl->learners[i];
997 
998 		if (!strcmp(learner_name, l->info.name))
999 			return l;
1000 	}
1001 
1002 	return NULL;
1003 }
1004 
1005 static uint32_t
1006 learner_action_data_size_get(struct rte_swx_ctl_pipeline *ctl, struct learner *l)
1007 {
1008 	uint32_t action_data_size = 0, i;
1009 
1010 	for (i = 0; i < l->info.n_actions; i++) {
1011 		uint32_t action_id = l->actions[i].action_id;
1012 		struct action *a = &ctl->actions[action_id];
1013 
1014 		if (a->data_size > action_data_size)
1015 			action_data_size = a->data_size;
1016 	}
1017 
1018 	return action_data_size;
1019 }
1020 
1021 static void
1022 table_state_free(struct rte_swx_ctl_pipeline *ctl)
1023 {
1024 	uint32_t table_base_index, selector_base_index, learner_base_index, i;
1025 
1026 	if (!ctl->ts_next)
1027 		return;
1028 
1029 	/* For each table, free its table state. */
1030 	table_base_index = 0;
1031 	for (i = 0; i < ctl->info.n_tables; i++) {
1032 		struct table *table = &ctl->tables[i];
1033 		struct rte_swx_table_state *ts = &ctl->ts_next[table_base_index + i];
1034 
1035 		/* Default action data. */
1036 		free(ts->default_action_data);
1037 
1038 		/* Table object. */
1039 		if (!table->is_stub && table->ops.free && ts->obj)
1040 			table->ops.free(ts->obj);
1041 	}
1042 
1043 	/* For each selector table, free its table state. */
1044 	selector_base_index = ctl->info.n_tables;
1045 	for (i = 0; i < ctl->info.n_selectors; i++) {
1046 		struct rte_swx_table_state *ts = &ctl->ts_next[selector_base_index + i];
1047 
1048 		/* Table object. */
1049 		if (ts->obj)
1050 			rte_swx_table_selector_free(ts->obj);
1051 	}
1052 
1053 	/* For each learner table, free its table state. */
1054 	learner_base_index = ctl->info.n_tables + ctl->info.n_selectors;
1055 	for (i = 0; i < ctl->info.n_learners; i++) {
1056 		struct rte_swx_table_state *ts = &ctl->ts_next[learner_base_index + i];
1057 
1058 		/* Default action data. */
1059 		free(ts->default_action_data);
1060 	}
1061 
1062 	free(ctl->ts_next);
1063 	ctl->ts_next = NULL;
1064 }
1065 
1066 static int
1067 table_state_create(struct rte_swx_ctl_pipeline *ctl)
1068 {
1069 	uint32_t table_base_index, selector_base_index, learner_base_index, i;
1070 	int status = 0;
1071 
1072 	ctl->ts_next = calloc(ctl->info.n_tables + ctl->info.n_selectors + ctl->info.n_learners,
1073 			      sizeof(struct rte_swx_table_state));
1074 	if (!ctl->ts_next) {
1075 		status = -ENOMEM;
1076 		goto error;
1077 	}
1078 
1079 	/* Tables. */
1080 	table_base_index = 0;
1081 	for (i = 0; i < ctl->info.n_tables; i++) {
1082 		struct table *table = &ctl->tables[i];
1083 		struct rte_swx_table_state *ts = &ctl->ts[table_base_index + i];
1084 		struct rte_swx_table_state *ts_next = &ctl->ts_next[table_base_index + i];
1085 
1086 		/* Table object. */
1087 		if (!table->is_stub && table->ops.add) {
1088 			ts_next->obj = table->ops.create(&table->params,
1089 							 &table->entries,
1090 							 table->info.args,
1091 							 ctl->numa_node);
1092 			if (!ts_next->obj) {
1093 				status = -ENODEV;
1094 				goto error;
1095 			}
1096 		}
1097 
1098 		if (!table->is_stub && !table->ops.add)
1099 			ts_next->obj = ts->obj;
1100 
1101 		/* Default action data: duplicate from current table state. */
1102 		ts_next->default_action_data =
1103 			malloc(table->params.action_data_size);
1104 		if (!ts_next->default_action_data) {
1105 			status = -ENOMEM;
1106 			goto error;
1107 		}
1108 
1109 		memcpy(ts_next->default_action_data,
1110 		       ts->default_action_data,
1111 		       table->params.action_data_size);
1112 
1113 		ts_next->default_action_id = ts->default_action_id;
1114 	}
1115 
1116 	/* Selector tables. */
1117 	selector_base_index = ctl->info.n_tables;
1118 	for (i = 0; i < ctl->info.n_selectors; i++) {
1119 		struct selector *s = &ctl->selectors[i];
1120 		struct rte_swx_table_state *ts_next = &ctl->ts_next[selector_base_index + i];
1121 
1122 		/* Table object. */
1123 		ts_next->obj = rte_swx_table_selector_create(&s->params, NULL, ctl->numa_node);
1124 		if (!ts_next->obj) {
1125 			status = -ENODEV;
1126 			goto error;
1127 		}
1128 	}
1129 
1130 	/* Learner tables. */
1131 	learner_base_index = ctl->info.n_tables + ctl->info.n_selectors;
1132 	for (i = 0; i < ctl->info.n_learners; i++) {
1133 		struct learner *l = &ctl->learners[i];
1134 		struct rte_swx_table_state *ts = &ctl->ts[learner_base_index + i];
1135 		struct rte_swx_table_state *ts_next = &ctl->ts_next[learner_base_index + i];
1136 
1137 		/* Table object: duplicate from the current table state. */
1138 		ts_next->obj = ts->obj;
1139 
1140 		/* Default action data: duplicate from the current table state. */
1141 		ts_next->default_action_data = malloc(l->action_data_size);
1142 		if (!ts_next->default_action_data) {
1143 			status = -ENOMEM;
1144 			goto error;
1145 		}
1146 
1147 		memcpy(ts_next->default_action_data,
1148 		       ts->default_action_data,
1149 		       l->action_data_size);
1150 
1151 		ts_next->default_action_id = ts->default_action_id;
1152 	}
1153 
1154 	return 0;
1155 
1156 error:
1157 	table_state_free(ctl);
1158 	return status;
1159 }
1160 
1161 void
1162 rte_swx_ctl_pipeline_free(struct rte_swx_ctl_pipeline *ctl)
1163 {
1164 	if (!ctl)
1165 		return;
1166 
1167 	action_free(ctl);
1168 
1169 	table_state_free(ctl);
1170 
1171 	learner_free(ctl);
1172 
1173 	selector_free(ctl);
1174 
1175 	table_free(ctl);
1176 
1177 	free(ctl);
1178 }
1179 
1180 struct rte_swx_ctl_pipeline *
1181 rte_swx_ctl_pipeline_create(struct rte_swx_pipeline *p)
1182 {
1183 	struct rte_swx_ctl_pipeline *ctl = NULL;
1184 	uint32_t i;
1185 	int status;
1186 
1187 	if (!p)
1188 		goto error;
1189 
1190 	ctl = calloc(1, sizeof(struct rte_swx_ctl_pipeline));
1191 	if (!ctl)
1192 		goto error;
1193 
1194 	/* info. */
1195 	status = rte_swx_ctl_pipeline_info_get(p, &ctl->info);
1196 	if (status)
1197 		goto error;
1198 
1199 	/* numa_node. */
1200 	status = rte_swx_ctl_pipeline_numa_node_get(p, &ctl->numa_node);
1201 	if (status)
1202 		goto error;
1203 
1204 	/* p. */
1205 	ctl->p = p;
1206 
1207 	/* actions. */
1208 	ctl->actions = calloc(ctl->info.n_actions, sizeof(struct action));
1209 	if (!ctl->actions)
1210 		goto error;
1211 
1212 	for (i = 0; i < ctl->info.n_actions; i++) {
1213 		struct action *a = &ctl->actions[i];
1214 		uint32_t j;
1215 
1216 		/* info. */
1217 		status = rte_swx_ctl_action_info_get(p, i, &a->info);
1218 		if (status)
1219 			goto error;
1220 
1221 		/* args. */
1222 		a->args = calloc(a->info.n_args,
1223 				 sizeof(struct rte_swx_ctl_action_arg_info));
1224 		if (!a->args)
1225 			goto error;
1226 
1227 		for (j = 0; j < a->info.n_args; j++) {
1228 			status = rte_swx_ctl_action_arg_info_get(p,
1229 								 i,
1230 								 j,
1231 								 &a->args[j]);
1232 			if (status)
1233 				goto error;
1234 		}
1235 
1236 		/* data_size. */
1237 		for (j = 0; j < a->info.n_args; j++) {
1238 			struct rte_swx_ctl_action_arg_info *info = &a->args[j];
1239 
1240 			a->data_size += info->n_bits;
1241 		}
1242 
1243 		a->data_size = (a->data_size + 7) / 8;
1244 	}
1245 
1246 	/* tables. */
1247 	ctl->tables = calloc(ctl->info.n_tables, sizeof(struct table));
1248 	if (!ctl->tables)
1249 		goto error;
1250 
1251 	for (i = 0; i < ctl->info.n_tables; i++) {
1252 		struct table *t = &ctl->tables[i];
1253 
1254 		TAILQ_INIT(&t->entries);
1255 		TAILQ_INIT(&t->pending_add);
1256 		TAILQ_INIT(&t->pending_modify0);
1257 		TAILQ_INIT(&t->pending_modify1);
1258 		TAILQ_INIT(&t->pending_delete);
1259 	}
1260 
1261 	for (i = 0; i < ctl->info.n_tables; i++) {
1262 		struct table *t = &ctl->tables[i];
1263 		uint32_t j;
1264 
1265 		/* info. */
1266 		status = rte_swx_ctl_table_info_get(p, i, &t->info);
1267 		if (status)
1268 			goto error;
1269 
1270 		/* mf. */
1271 		t->mf = calloc(t->info.n_match_fields,
1272 			sizeof(struct rte_swx_ctl_table_match_field_info));
1273 		if (!t->mf)
1274 			goto error;
1275 
1276 		for (j = 0; j < t->info.n_match_fields; j++) {
1277 			status = rte_swx_ctl_table_match_field_info_get(p,
1278 				i,
1279 				j,
1280 				&t->mf[j]);
1281 			if (status)
1282 				goto error;
1283 		}
1284 
1285 		/* actions. */
1286 		t->actions = calloc(t->info.n_actions,
1287 			sizeof(struct rte_swx_ctl_table_action_info));
1288 		if (!t->actions)
1289 			goto error;
1290 
1291 		for (j = 0; j < t->info.n_actions; j++) {
1292 			status = rte_swx_ctl_table_action_info_get(p,
1293 				i,
1294 				j,
1295 				&t->actions[j]);
1296 			if (status ||
1297 			    t->actions[j].action_id >= ctl->info.n_actions)
1298 				goto error;
1299 		}
1300 
1301 		/* ops, is_stub. */
1302 		status = rte_swx_ctl_table_ops_get(p, i, &t->ops, &t->is_stub);
1303 		if (status)
1304 			goto error;
1305 
1306 		if ((t->is_stub && t->info.n_match_fields) ||
1307 		    (!t->is_stub && !t->info.n_match_fields))
1308 			goto error;
1309 
1310 		/* params. */
1311 		status = table_params_get(ctl, i);
1312 		if (status)
1313 			goto error;
1314 	}
1315 
1316 	/* selector tables. */
1317 	ctl->selectors = calloc(ctl->info.n_selectors, sizeof(struct selector));
1318 	if (!ctl->selectors)
1319 		goto error;
1320 
1321 	for (i = 0; i < ctl->info.n_selectors; i++) {
1322 		struct selector *s = &ctl->selectors[i];
1323 		uint32_t j;
1324 
1325 		/* info. */
1326 		status = rte_swx_ctl_selector_info_get(p, i, &s->info);
1327 		if (status)
1328 			goto error;
1329 
1330 		/* group_id field. */
1331 		status = rte_swx_ctl_selector_group_id_field_info_get(p,
1332 			i,
1333 			&s->group_id_field);
1334 		if (status)
1335 			goto error;
1336 
1337 		/* selector fields. */
1338 		s->selector_fields = calloc(s->info.n_selector_fields,
1339 			sizeof(struct rte_swx_ctl_table_match_field_info));
1340 		if (!s->selector_fields)
1341 			goto error;
1342 
1343 		for (j = 0; j < s->info.n_selector_fields; j++) {
1344 			status = rte_swx_ctl_selector_field_info_get(p,
1345 				i,
1346 				j,
1347 				&s->selector_fields[j]);
1348 			if (status)
1349 				goto error;
1350 		}
1351 
1352 		/* member_id field. */
1353 		status = rte_swx_ctl_selector_member_id_field_info_get(p,
1354 			i,
1355 			&s->member_id_field);
1356 		if (status)
1357 			goto error;
1358 
1359 		/* groups. */
1360 		s->groups = calloc(s->info.n_groups_max,
1361 			sizeof(struct rte_swx_table_selector_group *));
1362 		if (!s->groups)
1363 			goto error;
1364 
1365 		/* pending_groups. */
1366 		s->pending_groups = calloc(s->info.n_groups_max,
1367 			sizeof(struct rte_swx_table_selector_group *));
1368 		if (!s->pending_groups)
1369 			goto error;
1370 
1371 		/* groups_added. */
1372 		s->groups_added = calloc(s->info.n_groups_max, sizeof(int));
1373 		if (!s->groups_added)
1374 			goto error;
1375 
1376 		/* groups_pending_delete. */
1377 		s->groups_pending_delete = calloc(s->info.n_groups_max, sizeof(int));
1378 		if (!s->groups_pending_delete)
1379 			goto error;
1380 
1381 		/* params. */
1382 		status = selector_params_get(ctl, i);
1383 		if (status)
1384 			goto error;
1385 	}
1386 
1387 	/* learner tables. */
1388 	ctl->learners = calloc(ctl->info.n_learners, sizeof(struct learner));
1389 	if (!ctl->learners)
1390 		goto error;
1391 
1392 	for (i = 0; i < ctl->info.n_learners; i++) {
1393 		struct learner *l = &ctl->learners[i];
1394 		uint32_t j;
1395 
1396 		/* info. */
1397 		status = rte_swx_ctl_learner_info_get(p, i, &l->info);
1398 		if (status)
1399 			goto error;
1400 
1401 		/* mf. */
1402 		l->mf = calloc(l->info.n_match_fields,
1403 			       sizeof(struct rte_swx_ctl_table_match_field_info));
1404 		if (!l->mf)
1405 			goto error;
1406 
1407 		for (j = 0; j < l->info.n_match_fields; j++) {
1408 			status = rte_swx_ctl_learner_match_field_info_get(p,
1409 				i,
1410 				j,
1411 				&l->mf[j]);
1412 			if (status)
1413 				goto error;
1414 		}
1415 
1416 		/* actions. */
1417 		l->actions = calloc(l->info.n_actions,
1418 			sizeof(struct rte_swx_ctl_table_action_info));
1419 		if (!l->actions)
1420 			goto error;
1421 
1422 		for (j = 0; j < l->info.n_actions; j++) {
1423 			status = rte_swx_ctl_learner_action_info_get(p,
1424 				i,
1425 				j,
1426 				&l->actions[j]);
1427 			if (status || l->actions[j].action_id >= ctl->info.n_actions)
1428 				goto error;
1429 		}
1430 
1431 		/* action_data_size. */
1432 		l->action_data_size = learner_action_data_size_get(ctl, l);
1433 	}
1434 
1435 	/* ts. */
1436 	status = rte_swx_pipeline_table_state_get(p, &ctl->ts);
1437 	if (status)
1438 		goto error;
1439 
1440 	/* ts_next. */
1441 	status = table_state_create(ctl);
1442 	if (status)
1443 		goto error;
1444 
1445 	return ctl;
1446 
1447 error:
1448 	rte_swx_ctl_pipeline_free(ctl);
1449 	return NULL;
1450 }
1451 
1452 int
1453 rte_swx_ctl_pipeline_table_entry_add(struct rte_swx_ctl_pipeline *ctl,
1454 				     const char *table_name,
1455 				     struct rte_swx_table_entry *entry)
1456 {
1457 	struct table *table;
1458 	struct rte_swx_table_entry *new_entry, *existing_entry;
1459 	uint32_t table_id;
1460 
1461 	CHECK(ctl, EINVAL);
1462 	CHECK(table_name && table_name[0], EINVAL);
1463 
1464 	table = table_find(ctl, table_name);
1465 	CHECK(table, EINVAL);
1466 	table_id = table - ctl->tables;
1467 
1468 	CHECK(entry, EINVAL);
1469 	CHECK(!table_entry_check(ctl, table_id, entry, 1, 1), EINVAL);
1470 
1471 	new_entry = table_entry_duplicate(ctl, table_id, entry, 1, 1);
1472 	CHECK(new_entry, ENOMEM);
1473 
1474 	/* The new entry is found in the table->entries list:
1475 	 * - Add the new entry to the table->pending_modify1 list;
1476 	 * - Move the existing entry from the table->entries list to the
1477 	 *   table->pending_modify0 list.
1478 	 */
1479 	existing_entry = table_entries_find(table, entry);
1480 	if (existing_entry) {
1481 		TAILQ_INSERT_TAIL(&table->pending_modify1,
1482 				  new_entry,
1483 				  node);
1484 
1485 		TAILQ_REMOVE(&table->entries,
1486 			     existing_entry,
1487 			     node);
1488 
1489 		TAILQ_INSERT_TAIL(&table->pending_modify0,
1490 				  existing_entry,
1491 				  node);
1492 
1493 		return 0;
1494 	}
1495 
1496 	/* The new entry is found in the table->pending_add list:
1497 	 * - Replace the entry in the table->pending_add list with the new entry
1498 	 *   (and free the replaced entry).
1499 	 */
1500 	existing_entry = table_pending_add_find(table, entry);
1501 	if (existing_entry) {
1502 		TAILQ_INSERT_AFTER(&table->pending_add,
1503 				   existing_entry,
1504 				   new_entry,
1505 				   node);
1506 
1507 		TAILQ_REMOVE(&table->pending_add,
1508 			     existing_entry,
1509 			     node);
1510 
1511 		table_entry_free(existing_entry);
1512 
1513 		return 0;
1514 	}
1515 
1516 	/* The new entry is found in the table->pending_modify1 list:
1517 	 * - Replace the entry in the table->pending_modify1 list with the new
1518 	 *   entry (and free the replaced entry).
1519 	 */
1520 	existing_entry = table_pending_modify1_find(table, entry);
1521 	if (existing_entry) {
1522 		TAILQ_INSERT_AFTER(&table->pending_modify1,
1523 				   existing_entry,
1524 				   new_entry,
1525 				   node);
1526 
1527 		TAILQ_REMOVE(&table->pending_modify1,
1528 			     existing_entry,
1529 			     node);
1530 
1531 		table_entry_free(existing_entry);
1532 
1533 		return 0;
1534 	}
1535 
1536 	/* The new entry is found in the table->pending_delete list:
1537 	 * - Add the new entry to the table->pending_modify1 list;
1538 	 * - Move the existing entry from the table->pending_delete list to the
1539 	 *   table->pending_modify0 list.
1540 	 */
1541 	existing_entry = table_pending_delete_find(table, entry);
1542 	if (existing_entry) {
1543 		TAILQ_INSERT_TAIL(&table->pending_modify1,
1544 				  new_entry,
1545 				  node);
1546 
1547 		TAILQ_REMOVE(&table->pending_delete,
1548 			     existing_entry,
1549 			     node);
1550 
1551 		TAILQ_INSERT_TAIL(&table->pending_modify0,
1552 				  existing_entry,
1553 				  node);
1554 
1555 		return 0;
1556 	}
1557 
1558 	/* The new entry is not found in any of the above lists:
1559 	 * - Add the new entry to the table->pending_add list.
1560 	 */
1561 	TAILQ_INSERT_TAIL(&table->pending_add, new_entry, node);
1562 
1563 	return 0;
1564 }
1565 
1566 int
1567 rte_swx_ctl_pipeline_table_entry_delete(struct rte_swx_ctl_pipeline *ctl,
1568 					const char *table_name,
1569 					struct rte_swx_table_entry *entry)
1570 {
1571 	struct table *table;
1572 	struct rte_swx_table_entry *existing_entry;
1573 	uint32_t table_id;
1574 
1575 	CHECK(ctl, EINVAL);
1576 
1577 	CHECK(table_name && table_name[0], EINVAL);
1578 	table = table_find(ctl, table_name);
1579 	CHECK(table, EINVAL);
1580 	table_id = table - ctl->tables;
1581 
1582 	CHECK(entry, EINVAL);
1583 	CHECK(!table_entry_check(ctl, table_id, entry, 1, 0), EINVAL);
1584 
1585 	/* The entry is found in the table->entries list:
1586 	 * - Move the existing entry from the table->entries list to to the
1587 	 *   table->pending_delete list.
1588 	 */
1589 	existing_entry = table_entries_find(table, entry);
1590 	if (existing_entry) {
1591 		TAILQ_REMOVE(&table->entries,
1592 			     existing_entry,
1593 			     node);
1594 
1595 		TAILQ_INSERT_TAIL(&table->pending_delete,
1596 				  existing_entry,
1597 				  node);
1598 
1599 		return 0;
1600 	}
1601 
1602 	/* The entry is found in the table->pending_add list:
1603 	 * - Remove the entry from the table->pending_add list and free it.
1604 	 */
1605 	existing_entry = table_pending_add_find(table, entry);
1606 	if (existing_entry) {
1607 		TAILQ_REMOVE(&table->pending_add,
1608 			     existing_entry,
1609 			     node);
1610 
1611 		table_entry_free(existing_entry);
1612 	}
1613 
1614 	/* The entry is found in the table->pending_modify1 list:
1615 	 * - Free the entry in the table->pending_modify1 list;
1616 	 * - Move the existing entry from the table->pending_modify0 list to the
1617 	 *   table->pending_delete list.
1618 	 */
1619 	existing_entry = table_pending_modify1_find(table, entry);
1620 	if (existing_entry) {
1621 		struct rte_swx_table_entry *real_existing_entry;
1622 
1623 		TAILQ_REMOVE(&table->pending_modify1,
1624 			     existing_entry,
1625 			     node);
1626 
1627 		table_entry_free(existing_entry);
1628 
1629 		real_existing_entry = table_pending_modify0_find(table, entry);
1630 		CHECK(real_existing_entry, EINVAL); /* Coverity. */
1631 
1632 		TAILQ_REMOVE(&table->pending_modify0,
1633 			     real_existing_entry,
1634 			     node);
1635 
1636 		TAILQ_INSERT_TAIL(&table->pending_delete,
1637 				  real_existing_entry,
1638 				  node);
1639 
1640 		return 0;
1641 	}
1642 
1643 	/* The entry is found in the table->pending_delete list:
1644 	 * - Do nothing: the existing entry is already in the
1645 	 *   table->pending_delete list, i.e. already marked for delete, so
1646 	 *   simply keep it there as it is.
1647 	 */
1648 
1649 	/* The entry is not found in any of the above lists:
1650 	 * - Do nothing: no existing entry to delete.
1651 	 */
1652 
1653 	return 0;
1654 }
1655 
1656 int
1657 rte_swx_ctl_pipeline_table_default_entry_add(struct rte_swx_ctl_pipeline *ctl,
1658 					     const char *table_name,
1659 					     struct rte_swx_table_entry *entry)
1660 {
1661 	struct table *table;
1662 	struct rte_swx_table_entry *new_entry;
1663 	uint32_t table_id;
1664 
1665 	CHECK(ctl, EINVAL);
1666 
1667 	CHECK(table_name && table_name[0], EINVAL);
1668 	table = table_find(ctl, table_name);
1669 	CHECK(table, EINVAL);
1670 	table_id = table - ctl->tables;
1671 	CHECK(!table->info.default_action_is_const, EINVAL);
1672 
1673 	CHECK(entry, EINVAL);
1674 	CHECK(!table_entry_check(ctl, table_id, entry, 0, 1), EINVAL);
1675 
1676 	new_entry = table_entry_duplicate(ctl, table_id, entry, 0, 1);
1677 	CHECK(new_entry, ENOMEM);
1678 
1679 	table_pending_default_free(table);
1680 
1681 	table->pending_default = new_entry;
1682 	return 0;
1683 }
1684 
1685 
1686 static void
1687 table_entry_list_free(struct rte_swx_table_entry_list *list)
1688 {
1689 	for ( ; ; ) {
1690 		struct rte_swx_table_entry *entry;
1691 
1692 		entry = TAILQ_FIRST(list);
1693 		if (!entry)
1694 			break;
1695 
1696 		TAILQ_REMOVE(list, entry, node);
1697 		table_entry_free(entry);
1698 	}
1699 }
1700 
1701 static int
1702 table_entry_list_duplicate(struct rte_swx_ctl_pipeline *ctl,
1703 			   uint32_t table_id,
1704 			   struct rte_swx_table_entry_list *dst,
1705 			   struct rte_swx_table_entry_list *src)
1706 {
1707 	struct rte_swx_table_entry *src_entry;
1708 
1709 	TAILQ_FOREACH(src_entry, src, node) {
1710 		struct rte_swx_table_entry *dst_entry;
1711 
1712 		dst_entry = table_entry_duplicate(ctl, table_id, src_entry, 1, 1);
1713 		if (!dst_entry)
1714 			goto error;
1715 
1716 		TAILQ_INSERT_TAIL(dst, dst_entry, node);
1717 	}
1718 
1719 	return 0;
1720 
1721 error:
1722 	table_entry_list_free(dst);
1723 	return -ENOMEM;
1724 }
1725 
1726 /* This commit stage contains all the operations that can fail; in case ANY of
1727  * them fails for ANY table, ALL of them are rolled back for ALL the tables.
1728  */
1729 static int
1730 table_rollfwd0(struct rte_swx_ctl_pipeline *ctl,
1731 	       uint32_t table_id,
1732 	       uint32_t after_swap)
1733 {
1734 	struct table *table = &ctl->tables[table_id];
1735 	struct rte_swx_table_state *ts = &ctl->ts[table_id];
1736 	struct rte_swx_table_state *ts_next = &ctl->ts_next[table_id];
1737 
1738 	if (table->is_stub || !table_is_update_pending(table, 0))
1739 		return 0;
1740 
1741 	/*
1742 	 * Current table supports incremental update.
1743 	 */
1744 	if (table->ops.add) {
1745 		/* Reset counters. */
1746 		table->n_add = 0;
1747 		table->n_modify = 0;
1748 		table->n_delete = 0;
1749 
1750 		/* Add pending rules. */
1751 		struct rte_swx_table_entry *entry;
1752 
1753 		TAILQ_FOREACH(entry, &table->pending_add, node) {
1754 			int status;
1755 
1756 			status = table->ops.add(ts_next->obj, entry);
1757 			if (status)
1758 				return status;
1759 
1760 			table->n_add++;
1761 		}
1762 
1763 		/* Modify pending rules. */
1764 		TAILQ_FOREACH(entry, &table->pending_modify1, node) {
1765 			int status;
1766 
1767 			status = table->ops.add(ts_next->obj, entry);
1768 			if (status)
1769 				return status;
1770 
1771 			table->n_modify++;
1772 		}
1773 
1774 		/* Delete pending rules. */
1775 		TAILQ_FOREACH(entry, &table->pending_delete, node) {
1776 			int status;
1777 
1778 			status = table->ops.del(ts_next->obj, entry);
1779 			if (status)
1780 				return status;
1781 
1782 			table->n_delete++;
1783 		}
1784 
1785 		return 0;
1786 	}
1787 
1788 	/*
1789 	 * Current table does NOT support incremental update.
1790 	 */
1791 	if (!after_swap) {
1792 		struct rte_swx_table_entry_list list;
1793 		int status;
1794 
1795 		/* Create updated list of entries included. */
1796 		TAILQ_INIT(&list);
1797 
1798 		status = table_entry_list_duplicate(ctl,
1799 						    table_id,
1800 						    &list,
1801 						    &table->entries);
1802 		if (status)
1803 			goto error;
1804 
1805 		status = table_entry_list_duplicate(ctl,
1806 						    table_id,
1807 						    &list,
1808 						    &table->pending_add);
1809 		if (status)
1810 			goto error;
1811 
1812 		status = table_entry_list_duplicate(ctl,
1813 						    table_id,
1814 						    &list,
1815 						    &table->pending_modify1);
1816 		if (status)
1817 			goto error;
1818 
1819 		/* Create new table object with the updates included. */
1820 		ts_next->obj = table->ops.create(&table->params,
1821 						 &list,
1822 						 table->info.args,
1823 						 ctl->numa_node);
1824 		if (!ts_next->obj) {
1825 			status = -ENODEV;
1826 			goto error;
1827 		}
1828 
1829 		table_entry_list_free(&list);
1830 
1831 		return 0;
1832 
1833 error:
1834 		table_entry_list_free(&list);
1835 		return status;
1836 	}
1837 
1838 	/* Free the old table object. */
1839 	if (ts_next->obj && table->ops.free)
1840 		table->ops.free(ts_next->obj);
1841 
1842 	/* Copy over the new table object. */
1843 	ts_next->obj = ts->obj;
1844 
1845 	return 0;
1846 }
1847 
1848 /* This commit stage contains all the operations that cannot fail. They are
1849  * executed only if the previous stage was successful for ALL the tables. Hence,
1850  * none of these operations has to be rolled back for ANY table.
1851  */
1852 static void
1853 table_rollfwd1(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)
1854 {
1855 	struct table *table = &ctl->tables[table_id];
1856 	struct rte_swx_table_state *ts_next = &ctl->ts_next[table_id];
1857 	struct action *a;
1858 	uint8_t *action_data;
1859 	uint64_t action_id;
1860 
1861 	/* Copy the pending default entry. */
1862 	if (!table->pending_default)
1863 		return;
1864 
1865 	action_id = table->pending_default->action_id;
1866 	action_data = table->pending_default->action_data;
1867 	a = &ctl->actions[action_id];
1868 
1869 	if (a->data_size)
1870 		memcpy(ts_next->default_action_data, action_data, a->data_size);
1871 
1872 	ts_next->default_action_id = action_id;
1873 }
1874 
1875 /* This last commit stage is simply finalizing a successful commit operation.
1876  * This stage is only executed if all the previous stages were successful. This
1877  * stage cannot fail.
1878  */
1879 static void
1880 table_rollfwd2(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)
1881 {
1882 	struct table *table = &ctl->tables[table_id];
1883 
1884 	/* Move all the pending add entries to the table, as they are now part
1885 	 * of the table.
1886 	 */
1887 	table_pending_add_admit(table);
1888 
1889 	/* Move all the pending modify1 entries to table, are they are now part
1890 	 * of the table. Free up all the pending modify0 entries, as they are no
1891 	 * longer part of the table.
1892 	 */
1893 	table_pending_modify1_admit(table);
1894 	table_pending_modify0_free(table);
1895 
1896 	/* Free up all the pending delete entries, as they are no longer part of
1897 	 * the table.
1898 	 */
1899 	table_pending_delete_free(table);
1900 
1901 	/* Free up the pending default entry, as it is now part of the table. */
1902 	table_pending_default_free(table);
1903 }
1904 
1905 /* The rollback stage is only executed when the commit failed, i.e. ANY of the
1906  * commit operations that can fail did fail for ANY table. It reverts ALL the
1907  * tables to their state before the commit started, as if the commit never
1908  * happened.
1909  */
1910 static void
1911 table_rollback(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)
1912 {
1913 	struct table *table = &ctl->tables[table_id];
1914 	struct rte_swx_table_state *ts_next = &ctl->ts_next[table_id];
1915 
1916 	if (table->is_stub || !table_is_update_pending(table, 0))
1917 		return;
1918 
1919 	if (table->ops.add) {
1920 		struct rte_swx_table_entry *entry;
1921 
1922 		/* Add back all the entries that were just deleted. */
1923 		TAILQ_FOREACH(entry, &table->pending_delete, node) {
1924 			if (!table->n_delete)
1925 				break;
1926 
1927 			table->ops.add(ts_next->obj, entry);
1928 			table->n_delete--;
1929 		}
1930 
1931 		/* Add back the old copy for all the entries that were just
1932 		 * modified.
1933 		 */
1934 		TAILQ_FOREACH(entry, &table->pending_modify0, node) {
1935 			if (!table->n_modify)
1936 				break;
1937 
1938 			table->ops.add(ts_next->obj, entry);
1939 			table->n_modify--;
1940 		}
1941 
1942 		/* Delete all the entries that were just added. */
1943 		TAILQ_FOREACH(entry, &table->pending_add, node) {
1944 			if (!table->n_add)
1945 				break;
1946 
1947 			table->ops.del(ts_next->obj, entry);
1948 			table->n_add--;
1949 		}
1950 	} else {
1951 		struct rte_swx_table_state *ts = &ctl->ts[table_id];
1952 
1953 		/* Free the new table object, as update was cancelled. */
1954 		if (ts_next->obj && table->ops.free)
1955 			table->ops.free(ts_next->obj);
1956 
1957 		/* Reinstate the old table object. */
1958 		ts_next->obj = ts->obj;
1959 	}
1960 }
1961 
1962 /* This stage is conditionally executed (as instructed by the user) after a
1963  * failed commit operation to remove ALL the pending work for ALL the tables.
1964  */
1965 static void
1966 table_abort(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)
1967 {
1968 	struct table *table = &ctl->tables[table_id];
1969 
1970 	/* Free up all the pending add entries, as none of them is part of the
1971 	 * table.
1972 	 */
1973 	table_pending_add_free(table);
1974 
1975 	/* Free up all the pending modify1 entries, as none of them made it to
1976 	 * the table. Add back all the pending modify0 entries, as none of them
1977 	 * was deleted from the table.
1978 	 */
1979 	table_pending_modify1_free(table);
1980 	table_pending_modify0_admit(table);
1981 
1982 	/* Add back all the pending delete entries, as none of them was deleted
1983 	 * from the table.
1984 	 */
1985 	table_pending_delete_admit(table);
1986 
1987 	/* Free up the pending default entry, as it is no longer going to be
1988 	 * added to the table.
1989 	 */
1990 	table_pending_default_free(table);
1991 }
1992 
1993 int
1994 rte_swx_ctl_pipeline_selector_group_add(struct rte_swx_ctl_pipeline *ctl,
1995 					const char *selector_name,
1996 					uint32_t *group_id)
1997 {
1998 	struct selector *s;
1999 	uint32_t i;
2000 
2001 	/* Check input arguments. */
2002 	if (!ctl || !selector_name || !selector_name[0] || !group_id)
2003 		return -EINVAL;
2004 
2005 	s = selector_find(ctl, selector_name);
2006 	if (!s)
2007 		return -EINVAL;
2008 
2009 	/* Find an unused group. */
2010 	for (i = 0; i < s->info.n_groups_max; i++)
2011 		if (!s->groups_added[i]) {
2012 			*group_id = i;
2013 			s->groups_added[i] = 1;
2014 			return 0;
2015 		}
2016 
2017 	return -ENOSPC;
2018 }
2019 
2020 int
2021 rte_swx_ctl_pipeline_selector_group_delete(struct rte_swx_ctl_pipeline *ctl,
2022 					   const char *selector_name,
2023 					   uint32_t group_id)
2024 {
2025 	struct selector *s;
2026 	struct rte_swx_table_selector_group *group;
2027 
2028 	/* Check input arguments. */
2029 	if (!ctl || !selector_name || !selector_name[0])
2030 		return -EINVAL;
2031 
2032 	s = selector_find(ctl, selector_name);
2033 	if (!s ||
2034 	   (group_id >= s->info.n_groups_max) ||
2035 	   !s->groups_added[group_id])
2036 		return -EINVAL;
2037 
2038 	/* Check if this group is already scheduled for deletion. */
2039 	if (s->groups_pending_delete[group_id])
2040 		return 0;
2041 
2042 	/* Initialize the pending group, if needed. */
2043 	if (!s->pending_groups[group_id]) {
2044 		int status;
2045 
2046 		status = selector_group_duplicate_to_pending(s, group_id);
2047 		if (status)
2048 			return status;
2049 	}
2050 
2051 	group = s->pending_groups[group_id];
2052 
2053 	/* Schedule removal of all the members from the current group. */
2054 	for ( ; ; ) {
2055 		struct rte_swx_table_selector_member *m;
2056 
2057 		m = TAILQ_FIRST(&group->members);
2058 		if (!m)
2059 			break;
2060 
2061 		TAILQ_REMOVE(&group->members, m, node);
2062 		free(m);
2063 	}
2064 
2065 	/* Schedule the group for deletion. */
2066 	s->groups_pending_delete[group_id] = 1;
2067 
2068 	return 0;
2069 }
2070 
2071 int
2072 rte_swx_ctl_pipeline_selector_group_member_add(struct rte_swx_ctl_pipeline *ctl,
2073 					       const char *selector_name,
2074 					       uint32_t group_id,
2075 					       uint32_t member_id,
2076 					       uint32_t member_weight)
2077 {
2078 	struct selector *s;
2079 	struct rte_swx_table_selector_group *group;
2080 	struct rte_swx_table_selector_member *m;
2081 
2082 	if (!member_weight)
2083 		return rte_swx_ctl_pipeline_selector_group_member_delete(ctl,
2084 									 selector_name,
2085 									 group_id,
2086 									 member_id);
2087 
2088 	/* Check input arguments. */
2089 	if (!ctl || !selector_name || !selector_name[0])
2090 		return -EINVAL;
2091 
2092 	s = selector_find(ctl, selector_name);
2093 	if (!s ||
2094 	   (group_id >= s->info.n_groups_max) ||
2095 	   !s->groups_added[group_id] ||
2096 	   s->groups_pending_delete[group_id])
2097 		return -EINVAL;
2098 
2099 	/* Initialize the pending group, if needed. */
2100 	if (!s->pending_groups[group_id]) {
2101 		int status;
2102 
2103 		status = selector_group_duplicate_to_pending(s, group_id);
2104 		if (status)
2105 			return status;
2106 	}
2107 
2108 	group = s->pending_groups[group_id];
2109 
2110 	/* If this member is already in this group, then simply update its weight and return. */
2111 	TAILQ_FOREACH(m, &group->members, node)
2112 		if (m->member_id == member_id) {
2113 			m->member_weight = member_weight;
2114 			return 0;
2115 		}
2116 
2117 	/* Add new member to this group. */
2118 	m = calloc(1, sizeof(struct rte_swx_table_selector_member));
2119 	if (!m)
2120 		return -ENOMEM;
2121 
2122 	m->member_id = member_id;
2123 	m->member_weight = member_weight;
2124 
2125 	TAILQ_INSERT_TAIL(&group->members, m, node);
2126 
2127 	return 0;
2128 }
2129 
2130 int
2131 rte_swx_ctl_pipeline_selector_group_member_delete(struct rte_swx_ctl_pipeline *ctl,
2132 						  const char *selector_name,
2133 						  uint32_t group_id __rte_unused,
2134 						  uint32_t member_id __rte_unused)
2135 {
2136 	struct selector *s;
2137 	struct rte_swx_table_selector_group *group;
2138 	struct rte_swx_table_selector_member *m;
2139 
2140 	/* Check input arguments. */
2141 	if (!ctl || !selector_name || !selector_name[0])
2142 		return -EINVAL;
2143 
2144 	s = selector_find(ctl, selector_name);
2145 	if (!s ||
2146 	    (group_id >= s->info.n_groups_max) ||
2147 	    !s->groups_added[group_id] ||
2148 	    s->groups_pending_delete[group_id])
2149 		return -EINVAL;
2150 
2151 	/* Initialize the pending group, if needed. */
2152 	if (!s->pending_groups[group_id]) {
2153 		int status;
2154 
2155 		status = selector_group_duplicate_to_pending(s, group_id);
2156 		if (status)
2157 			return status;
2158 	}
2159 
2160 	group = s->pending_groups[group_id];
2161 
2162 	/* Look for this member in the group and remove it, if found. */
2163 	TAILQ_FOREACH(m, &group->members, node)
2164 		if (m->member_id == member_id) {
2165 			TAILQ_REMOVE(&group->members, m, node);
2166 			free(m);
2167 			return 0;
2168 		}
2169 
2170 	return 0;
2171 }
2172 
2173 static int
2174 selector_rollfwd(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id)
2175 {
2176 	struct selector *s = &ctl->selectors[selector_id];
2177 	struct rte_swx_table_state *ts_next = &ctl->ts_next[ctl->info.n_tables + selector_id];
2178 	uint32_t group_id;
2179 
2180 	/* Push pending group member changes (s->pending_groups[group_id]) to the selector table
2181 	 * mirror copy (ts_next->obj).
2182 	 */
2183 	for (group_id = 0; group_id < s->info.n_groups_max; group_id++) {
2184 		struct rte_swx_table_selector_group *group = s->pending_groups[group_id];
2185 		int status;
2186 
2187 		/* Skip this group if no change needed. */
2188 		if (!group)
2189 			continue;
2190 
2191 		/* Apply the pending changes for the current group. */
2192 		status = rte_swx_table_selector_group_set(ts_next->obj, group_id, group);
2193 		if (status)
2194 			return status;
2195 	}
2196 
2197 	return 0;
2198 }
2199 
2200 static void
2201 selector_rollfwd_finalize(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id)
2202 {
2203 	struct selector *s = &ctl->selectors[selector_id];
2204 	uint32_t group_id;
2205 
2206 	/* Commit pending group member changes (s->pending_groups[group_id]) to the stable group
2207 	 * records (s->groups[group_id).
2208 	 */
2209 	for (group_id = 0; group_id < s->info.n_groups_max; group_id++) {
2210 		struct rte_swx_table_selector_group *g = s->groups[group_id];
2211 		struct rte_swx_table_selector_group *gp = s->pending_groups[group_id];
2212 
2213 		/* Skip this group if no change needed. */
2214 		if (!gp)
2215 			continue;
2216 
2217 		/* Transition the pending changes to stable. */
2218 		s->groups[group_id] = gp;
2219 		s->pending_groups[group_id] = NULL;
2220 
2221 		/* Free the old group member list. */
2222 		if (!g)
2223 			continue;
2224 
2225 		for ( ; ; ) {
2226 			struct rte_swx_table_selector_member *m;
2227 
2228 			m = TAILQ_FIRST(&g->members);
2229 			if (!m)
2230 				break;
2231 
2232 			TAILQ_REMOVE(&g->members, m, node);
2233 			free(m);
2234 		}
2235 
2236 		free(g);
2237 	}
2238 
2239 	/* Commit pending group validity changes (from s->groups_pending_delete[group_id] to
2240 	 * s->groups_added[group_id].
2241 	 */
2242 	for (group_id = 0; group_id < s->info.n_groups_max; group_id++)
2243 		if (s->groups_pending_delete[group_id]) {
2244 			s->groups_added[group_id] = 0;
2245 			s->groups_pending_delete[group_id] = 0;
2246 		}
2247 }
2248 
2249 static void
2250 selector_rollback(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id)
2251 {
2252 	struct selector *s = &ctl->selectors[selector_id];
2253 	struct rte_swx_table_state *ts = &ctl->ts[ctl->info.n_tables + selector_id];
2254 	struct rte_swx_table_state *ts_next = &ctl->ts_next[ctl->info.n_tables + selector_id];
2255 	uint32_t group_id;
2256 
2257 	/* Discard any previous changes to the selector table mirror copy (ts_next->obj). */
2258 	for (group_id = 0; group_id < s->info.n_groups_max; group_id++) {
2259 		struct rte_swx_table_selector_group *gp = s->pending_groups[group_id];
2260 
2261 		if (gp) {
2262 			ts_next->obj = ts->obj;
2263 			break;
2264 		}
2265 	}
2266 }
2267 
2268 static void
2269 selector_abort(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id)
2270 {
2271 	struct selector *s = &ctl->selectors[selector_id];
2272 	uint32_t group_id;
2273 
2274 	/* Discard any pending group member changes (s->pending_groups[group_id]). */
2275 	for (group_id = 0; group_id < s->info.n_groups_max; group_id++)
2276 		selector_pending_group_members_free(s, group_id);
2277 
2278 	/* Discard any pending group deletions. */
2279 	memset(s->groups_pending_delete, 0, s->info.n_groups_max * sizeof(int));
2280 }
2281 
2282 static struct rte_swx_table_entry *
2283 learner_default_entry_alloc(struct learner *l)
2284 {
2285 	struct rte_swx_table_entry *entry;
2286 
2287 	entry = calloc(1, sizeof(struct rte_swx_table_entry));
2288 	if (!entry)
2289 		goto error;
2290 
2291 	/* action_data. */
2292 	if (l->action_data_size) {
2293 		entry->action_data = calloc(1, l->action_data_size);
2294 		if (!entry->action_data)
2295 			goto error;
2296 	}
2297 
2298 	return entry;
2299 
2300 error:
2301 	table_entry_free(entry);
2302 	return NULL;
2303 }
2304 
2305 static int
2306 learner_default_entry_check(struct rte_swx_ctl_pipeline *ctl,
2307 			    uint32_t learner_id,
2308 			    struct rte_swx_table_entry *entry)
2309 {
2310 	struct learner *l = &ctl->learners[learner_id];
2311 	struct action *a;
2312 	uint32_t i;
2313 
2314 	CHECK(entry, EINVAL);
2315 
2316 	/* action_id. */
2317 	for (i = 0; i < l->info.n_actions; i++)
2318 		if (entry->action_id == l->actions[i].action_id)
2319 			break;
2320 
2321 	CHECK(i < l->info.n_actions, EINVAL);
2322 
2323 	/* action_data. */
2324 	a = &ctl->actions[entry->action_id];
2325 	CHECK(!(a->data_size && !entry->action_data), EINVAL);
2326 
2327 	return 0;
2328 }
2329 
2330 static struct rte_swx_table_entry *
2331 learner_default_entry_duplicate(struct rte_swx_ctl_pipeline *ctl,
2332 				uint32_t learner_id,
2333 				struct rte_swx_table_entry *entry)
2334 {
2335 	struct learner *l = &ctl->learners[learner_id];
2336 	struct rte_swx_table_entry *new_entry = NULL;
2337 	struct action *a;
2338 	uint32_t i;
2339 
2340 	if (!entry)
2341 		goto error;
2342 
2343 	new_entry = calloc(1, sizeof(struct rte_swx_table_entry));
2344 	if (!new_entry)
2345 		goto error;
2346 
2347 	/* action_id. */
2348 	for (i = 0; i < l->info.n_actions; i++)
2349 		if (entry->action_id == l->actions[i].action_id)
2350 			break;
2351 
2352 	if (i >= l->info.n_actions)
2353 		goto error;
2354 
2355 	new_entry->action_id = entry->action_id;
2356 
2357 	/* action_data. */
2358 	a = &ctl->actions[entry->action_id];
2359 	if (a->data_size && !entry->action_data)
2360 		goto error;
2361 
2362 	/* The table layer provisions a constant action data size per
2363 	 * entry, which should be the largest data size for all the
2364 	 * actions enabled for the current table, and attempts to copy
2365 	 * this many bytes each time a table entry is added, even if the
2366 	 * specific action requires less data or even no data at all,
2367 	 * hence we always have to allocate the max.
2368 	 */
2369 	new_entry->action_data = calloc(1, l->action_data_size);
2370 	if (!new_entry->action_data)
2371 		goto error;
2372 
2373 	if (a->data_size)
2374 		memcpy(new_entry->action_data, entry->action_data, a->data_size);
2375 
2376 	return new_entry;
2377 
2378 error:
2379 	table_entry_free(new_entry);
2380 	return NULL;
2381 }
2382 
2383 int
2384 rte_swx_ctl_pipeline_learner_default_entry_add(struct rte_swx_ctl_pipeline *ctl,
2385 					       const char *learner_name,
2386 					       struct rte_swx_table_entry *entry)
2387 {
2388 	struct learner *l;
2389 	struct rte_swx_table_entry *new_entry;
2390 	uint32_t learner_id;
2391 
2392 	CHECK(ctl, EINVAL);
2393 
2394 	CHECK(learner_name && learner_name[0], EINVAL);
2395 	l = learner_find(ctl, learner_name);
2396 	CHECK(l, EINVAL);
2397 	learner_id = l - ctl->learners;
2398 	CHECK(!l->info.default_action_is_const, EINVAL);
2399 
2400 	CHECK(entry, EINVAL);
2401 	CHECK(!learner_default_entry_check(ctl, learner_id, entry), EINVAL);
2402 
2403 	CHECK(l->actions[entry->action_id].action_is_for_default_entry, EINVAL);
2404 
2405 	new_entry = learner_default_entry_duplicate(ctl, learner_id, entry);
2406 	CHECK(new_entry, ENOMEM);
2407 
2408 	learner_pending_default_free(l);
2409 
2410 	l->pending_default = new_entry;
2411 	return 0;
2412 }
2413 
2414 static void
2415 learner_rollfwd(struct rte_swx_ctl_pipeline *ctl, uint32_t learner_id)
2416 {
2417 	struct learner *l = &ctl->learners[learner_id];
2418 	struct rte_swx_table_state *ts_next = &ctl->ts_next[ctl->info.n_tables +
2419 		ctl->info.n_selectors + learner_id];
2420 	struct action *a;
2421 	uint8_t *action_data;
2422 	uint64_t action_id;
2423 
2424 	/* Copy the pending default entry. */
2425 	if (!l->pending_default)
2426 		return;
2427 
2428 	action_id = l->pending_default->action_id;
2429 	action_data = l->pending_default->action_data;
2430 	a = &ctl->actions[action_id];
2431 
2432 	if (a->data_size)
2433 		memcpy(ts_next->default_action_data, action_data, a->data_size);
2434 
2435 	ts_next->default_action_id = action_id;
2436 }
2437 
2438 static void
2439 learner_rollfwd_finalize(struct rte_swx_ctl_pipeline *ctl, uint32_t learner_id)
2440 {
2441 	struct learner *l = &ctl->learners[learner_id];
2442 
2443 	/* Free up the pending default entry, as it is now part of the table. */
2444 	learner_pending_default_free(l);
2445 }
2446 
2447 static void
2448 learner_abort(struct rte_swx_ctl_pipeline *ctl, uint32_t learner_id)
2449 {
2450 	struct learner *l = &ctl->learners[learner_id];
2451 
2452 	/* Free up the pending default entry, as it is no longer going to be added to the table. */
2453 	learner_pending_default_free(l);
2454 }
2455 
2456 int
2457 rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail)
2458 {
2459 	struct rte_swx_table_state *ts;
2460 	int status = 0;
2461 	uint32_t i;
2462 
2463 	CHECK(ctl, EINVAL);
2464 
2465 	/* Operate the changes on the current ts_next before it becomes the new ts. First, operate
2466 	 * all the changes that can fail; if no failure, then operate the changes that cannot fail.
2467 	 * We must be able to fully revert all the changes that can fail as if they never happened.
2468 	 */
2469 	for (i = 0; i < ctl->info.n_tables; i++) {
2470 		status = table_rollfwd0(ctl, i, 0);
2471 		if (status)
2472 			goto rollback;
2473 	}
2474 
2475 	for (i = 0; i < ctl->info.n_selectors; i++) {
2476 		status = selector_rollfwd(ctl, i);
2477 		if (status)
2478 			goto rollback;
2479 	}
2480 
2481 	/* Second, operate all the changes that cannot fail. Since nothing can fail from this point
2482 	 * onwards, the transaction is guaranteed to be successful.
2483 	 */
2484 	for (i = 0; i < ctl->info.n_tables; i++)
2485 		table_rollfwd1(ctl, i);
2486 
2487 	for (i = 0; i < ctl->info.n_learners; i++)
2488 		learner_rollfwd(ctl, i);
2489 
2490 	/* Swap the table state for the data plane. The current ts and ts_next
2491 	 * become the new ts_next and ts, respectively.
2492 	 */
2493 	rte_swx_pipeline_table_state_set(ctl->p, ctl->ts_next);
2494 	usleep(100);
2495 	ts = ctl->ts;
2496 	ctl->ts = ctl->ts_next;
2497 	ctl->ts_next = ts;
2498 
2499 	/* Operate the changes on the current ts_next, which is the previous ts, in order to get
2500 	 * the current ts_next in sync with the current ts. Since the changes that can fail did
2501 	 * not fail on the previous ts_next, it is guaranteed that they will not fail on the
2502 	 * current ts_next, hence no error checking is needed.
2503 	 */
2504 	for (i = 0; i < ctl->info.n_tables; i++) {
2505 		table_rollfwd0(ctl, i, 1);
2506 		table_rollfwd1(ctl, i);
2507 		table_rollfwd2(ctl, i);
2508 	}
2509 
2510 	for (i = 0; i < ctl->info.n_selectors; i++) {
2511 		selector_rollfwd(ctl, i);
2512 		selector_rollfwd_finalize(ctl, i);
2513 	}
2514 
2515 	for (i = 0; i < ctl->info.n_learners; i++) {
2516 		learner_rollfwd(ctl, i);
2517 		learner_rollfwd_finalize(ctl, i);
2518 	}
2519 
2520 	return 0;
2521 
2522 rollback:
2523 	for (i = 0; i < ctl->info.n_tables; i++) {
2524 		table_rollback(ctl, i);
2525 		if (abort_on_fail)
2526 			table_abort(ctl, i);
2527 	}
2528 
2529 	for (i = 0; i < ctl->info.n_selectors; i++) {
2530 		selector_rollback(ctl, i);
2531 		if (abort_on_fail)
2532 			selector_abort(ctl, i);
2533 	}
2534 
2535 	if (abort_on_fail)
2536 		for (i = 0; i < ctl->info.n_learners; i++)
2537 			learner_abort(ctl, i);
2538 
2539 	return status;
2540 }
2541 
2542 void
2543 rte_swx_ctl_pipeline_abort(struct rte_swx_ctl_pipeline *ctl)
2544 {
2545 	uint32_t i;
2546 
2547 	if (!ctl)
2548 		return;
2549 
2550 	for (i = 0; i < ctl->info.n_tables; i++)
2551 		table_abort(ctl, i);
2552 
2553 	for (i = 0; i < ctl->info.n_selectors; i++)
2554 		selector_abort(ctl, i);
2555 
2556 	for (i = 0; i < ctl->info.n_learners; i++)
2557 		learner_abort(ctl, i);
2558 }
2559 
2560 static int
2561 mask_to_prefix(uint64_t mask, uint32_t mask_length, uint32_t *prefix_length)
2562 {
2563 	uint32_t n_trailing_zeros = 0, n_ones = 0, i;
2564 
2565 	if (!mask) {
2566 		*prefix_length = 0;
2567 		return 0;
2568 	}
2569 
2570 	/* Count trailing zero bits. */
2571 	for (i = 0; i < 64; i++) {
2572 		if (mask & (1LLU << i))
2573 			break;
2574 
2575 		n_trailing_zeros++;
2576 	}
2577 
2578 	/* Count the one bits that follow. */
2579 	for ( ; i < 64; i++) {
2580 		if (!(mask & (1LLU << i)))
2581 			break;
2582 
2583 		n_ones++;
2584 	}
2585 
2586 	/* Check that no more one bits are present */
2587 	for ( ; i < 64; i++)
2588 		if (mask & (1LLU << i))
2589 			return -EINVAL;
2590 
2591 	/* Check that the input mask is a prefix or the right length. */
2592 	if (n_ones + n_trailing_zeros != mask_length)
2593 		return -EINVAL;
2594 
2595 	*prefix_length = n_ones;
2596 	return 0;
2597 }
2598 
2599 static int
2600 token_is_comment(const char *token)
2601 {
2602 	if ((token[0] == '#') ||
2603 	    (token[0] == ';') ||
2604 	    ((token[0] == '/') && (token[1] == '/')))
2605 		return 1; /* TRUE. */
2606 
2607 	return 0; /* FALSE. */
2608 }
2609 
2610 #define RTE_SWX_CTL_ENTRY_TOKENS_MAX 256
2611 
2612 struct rte_swx_table_entry *
2613 rte_swx_ctl_pipeline_table_entry_read(struct rte_swx_ctl_pipeline *ctl,
2614 				      const char *table_name,
2615 				      const char *string,
2616 				      int *is_blank_or_comment)
2617 {
2618 	char *token_array[RTE_SWX_CTL_ENTRY_TOKENS_MAX], **tokens;
2619 	struct table *table;
2620 	struct action *action;
2621 	struct rte_swx_table_entry *entry = NULL;
2622 	char *s0 = NULL, *s;
2623 	uint32_t n_tokens = 0, arg_offset = 0, lpm_prefix_length_max = 0, lpm_prefix_length = 0, i;
2624 	int lpm = 0, blank_or_comment = 0;
2625 
2626 	/* Check input arguments. */
2627 	if (!ctl)
2628 		goto error;
2629 
2630 	if (!table_name || !table_name[0])
2631 		goto error;
2632 
2633 	table = table_find(ctl, table_name);
2634 	if (!table)
2635 		goto error;
2636 
2637 	if (!string || !string[0])
2638 		goto error;
2639 
2640 	/* Memory allocation. */
2641 	s0 = strdup(string);
2642 	if (!s0)
2643 		goto error;
2644 
2645 	entry = table_entry_alloc(table);
2646 	if (!entry)
2647 		goto error;
2648 
2649 	/* Parse the string into tokens. */
2650 	for (s = s0; ; ) {
2651 		char *token;
2652 
2653 		token = strtok_r(s, " \f\n\r\t\v", &s);
2654 		if (!token || token_is_comment(token))
2655 			break;
2656 
2657 		if (n_tokens >= RTE_SWX_CTL_ENTRY_TOKENS_MAX)
2658 			goto error;
2659 
2660 		token_array[n_tokens] = token;
2661 		n_tokens++;
2662 	}
2663 
2664 	if (!n_tokens) {
2665 		blank_or_comment = 1;
2666 		goto error;
2667 	}
2668 
2669 	tokens = token_array;
2670 
2671 	/*
2672 	 * Match.
2673 	 */
2674 	if (!(n_tokens && !strcmp(tokens[0], "match")))
2675 		goto action;
2676 
2677 	if (n_tokens < 1 + table->info.n_match_fields)
2678 		goto error;
2679 
2680 	for (i = 0; i < table->info.n_match_fields; i++) {
2681 		struct rte_swx_ctl_table_match_field_info *mf = &table->mf[i];
2682 		char *mf_val = tokens[1 + i], *mf_mask = NULL;
2683 		uint64_t val, mask = UINT64_MAX;
2684 		uint32_t offset = (mf->offset - table->mf_first->offset) / 8;
2685 
2686 		/*
2687 		 * Mask.
2688 		 */
2689 		mf_mask = strchr(mf_val, '/');
2690 		if (mf_mask) {
2691 			*mf_mask = 0;
2692 			mf_mask++;
2693 
2694 			/* Parse. */
2695 			mask = strtoull(mf_mask, &mf_mask, 0);
2696 			if (mf_mask[0])
2697 				goto error;
2698 
2699 			/* LPM. */
2700 			if (mf->match_type == RTE_SWX_TABLE_MATCH_LPM) {
2701 				int status;
2702 
2703 				lpm = 1;
2704 
2705 				lpm_prefix_length_max = mf->n_bits;
2706 
2707 				status = mask_to_prefix(mask, mf->n_bits, &lpm_prefix_length);
2708 				if (status)
2709 					goto error;
2710 			}
2711 
2712 			/* Endianness conversion. */
2713 			if (mf->is_header)
2714 				mask = field_hton(mask, mf->n_bits);
2715 		}
2716 
2717 		/* Copy to entry. */
2718 		if (entry->key_mask)
2719 			memcpy(&entry->key_mask[offset],
2720 			       (uint8_t *)&mask,
2721 			       mf->n_bits / 8);
2722 
2723 		/*
2724 		 * Value.
2725 		 */
2726 		/* Parse. */
2727 		val = strtoull(mf_val, &mf_val, 0);
2728 		if (mf_val[0])
2729 			goto error;
2730 
2731 		/* Endianness conversion. */
2732 		if (mf->is_header)
2733 			val = field_hton(val, mf->n_bits);
2734 
2735 		/* Copy to entry. */
2736 		memcpy(&entry->key[offset],
2737 		       (uint8_t *)&val,
2738 		       mf->n_bits / 8);
2739 	}
2740 
2741 	tokens += 1 + table->info.n_match_fields;
2742 	n_tokens -= 1 + table->info.n_match_fields;
2743 
2744 	/*
2745 	 * Match priority.
2746 	 */
2747 	if (n_tokens && !strcmp(tokens[0], "priority")) {
2748 		char *priority = tokens[1];
2749 		uint32_t val;
2750 
2751 		if (n_tokens < 2)
2752 			goto error;
2753 
2754 		/* Parse. */
2755 		val = strtoul(priority, &priority, 0);
2756 		if (priority[0])
2757 			goto error;
2758 
2759 		/* Copy to entry. */
2760 		entry->key_priority = val;
2761 
2762 		tokens += 2;
2763 		n_tokens -= 2;
2764 	}
2765 
2766 	/* LPM. */
2767 	if (lpm)
2768 		entry->key_priority = lpm_prefix_length_max - lpm_prefix_length;
2769 
2770 	/*
2771 	 * Action.
2772 	 */
2773 action:
2774 	if (!(n_tokens && !strcmp(tokens[0], "action")))
2775 		goto other;
2776 
2777 	if (n_tokens < 2)
2778 		goto error;
2779 
2780 	action = action_find(ctl, tokens[1]);
2781 	if (!action)
2782 		goto error;
2783 
2784 	if (n_tokens < 2 + action->info.n_args * 2)
2785 		goto error;
2786 
2787 	/* action_id. */
2788 	entry->action_id = action - ctl->actions;
2789 
2790 	/* action_data. */
2791 	for (i = 0; i < action->info.n_args; i++) {
2792 		struct rte_swx_ctl_action_arg_info *arg = &action->args[i];
2793 		char *arg_name, *arg_val;
2794 		uint64_t val;
2795 
2796 		arg_name = tokens[2 + i * 2];
2797 		arg_val = tokens[2 + i * 2 + 1];
2798 
2799 		if (strcmp(arg_name, arg->name))
2800 			goto error;
2801 
2802 		val = strtoull(arg_val, &arg_val, 0);
2803 		if (arg_val[0])
2804 			goto error;
2805 
2806 		/* Endianness conversion. */
2807 		if (arg->is_network_byte_order)
2808 			val = field_hton(val, arg->n_bits);
2809 
2810 		/* Copy to entry. */
2811 		memcpy(&entry->action_data[arg_offset],
2812 		       (uint8_t *)&val,
2813 		       arg->n_bits / 8);
2814 
2815 		arg_offset += arg->n_bits / 8;
2816 	}
2817 
2818 	tokens += 2 + action->info.n_args * 2;
2819 	n_tokens -= 2 + action->info.n_args * 2;
2820 
2821 other:
2822 	if (n_tokens)
2823 		goto error;
2824 
2825 	free(s0);
2826 	return entry;
2827 
2828 error:
2829 	table_entry_free(entry);
2830 	free(s0);
2831 	if (is_blank_or_comment)
2832 		*is_blank_or_comment = blank_or_comment;
2833 	return NULL;
2834 }
2835 
2836 struct rte_swx_table_entry *
2837 rte_swx_ctl_pipeline_learner_default_entry_read(struct rte_swx_ctl_pipeline *ctl,
2838 						const char *learner_name,
2839 						const char *string,
2840 						int *is_blank_or_comment)
2841 {
2842 	char *token_array[RTE_SWX_CTL_ENTRY_TOKENS_MAX], **tokens;
2843 	struct learner *l;
2844 	struct action *action;
2845 	struct rte_swx_table_entry *entry = NULL;
2846 	char *s0 = NULL, *s;
2847 	uint32_t n_tokens = 0, arg_offset = 0, i;
2848 	int blank_or_comment = 0;
2849 
2850 	/* Check input arguments. */
2851 	if (!ctl)
2852 		goto error;
2853 
2854 	if (!learner_name || !learner_name[0])
2855 		goto error;
2856 
2857 	l = learner_find(ctl, learner_name);
2858 	if (!l)
2859 		goto error;
2860 
2861 	if (!string || !string[0])
2862 		goto error;
2863 
2864 	/* Memory allocation. */
2865 	s0 = strdup(string);
2866 	if (!s0)
2867 		goto error;
2868 
2869 	entry = learner_default_entry_alloc(l);
2870 	if (!entry)
2871 		goto error;
2872 
2873 	/* Parse the string into tokens. */
2874 	for (s = s0; ; ) {
2875 		char *token;
2876 
2877 		token = strtok_r(s, " \f\n\r\t\v", &s);
2878 		if (!token || token_is_comment(token))
2879 			break;
2880 
2881 		if (n_tokens >= RTE_SWX_CTL_ENTRY_TOKENS_MAX)
2882 			goto error;
2883 
2884 		token_array[n_tokens] = token;
2885 		n_tokens++;
2886 	}
2887 
2888 	if (!n_tokens) {
2889 		blank_or_comment = 1;
2890 		goto error;
2891 	}
2892 
2893 	tokens = token_array;
2894 
2895 	/*
2896 	 * Action.
2897 	 */
2898 	if (!(n_tokens && !strcmp(tokens[0], "action")))
2899 		goto other;
2900 
2901 	if (n_tokens < 2)
2902 		goto error;
2903 
2904 	action = action_find(ctl, tokens[1]);
2905 	if (!action)
2906 		goto error;
2907 
2908 	if (n_tokens < 2 + action->info.n_args * 2)
2909 		goto error;
2910 
2911 	/* action_id. */
2912 	entry->action_id = action - ctl->actions;
2913 
2914 	/* action_data. */
2915 	for (i = 0; i < action->info.n_args; i++) {
2916 		struct rte_swx_ctl_action_arg_info *arg = &action->args[i];
2917 		char *arg_name, *arg_val;
2918 		uint64_t val;
2919 
2920 		arg_name = tokens[2 + i * 2];
2921 		arg_val = tokens[2 + i * 2 + 1];
2922 
2923 		if (strcmp(arg_name, arg->name))
2924 			goto error;
2925 
2926 		val = strtoull(arg_val, &arg_val, 0);
2927 		if (arg_val[0])
2928 			goto error;
2929 
2930 		/* Endianness conversion. */
2931 		if (arg->is_network_byte_order)
2932 			val = field_hton(val, arg->n_bits);
2933 
2934 		/* Copy to entry. */
2935 		memcpy(&entry->action_data[arg_offset],
2936 		       (uint8_t *)&val,
2937 		       arg->n_bits / 8);
2938 
2939 		arg_offset += arg->n_bits / 8;
2940 	}
2941 
2942 	tokens += 2 + action->info.n_args * 2;
2943 	n_tokens -= 2 + action->info.n_args * 2;
2944 
2945 other:
2946 	if (n_tokens)
2947 		goto error;
2948 
2949 	free(s0);
2950 	return entry;
2951 
2952 error:
2953 	table_entry_free(entry);
2954 	free(s0);
2955 	if (is_blank_or_comment)
2956 		*is_blank_or_comment = blank_or_comment;
2957 	return NULL;
2958 }
2959 
2960 static void
2961 table_entry_printf(FILE *f,
2962 		   struct rte_swx_ctl_pipeline *ctl,
2963 		   struct table *table,
2964 		   struct rte_swx_table_entry *entry)
2965 {
2966 	struct action *action = &ctl->actions[entry->action_id];
2967 	uint32_t i;
2968 
2969 	fprintf(f, "match ");
2970 	for (i = 0; i < table->params.key_size; i++)
2971 		fprintf(f, "%02x", entry->key[i]);
2972 
2973 	if (entry->key_mask) {
2974 		fprintf(f, "/");
2975 		for (i = 0; i < table->params.key_size; i++)
2976 			fprintf(f, "%02x", entry->key_mask[i]);
2977 	}
2978 
2979 	fprintf(f, " priority %u", entry->key_priority);
2980 
2981 	fprintf(f, " action %s ", action->info.name);
2982 	for (i = 0; i < action->data_size; i++)
2983 		fprintf(f, "%02x", entry->action_data[i]);
2984 
2985 	fprintf(f, "\n");
2986 }
2987 
2988 int
2989 rte_swx_ctl_pipeline_table_fprintf(FILE *f,
2990 				   struct rte_swx_ctl_pipeline *ctl,
2991 				   const char *table_name)
2992 {
2993 	struct table *table;
2994 	struct rte_swx_table_entry *entry;
2995 	uint32_t n_entries = 0, i;
2996 
2997 	if (!f || !ctl || !table_name || !table_name[0])
2998 		return -EINVAL;
2999 
3000 	table = table_find(ctl, table_name);
3001 	if (!table)
3002 		return -EINVAL;
3003 
3004 	/* Table. */
3005 	fprintf(f, "# Table %s: key size %u bytes, key offset %u, key mask [",
3006 		table->info.name,
3007 		table->params.key_size,
3008 		table->params.key_offset);
3009 
3010 	for (i = 0; i < table->params.key_size; i++)
3011 		fprintf(f, "%02x", table->params.key_mask0[i]);
3012 
3013 	fprintf(f, "], action data size %u bytes\n",
3014 		table->params.action_data_size);
3015 
3016 	/* Table entries. */
3017 	TAILQ_FOREACH(entry, &table->entries, node) {
3018 		table_entry_printf(f, ctl, table, entry);
3019 		n_entries++;
3020 	}
3021 
3022 	TAILQ_FOREACH(entry, &table->pending_modify0, node) {
3023 		table_entry_printf(f, ctl, table, entry);
3024 		n_entries++;
3025 	}
3026 
3027 	TAILQ_FOREACH(entry, &table->pending_delete, node) {
3028 		table_entry_printf(f, ctl, table, entry);
3029 		n_entries++;
3030 	}
3031 
3032 	fprintf(f, "# Table %s currently has %u entries.\n",
3033 		table_name,
3034 		n_entries);
3035 	return 0;
3036 }
3037 
3038 int
3039 rte_swx_ctl_pipeline_selector_fprintf(FILE *f,
3040 				      struct rte_swx_ctl_pipeline *ctl,
3041 				      const char *selector_name)
3042 {
3043 	struct selector *s;
3044 	uint32_t group_id;
3045 
3046 	if (!f || !ctl || !selector_name || !selector_name[0])
3047 		return -EINVAL;
3048 
3049 	s = selector_find(ctl, selector_name);
3050 	if (!s)
3051 		return -EINVAL;
3052 
3053 	/* Selector. */
3054 	fprintf(f, "# Selector %s: max groups %u, max members per group %u\n",
3055 		s->info.name,
3056 		s->info.n_groups_max,
3057 		s->info.n_members_per_group_max);
3058 
3059 	/* Groups. */
3060 	for (group_id = 0; group_id < s->info.n_groups_max; group_id++) {
3061 		struct rte_swx_table_selector_group *group = s->groups[group_id];
3062 		struct rte_swx_table_selector_member *m;
3063 		uint32_t n_members = 0;
3064 
3065 		fprintf(f, "Group %u = [", group_id);
3066 
3067 		/* Non-empty group. */
3068 		if (group)
3069 			TAILQ_FOREACH(m, &group->members, node) {
3070 				fprintf(f, "%u:%u ", m->member_id, m->member_weight);
3071 				n_members++;
3072 			}
3073 
3074 		/* Empty group. */
3075 		if (!n_members)
3076 			fprintf(f, "0:1 ");
3077 
3078 		fprintf(f, "]\n");
3079 	}
3080 
3081 	return 0;
3082 }
3083