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