xref: /dpdk/lib/pipeline/rte_swx_ctl.c (revision daa02b5cddbb8e11b31d41e2bf7bb1ae64dcae2f)
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 	new_entry = table_entry_duplicate(ctl, table_id, entry, 1, 1);
1450 	CHECK(new_entry, ENOMEM);
1451 
1452 	/* The new entry is found in the table->entries list:
1453 	 * - Add the new entry to the table->pending_modify1 list;
1454 	 * - Move the existing entry from the table->entries list to the
1455 	 *   table->pending_modify0 list.
1456 	 */
1457 	existing_entry = table_entries_find(table, entry);
1458 	if (existing_entry) {
1459 		TAILQ_INSERT_TAIL(&table->pending_modify1,
1460 				  new_entry,
1461 				  node);
1462 
1463 		TAILQ_REMOVE(&table->entries,
1464 			     existing_entry,
1465 			     node);
1466 
1467 		TAILQ_INSERT_TAIL(&table->pending_modify0,
1468 				  existing_entry,
1469 				  node);
1470 
1471 		return 0;
1472 	}
1473 
1474 	/* The new entry is found in the table->pending_add list:
1475 	 * - Replace the entry in the table->pending_add list with the new entry
1476 	 *   (and free the replaced entry).
1477 	 */
1478 	existing_entry = table_pending_add_find(table, entry);
1479 	if (existing_entry) {
1480 		TAILQ_INSERT_AFTER(&table->pending_add,
1481 				   existing_entry,
1482 				   new_entry,
1483 				   node);
1484 
1485 		TAILQ_REMOVE(&table->pending_add,
1486 			     existing_entry,
1487 			     node);
1488 
1489 		table_entry_free(existing_entry);
1490 
1491 		return 0;
1492 	}
1493 
1494 	/* The new entry is found in the table->pending_modify1 list:
1495 	 * - Replace the entry in the table->pending_modify1 list with the new
1496 	 *   entry (and free the replaced entry).
1497 	 */
1498 	existing_entry = table_pending_modify1_find(table, entry);
1499 	if (existing_entry) {
1500 		TAILQ_INSERT_AFTER(&table->pending_modify1,
1501 				   existing_entry,
1502 				   new_entry,
1503 				   node);
1504 
1505 		TAILQ_REMOVE(&table->pending_modify1,
1506 			     existing_entry,
1507 			     node);
1508 
1509 		table_entry_free(existing_entry);
1510 
1511 		return 0;
1512 	}
1513 
1514 	/* The new entry is found in the table->pending_delete list:
1515 	 * - Add the new entry to the table->pending_modify1 list;
1516 	 * - Move the existing entry from the table->pending_delete list to the
1517 	 *   table->pending_modify0 list.
1518 	 */
1519 	existing_entry = table_pending_delete_find(table, entry);
1520 	if (existing_entry) {
1521 		TAILQ_INSERT_TAIL(&table->pending_modify1,
1522 				  new_entry,
1523 				  node);
1524 
1525 		TAILQ_REMOVE(&table->pending_delete,
1526 			     existing_entry,
1527 			     node);
1528 
1529 		TAILQ_INSERT_TAIL(&table->pending_modify0,
1530 				  existing_entry,
1531 				  node);
1532 
1533 		return 0;
1534 	}
1535 
1536 	/* The new entry is not found in any of the above lists:
1537 	 * - Add the new entry to the table->pending_add list.
1538 	 */
1539 	TAILQ_INSERT_TAIL(&table->pending_add, new_entry, node);
1540 
1541 	return 0;
1542 }
1543 
1544 int
1545 rte_swx_ctl_pipeline_table_entry_delete(struct rte_swx_ctl_pipeline *ctl,
1546 					const char *table_name,
1547 					struct rte_swx_table_entry *entry)
1548 {
1549 	struct table *table;
1550 	struct rte_swx_table_entry *existing_entry;
1551 	uint32_t table_id;
1552 
1553 	CHECK(ctl, EINVAL);
1554 
1555 	CHECK(table_name && table_name[0], EINVAL);
1556 	table = table_find(ctl, table_name);
1557 	CHECK(table, EINVAL);
1558 	table_id = table - ctl->tables;
1559 
1560 	CHECK(entry, EINVAL);
1561 	CHECK(!table_entry_check(ctl, table_id, entry, 1, 0), EINVAL);
1562 
1563 	/* The entry is found in the table->entries list:
1564 	 * - Move the existing entry from the table->entries list to to the
1565 	 *   table->pending_delete list.
1566 	 */
1567 	existing_entry = table_entries_find(table, entry);
1568 	if (existing_entry) {
1569 		TAILQ_REMOVE(&table->entries,
1570 			     existing_entry,
1571 			     node);
1572 
1573 		TAILQ_INSERT_TAIL(&table->pending_delete,
1574 				  existing_entry,
1575 				  node);
1576 
1577 		return 0;
1578 	}
1579 
1580 	/* The entry is found in the table->pending_add list:
1581 	 * - Remove the entry from the table->pending_add list and free it.
1582 	 */
1583 	existing_entry = table_pending_add_find(table, entry);
1584 	if (existing_entry) {
1585 		TAILQ_REMOVE(&table->pending_add,
1586 			     existing_entry,
1587 			     node);
1588 
1589 		table_entry_free(existing_entry);
1590 	}
1591 
1592 	/* The entry is found in the table->pending_modify1 list:
1593 	 * - Free the entry in the table->pending_modify1 list;
1594 	 * - Move the existing entry from the table->pending_modify0 list to the
1595 	 *   table->pending_delete list.
1596 	 */
1597 	existing_entry = table_pending_modify1_find(table, entry);
1598 	if (existing_entry) {
1599 		struct rte_swx_table_entry *real_existing_entry;
1600 
1601 		TAILQ_REMOVE(&table->pending_modify1,
1602 			     existing_entry,
1603 			     node);
1604 
1605 		table_entry_free(existing_entry);
1606 
1607 		real_existing_entry = table_pending_modify0_find(table, entry);
1608 		CHECK(real_existing_entry, EINVAL); /* Coverity. */
1609 
1610 		TAILQ_REMOVE(&table->pending_modify0,
1611 			     real_existing_entry,
1612 			     node);
1613 
1614 		TAILQ_INSERT_TAIL(&table->pending_delete,
1615 				  real_existing_entry,
1616 				  node);
1617 
1618 		return 0;
1619 	}
1620 
1621 	/* The entry is found in the table->pending_delete list:
1622 	 * - Do nothing: the existing entry is already in the
1623 	 *   table->pending_delete list, i.e. already marked for delete, so
1624 	 *   simply keep it there as it is.
1625 	 */
1626 
1627 	/* The entry is not found in any of the above lists:
1628 	 * - Do nothing: no existing entry to delete.
1629 	 */
1630 
1631 	return 0;
1632 }
1633 
1634 int
1635 rte_swx_ctl_pipeline_table_default_entry_add(struct rte_swx_ctl_pipeline *ctl,
1636 					     const char *table_name,
1637 					     struct rte_swx_table_entry *entry)
1638 {
1639 	struct table *table;
1640 	struct rte_swx_table_entry *new_entry;
1641 	uint32_t table_id;
1642 
1643 	CHECK(ctl, EINVAL);
1644 
1645 	CHECK(table_name && table_name[0], EINVAL);
1646 	table = table_find(ctl, table_name);
1647 	CHECK(table, EINVAL);
1648 	table_id = table - ctl->tables;
1649 	CHECK(!table->info.default_action_is_const, EINVAL);
1650 
1651 	CHECK(entry, EINVAL);
1652 	CHECK(!table_entry_check(ctl, table_id, entry, 0, 1), EINVAL);
1653 
1654 	new_entry = table_entry_duplicate(ctl, table_id, entry, 0, 1);
1655 	CHECK(new_entry, ENOMEM);
1656 
1657 	table_pending_default_free(table);
1658 
1659 	table->pending_default = new_entry;
1660 	return 0;
1661 }
1662 
1663 
1664 static void
1665 table_entry_list_free(struct rte_swx_table_entry_list *list)
1666 {
1667 	for ( ; ; ) {
1668 		struct rte_swx_table_entry *entry;
1669 
1670 		entry = TAILQ_FIRST(list);
1671 		if (!entry)
1672 			break;
1673 
1674 		TAILQ_REMOVE(list, entry, node);
1675 		table_entry_free(entry);
1676 	}
1677 }
1678 
1679 static int
1680 table_entry_list_duplicate(struct rte_swx_ctl_pipeline *ctl,
1681 			   uint32_t table_id,
1682 			   struct rte_swx_table_entry_list *dst,
1683 			   struct rte_swx_table_entry_list *src)
1684 {
1685 	struct rte_swx_table_entry *src_entry;
1686 
1687 	TAILQ_FOREACH(src_entry, src, node) {
1688 		struct rte_swx_table_entry *dst_entry;
1689 
1690 		dst_entry = table_entry_duplicate(ctl, table_id, src_entry, 1, 1);
1691 		if (!dst_entry)
1692 			goto error;
1693 
1694 		TAILQ_INSERT_TAIL(dst, dst_entry, node);
1695 	}
1696 
1697 	return 0;
1698 
1699 error:
1700 	table_entry_list_free(dst);
1701 	return -ENOMEM;
1702 }
1703 
1704 /* This commit stage contains all the operations that can fail; in case ANY of
1705  * them fails for ANY table, ALL of them are rolled back for ALL the tables.
1706  */
1707 static int
1708 table_rollfwd0(struct rte_swx_ctl_pipeline *ctl,
1709 	       uint32_t table_id,
1710 	       uint32_t after_swap)
1711 {
1712 	struct table *table = &ctl->tables[table_id];
1713 	struct rte_swx_table_state *ts = &ctl->ts[table_id];
1714 	struct rte_swx_table_state *ts_next = &ctl->ts_next[table_id];
1715 
1716 	if (table->is_stub || !table_is_update_pending(table, 0))
1717 		return 0;
1718 
1719 	/*
1720 	 * Current table supports incremental update.
1721 	 */
1722 	if (table->ops.add) {
1723 		/* Reset counters. */
1724 		table->n_add = 0;
1725 		table->n_modify = 0;
1726 		table->n_delete = 0;
1727 
1728 		/* Add pending rules. */
1729 		struct rte_swx_table_entry *entry;
1730 
1731 		TAILQ_FOREACH(entry, &table->pending_add, node) {
1732 			int status;
1733 
1734 			status = table->ops.add(ts_next->obj, entry);
1735 			if (status)
1736 				return status;
1737 
1738 			table->n_add++;
1739 		}
1740 
1741 		/* Modify pending rules. */
1742 		TAILQ_FOREACH(entry, &table->pending_modify1, node) {
1743 			int status;
1744 
1745 			status = table->ops.add(ts_next->obj, entry);
1746 			if (status)
1747 				return status;
1748 
1749 			table->n_modify++;
1750 		}
1751 
1752 		/* Delete pending rules. */
1753 		TAILQ_FOREACH(entry, &table->pending_delete, node) {
1754 			int status;
1755 
1756 			status = table->ops.del(ts_next->obj, entry);
1757 			if (status)
1758 				return status;
1759 
1760 			table->n_delete++;
1761 		}
1762 
1763 		return 0;
1764 	}
1765 
1766 	/*
1767 	 * Current table does NOT support incremental update.
1768 	 */
1769 	if (!after_swap) {
1770 		struct rte_swx_table_entry_list list;
1771 		int status;
1772 
1773 		/* Create updated list of entries included. */
1774 		TAILQ_INIT(&list);
1775 
1776 		status = table_entry_list_duplicate(ctl,
1777 						    table_id,
1778 						    &list,
1779 						    &table->entries);
1780 		if (status)
1781 			goto error;
1782 
1783 		status = table_entry_list_duplicate(ctl,
1784 						    table_id,
1785 						    &list,
1786 						    &table->pending_add);
1787 		if (status)
1788 			goto error;
1789 
1790 		status = table_entry_list_duplicate(ctl,
1791 						    table_id,
1792 						    &list,
1793 						    &table->pending_modify1);
1794 		if (status)
1795 			goto error;
1796 
1797 		/* Create new table object with the updates included. */
1798 		ts_next->obj = table->ops.create(&table->params,
1799 						 &list,
1800 						 table->info.args,
1801 						 ctl->numa_node);
1802 		if (!ts_next->obj) {
1803 			status = -ENODEV;
1804 			goto error;
1805 		}
1806 
1807 		table_entry_list_free(&list);
1808 
1809 		return 0;
1810 
1811 error:
1812 		table_entry_list_free(&list);
1813 		return status;
1814 	}
1815 
1816 	/* Free the old table object. */
1817 	if (ts_next->obj && table->ops.free)
1818 		table->ops.free(ts_next->obj);
1819 
1820 	/* Copy over the new table object. */
1821 	ts_next->obj = ts->obj;
1822 
1823 	return 0;
1824 }
1825 
1826 /* This commit stage contains all the operations that cannot fail. They are
1827  * executed only if the previous stage was successful for ALL the tables. Hence,
1828  * none of these operations has to be rolled back for ANY table.
1829  */
1830 static void
1831 table_rollfwd1(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)
1832 {
1833 	struct table *table = &ctl->tables[table_id];
1834 	struct rte_swx_table_state *ts_next = &ctl->ts_next[table_id];
1835 	struct action *a;
1836 	uint8_t *action_data;
1837 	uint64_t action_id;
1838 
1839 	/* Copy the pending default entry. */
1840 	if (!table->pending_default)
1841 		return;
1842 
1843 	action_id = table->pending_default->action_id;
1844 	action_data = table->pending_default->action_data;
1845 	a = &ctl->actions[action_id];
1846 
1847 	if (a->data_size)
1848 		memcpy(ts_next->default_action_data, action_data, a->data_size);
1849 
1850 	ts_next->default_action_id = action_id;
1851 }
1852 
1853 /* This last commit stage is simply finalizing a successful commit operation.
1854  * This stage is only executed if all the previous stages were successful. This
1855  * stage cannot fail.
1856  */
1857 static void
1858 table_rollfwd2(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)
1859 {
1860 	struct table *table = &ctl->tables[table_id];
1861 
1862 	/* Move all the pending add entries to the table, as they are now part
1863 	 * of the table.
1864 	 */
1865 	table_pending_add_admit(table);
1866 
1867 	/* Move all the pending modify1 entries to table, are they are now part
1868 	 * of the table. Free up all the pending modify0 entries, as they are no
1869 	 * longer part of the table.
1870 	 */
1871 	table_pending_modify1_admit(table);
1872 	table_pending_modify0_free(table);
1873 
1874 	/* Free up all the pending delete entries, as they are no longer part of
1875 	 * the table.
1876 	 */
1877 	table_pending_delete_free(table);
1878 
1879 	/* Free up the pending default entry, as it is now part of the table. */
1880 	table_pending_default_free(table);
1881 }
1882 
1883 /* The rollback stage is only executed when the commit failed, i.e. ANY of the
1884  * commit operations that can fail did fail for ANY table. It reverts ALL the
1885  * tables to their state before the commit started, as if the commit never
1886  * happened.
1887  */
1888 static void
1889 table_rollback(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)
1890 {
1891 	struct table *table = &ctl->tables[table_id];
1892 	struct rte_swx_table_state *ts_next = &ctl->ts_next[table_id];
1893 
1894 	if (table->is_stub || !table_is_update_pending(table, 0))
1895 		return;
1896 
1897 	if (table->ops.add) {
1898 		struct rte_swx_table_entry *entry;
1899 
1900 		/* Add back all the entries that were just deleted. */
1901 		TAILQ_FOREACH(entry, &table->pending_delete, node) {
1902 			if (!table->n_delete)
1903 				break;
1904 
1905 			table->ops.add(ts_next->obj, entry);
1906 			table->n_delete--;
1907 		}
1908 
1909 		/* Add back the old copy for all the entries that were just
1910 		 * modified.
1911 		 */
1912 		TAILQ_FOREACH(entry, &table->pending_modify0, node) {
1913 			if (!table->n_modify)
1914 				break;
1915 
1916 			table->ops.add(ts_next->obj, entry);
1917 			table->n_modify--;
1918 		}
1919 
1920 		/* Delete all the entries that were just added. */
1921 		TAILQ_FOREACH(entry, &table->pending_add, node) {
1922 			if (!table->n_add)
1923 				break;
1924 
1925 			table->ops.del(ts_next->obj, entry);
1926 			table->n_add--;
1927 		}
1928 	} else {
1929 		struct rte_swx_table_state *ts = &ctl->ts[table_id];
1930 
1931 		/* Free the new table object, as update was cancelled. */
1932 		if (ts_next->obj && table->ops.free)
1933 			table->ops.free(ts_next->obj);
1934 
1935 		/* Reinstate the old table object. */
1936 		ts_next->obj = ts->obj;
1937 	}
1938 }
1939 
1940 /* This stage is conditionally executed (as instructed by the user) after a
1941  * failed commit operation to remove ALL the pending work for ALL the tables.
1942  */
1943 static void
1944 table_abort(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)
1945 {
1946 	struct table *table = &ctl->tables[table_id];
1947 
1948 	/* Free up all the pending add entries, as none of them is part of the
1949 	 * table.
1950 	 */
1951 	table_pending_add_free(table);
1952 
1953 	/* Free up all the pending modify1 entries, as none of them made it to
1954 	 * the table. Add back all the pending modify0 entries, as none of them
1955 	 * was deleted from the table.
1956 	 */
1957 	table_pending_modify1_free(table);
1958 	table_pending_modify0_admit(table);
1959 
1960 	/* Add back all the pending delete entries, as none of them was deleted
1961 	 * from the table.
1962 	 */
1963 	table_pending_delete_admit(table);
1964 
1965 	/* Free up the pending default entry, as it is no longer going to be
1966 	 * added to the table.
1967 	 */
1968 	table_pending_default_free(table);
1969 }
1970 
1971 int
1972 rte_swx_ctl_pipeline_selector_group_add(struct rte_swx_ctl_pipeline *ctl,
1973 					const char *selector_name,
1974 					uint32_t *group_id)
1975 {
1976 	struct selector *s;
1977 	uint32_t i;
1978 
1979 	/* Check input arguments. */
1980 	if (!ctl || !selector_name || !selector_name[0] || !group_id)
1981 		return -EINVAL;
1982 
1983 	s = selector_find(ctl, selector_name);
1984 	if (!s)
1985 		return -EINVAL;
1986 
1987 	/* Find an unused group. */
1988 	for (i = 0; i < s->info.n_groups_max; i++)
1989 		if (!s->groups_added[i]) {
1990 			*group_id = i;
1991 			s->groups_added[i] = 1;
1992 			return 0;
1993 		}
1994 
1995 	return -ENOSPC;
1996 }
1997 
1998 int
1999 rte_swx_ctl_pipeline_selector_group_delete(struct rte_swx_ctl_pipeline *ctl,
2000 					   const char *selector_name,
2001 					   uint32_t group_id)
2002 {
2003 	struct selector *s;
2004 	struct rte_swx_table_selector_group *group;
2005 
2006 	/* Check input arguments. */
2007 	if (!ctl || !selector_name || !selector_name[0])
2008 		return -EINVAL;
2009 
2010 	s = selector_find(ctl, selector_name);
2011 	if (!s ||
2012 	   (group_id >= s->info.n_groups_max) ||
2013 	   !s->groups_added[group_id])
2014 		return -EINVAL;
2015 
2016 	/* Check if this group is already scheduled for deletion. */
2017 	if (s->groups_pending_delete[group_id])
2018 		return 0;
2019 
2020 	/* Initialize the pending group, if needed. */
2021 	if (!s->pending_groups[group_id]) {
2022 		int status;
2023 
2024 		status = selector_group_duplicate_to_pending(s, group_id);
2025 		if (status)
2026 			return status;
2027 	}
2028 
2029 	group = s->pending_groups[group_id];
2030 
2031 	/* Schedule removal of all the members from the current group. */
2032 	for ( ; ; ) {
2033 		struct rte_swx_table_selector_member *m;
2034 
2035 		m = TAILQ_FIRST(&group->members);
2036 		if (!m)
2037 			break;
2038 
2039 		TAILQ_REMOVE(&group->members, m, node);
2040 		free(m);
2041 	}
2042 
2043 	/* Schedule the group for deletion. */
2044 	s->groups_pending_delete[group_id] = 1;
2045 
2046 	return 0;
2047 }
2048 
2049 int
2050 rte_swx_ctl_pipeline_selector_group_member_add(struct rte_swx_ctl_pipeline *ctl,
2051 					       const char *selector_name,
2052 					       uint32_t group_id,
2053 					       uint32_t member_id,
2054 					       uint32_t member_weight)
2055 {
2056 	struct selector *s;
2057 	struct rte_swx_table_selector_group *group;
2058 	struct rte_swx_table_selector_member *m;
2059 
2060 	if (!member_weight)
2061 		return rte_swx_ctl_pipeline_selector_group_member_delete(ctl,
2062 									 selector_name,
2063 									 group_id,
2064 									 member_id);
2065 
2066 	/* Check input arguments. */
2067 	if (!ctl || !selector_name || !selector_name[0])
2068 		return -EINVAL;
2069 
2070 	s = selector_find(ctl, selector_name);
2071 	if (!s ||
2072 	   (group_id >= s->info.n_groups_max) ||
2073 	   !s->groups_added[group_id] ||
2074 	   s->groups_pending_delete[group_id])
2075 		return -EINVAL;
2076 
2077 	/* Initialize the pending group, if needed. */
2078 	if (!s->pending_groups[group_id]) {
2079 		int status;
2080 
2081 		status = selector_group_duplicate_to_pending(s, group_id);
2082 		if (status)
2083 			return status;
2084 	}
2085 
2086 	group = s->pending_groups[group_id];
2087 
2088 	/* If this member is already in this group, then simply update its weight and return. */
2089 	TAILQ_FOREACH(m, &group->members, node)
2090 		if (m->member_id == member_id) {
2091 			m->member_weight = member_weight;
2092 			return 0;
2093 		}
2094 
2095 	/* Add new member to this group. */
2096 	m = calloc(1, sizeof(struct rte_swx_table_selector_member));
2097 	if (!m)
2098 		return -ENOMEM;
2099 
2100 	m->member_id = member_id;
2101 	m->member_weight = member_weight;
2102 
2103 	TAILQ_INSERT_TAIL(&group->members, m, node);
2104 
2105 	return 0;
2106 }
2107 
2108 int
2109 rte_swx_ctl_pipeline_selector_group_member_delete(struct rte_swx_ctl_pipeline *ctl,
2110 						  const char *selector_name,
2111 						  uint32_t group_id __rte_unused,
2112 						  uint32_t member_id __rte_unused)
2113 {
2114 	struct selector *s;
2115 	struct rte_swx_table_selector_group *group;
2116 	struct rte_swx_table_selector_member *m;
2117 
2118 	/* Check input arguments. */
2119 	if (!ctl || !selector_name || !selector_name[0])
2120 		return -EINVAL;
2121 
2122 	s = selector_find(ctl, selector_name);
2123 	if (!s ||
2124 	    (group_id >= s->info.n_groups_max) ||
2125 	    !s->groups_added[group_id] ||
2126 	    s->groups_pending_delete[group_id])
2127 		return -EINVAL;
2128 
2129 	/* Initialize the pending group, if needed. */
2130 	if (!s->pending_groups[group_id]) {
2131 		int status;
2132 
2133 		status = selector_group_duplicate_to_pending(s, group_id);
2134 		if (status)
2135 			return status;
2136 	}
2137 
2138 	group = s->pending_groups[group_id];
2139 
2140 	/* Look for this member in the group and remove it, if found. */
2141 	TAILQ_FOREACH(m, &group->members, node)
2142 		if (m->member_id == member_id) {
2143 			TAILQ_REMOVE(&group->members, m, node);
2144 			free(m);
2145 			return 0;
2146 		}
2147 
2148 	return 0;
2149 }
2150 
2151 static int
2152 selector_rollfwd(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id)
2153 {
2154 	struct selector *s = &ctl->selectors[selector_id];
2155 	struct rte_swx_table_state *ts_next = &ctl->ts_next[ctl->info.n_tables + selector_id];
2156 	uint32_t group_id;
2157 
2158 	/* Push pending group member changes (s->pending_groups[group_id]) to the selector table
2159 	 * mirror copy (ts_next->obj).
2160 	 */
2161 	for (group_id = 0; group_id < s->info.n_groups_max; group_id++) {
2162 		struct rte_swx_table_selector_group *group = s->pending_groups[group_id];
2163 		int status;
2164 
2165 		/* Skip this group if no change needed. */
2166 		if (!group)
2167 			continue;
2168 
2169 		/* Apply the pending changes for the current group. */
2170 		status = rte_swx_table_selector_group_set(ts_next->obj, group_id, group);
2171 		if (status)
2172 			return status;
2173 	}
2174 
2175 	return 0;
2176 }
2177 
2178 static void
2179 selector_rollfwd_finalize(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id)
2180 {
2181 	struct selector *s = &ctl->selectors[selector_id];
2182 	uint32_t group_id;
2183 
2184 	/* Commit pending group member changes (s->pending_groups[group_id]) to the stable group
2185 	 * records (s->groups[group_id).
2186 	 */
2187 	for (group_id = 0; group_id < s->info.n_groups_max; group_id++) {
2188 		struct rte_swx_table_selector_group *g = s->groups[group_id];
2189 		struct rte_swx_table_selector_group *gp = s->pending_groups[group_id];
2190 
2191 		/* Skip this group if no change needed. */
2192 		if (!gp)
2193 			continue;
2194 
2195 		/* Transition the pending changes to stable. */
2196 		s->groups[group_id] = gp;
2197 		s->pending_groups[group_id] = NULL;
2198 
2199 		/* Free the old group member list. */
2200 		if (!g)
2201 			continue;
2202 
2203 		for ( ; ; ) {
2204 			struct rte_swx_table_selector_member *m;
2205 
2206 			m = TAILQ_FIRST(&g->members);
2207 			if (!m)
2208 				break;
2209 
2210 			TAILQ_REMOVE(&g->members, m, node);
2211 			free(m);
2212 		}
2213 
2214 		free(g);
2215 	}
2216 
2217 	/* Commit pending group validity changes (from s->groups_pending_delete[group_id] to
2218 	 * s->groups_added[group_id].
2219 	 */
2220 	for (group_id = 0; group_id < s->info.n_groups_max; group_id++)
2221 		if (s->groups_pending_delete[group_id]) {
2222 			s->groups_added[group_id] = 0;
2223 			s->groups_pending_delete[group_id] = 0;
2224 		}
2225 }
2226 
2227 static void
2228 selector_rollback(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id)
2229 {
2230 	struct selector *s = &ctl->selectors[selector_id];
2231 	struct rte_swx_table_state *ts = &ctl->ts[ctl->info.n_tables + selector_id];
2232 	struct rte_swx_table_state *ts_next = &ctl->ts_next[ctl->info.n_tables + selector_id];
2233 	uint32_t group_id;
2234 
2235 	/* Discard any previous changes to the selector table mirror copy (ts_next->obj). */
2236 	for (group_id = 0; group_id < s->info.n_groups_max; group_id++) {
2237 		struct rte_swx_table_selector_group *gp = s->pending_groups[group_id];
2238 
2239 		if (gp) {
2240 			ts_next->obj = ts->obj;
2241 			break;
2242 		}
2243 	}
2244 }
2245 
2246 static void
2247 selector_abort(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id)
2248 {
2249 	struct selector *s = &ctl->selectors[selector_id];
2250 	uint32_t group_id;
2251 
2252 	/* Discard any pending group member changes (s->pending_groups[group_id]). */
2253 	for (group_id = 0; group_id < s->info.n_groups_max; group_id++)
2254 		selector_pending_group_members_free(s, group_id);
2255 
2256 	/* Discard any pending group deletions. */
2257 	memset(s->groups_pending_delete, 0, s->info.n_groups_max * sizeof(int));
2258 }
2259 
2260 static struct rte_swx_table_entry *
2261 learner_default_entry_alloc(struct learner *l)
2262 {
2263 	struct rte_swx_table_entry *entry;
2264 
2265 	entry = calloc(1, sizeof(struct rte_swx_table_entry));
2266 	if (!entry)
2267 		goto error;
2268 
2269 	/* action_data. */
2270 	if (l->action_data_size) {
2271 		entry->action_data = calloc(1, l->action_data_size);
2272 		if (!entry->action_data)
2273 			goto error;
2274 	}
2275 
2276 	return entry;
2277 
2278 error:
2279 	table_entry_free(entry);
2280 	return NULL;
2281 }
2282 
2283 static int
2284 learner_default_entry_check(struct rte_swx_ctl_pipeline *ctl,
2285 			    uint32_t learner_id,
2286 			    struct rte_swx_table_entry *entry)
2287 {
2288 	struct learner *l = &ctl->learners[learner_id];
2289 	struct action *a;
2290 	uint32_t i;
2291 
2292 	CHECK(entry, EINVAL);
2293 
2294 	/* action_id. */
2295 	for (i = 0; i < l->info.n_actions; i++)
2296 		if (entry->action_id == l->actions[i].action_id)
2297 			break;
2298 
2299 	CHECK(i < l->info.n_actions, EINVAL);
2300 
2301 	/* action_data. */
2302 	a = &ctl->actions[entry->action_id];
2303 	CHECK(!(a->data_size && !entry->action_data), EINVAL);
2304 
2305 	return 0;
2306 }
2307 
2308 static struct rte_swx_table_entry *
2309 learner_default_entry_duplicate(struct rte_swx_ctl_pipeline *ctl,
2310 				uint32_t learner_id,
2311 				struct rte_swx_table_entry *entry)
2312 {
2313 	struct learner *l = &ctl->learners[learner_id];
2314 	struct rte_swx_table_entry *new_entry = NULL;
2315 	struct action *a;
2316 	uint32_t i;
2317 
2318 	if (!entry)
2319 		goto error;
2320 
2321 	new_entry = calloc(1, sizeof(struct rte_swx_table_entry));
2322 	if (!new_entry)
2323 		goto error;
2324 
2325 	/* action_id. */
2326 	for (i = 0; i < l->info.n_actions; i++)
2327 		if (entry->action_id == l->actions[i].action_id)
2328 			break;
2329 
2330 	if (i >= l->info.n_actions)
2331 		goto error;
2332 
2333 	new_entry->action_id = entry->action_id;
2334 
2335 	/* action_data. */
2336 	a = &ctl->actions[entry->action_id];
2337 	if (a->data_size && !entry->action_data)
2338 		goto error;
2339 
2340 	/* The table layer provisions a constant action data size per
2341 	 * entry, which should be the largest data size for all the
2342 	 * actions enabled for the current table, and attempts to copy
2343 	 * this many bytes each time a table entry is added, even if the
2344 	 * specific action requires less data or even no data at all,
2345 	 * hence we always have to allocate the max.
2346 	 */
2347 	new_entry->action_data = calloc(1, l->action_data_size);
2348 	if (!new_entry->action_data)
2349 		goto error;
2350 
2351 	if (a->data_size)
2352 		memcpy(new_entry->action_data, entry->action_data, a->data_size);
2353 
2354 	return new_entry;
2355 
2356 error:
2357 	table_entry_free(new_entry);
2358 	return NULL;
2359 }
2360 
2361 int
2362 rte_swx_ctl_pipeline_learner_default_entry_add(struct rte_swx_ctl_pipeline *ctl,
2363 					       const char *learner_name,
2364 					       struct rte_swx_table_entry *entry)
2365 {
2366 	struct learner *l;
2367 	struct rte_swx_table_entry *new_entry;
2368 	uint32_t learner_id;
2369 
2370 	CHECK(ctl, EINVAL);
2371 
2372 	CHECK(learner_name && learner_name[0], EINVAL);
2373 	l = learner_find(ctl, learner_name);
2374 	CHECK(l, EINVAL);
2375 	learner_id = l - ctl->learners;
2376 	CHECK(!l->info.default_action_is_const, EINVAL);
2377 
2378 	CHECK(entry, EINVAL);
2379 	CHECK(!learner_default_entry_check(ctl, learner_id, entry), EINVAL);
2380 
2381 	new_entry = learner_default_entry_duplicate(ctl, learner_id, entry);
2382 	CHECK(new_entry, ENOMEM);
2383 
2384 	learner_pending_default_free(l);
2385 
2386 	l->pending_default = new_entry;
2387 	return 0;
2388 }
2389 
2390 static void
2391 learner_rollfwd(struct rte_swx_ctl_pipeline *ctl, uint32_t learner_id)
2392 {
2393 	struct learner *l = &ctl->learners[learner_id];
2394 	struct rte_swx_table_state *ts_next = &ctl->ts_next[ctl->info.n_tables +
2395 		ctl->info.n_selectors + learner_id];
2396 	struct action *a;
2397 	uint8_t *action_data;
2398 	uint64_t action_id;
2399 
2400 	/* Copy the pending default entry. */
2401 	if (!l->pending_default)
2402 		return;
2403 
2404 	action_id = l->pending_default->action_id;
2405 	action_data = l->pending_default->action_data;
2406 	a = &ctl->actions[action_id];
2407 
2408 	if (a->data_size)
2409 		memcpy(ts_next->default_action_data, action_data, a->data_size);
2410 
2411 	ts_next->default_action_id = action_id;
2412 }
2413 
2414 static void
2415 learner_rollfwd_finalize(struct rte_swx_ctl_pipeline *ctl, uint32_t learner_id)
2416 {
2417 	struct learner *l = &ctl->learners[learner_id];
2418 
2419 	/* Free up the pending default entry, as it is now part of the table. */
2420 	learner_pending_default_free(l);
2421 }
2422 
2423 static void
2424 learner_abort(struct rte_swx_ctl_pipeline *ctl, uint32_t learner_id)
2425 {
2426 	struct learner *l = &ctl->learners[learner_id];
2427 
2428 	/* Free up the pending default entry, as it is no longer going to be added to the table. */
2429 	learner_pending_default_free(l);
2430 }
2431 
2432 int
2433 rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail)
2434 {
2435 	struct rte_swx_table_state *ts;
2436 	int status = 0;
2437 	uint32_t i;
2438 
2439 	CHECK(ctl, EINVAL);
2440 
2441 	/* Operate the changes on the current ts_next before it becomes the new ts. First, operate
2442 	 * all the changes that can fail; if no failure, then operate the changes that cannot fail.
2443 	 * We must be able to fully revert all the changes that can fail as if they never happened.
2444 	 */
2445 	for (i = 0; i < ctl->info.n_tables; i++) {
2446 		status = table_rollfwd0(ctl, i, 0);
2447 		if (status)
2448 			goto rollback;
2449 	}
2450 
2451 	for (i = 0; i < ctl->info.n_selectors; i++) {
2452 		status = selector_rollfwd(ctl, i);
2453 		if (status)
2454 			goto rollback;
2455 	}
2456 
2457 	/* Second, operate all the changes that cannot fail. Since nothing can fail from this point
2458 	 * onwards, the transaction is guaranteed to be successful.
2459 	 */
2460 	for (i = 0; i < ctl->info.n_tables; i++)
2461 		table_rollfwd1(ctl, i);
2462 
2463 	for (i = 0; i < ctl->info.n_learners; i++)
2464 		learner_rollfwd(ctl, i);
2465 
2466 	/* Swap the table state for the data plane. The current ts and ts_next
2467 	 * become the new ts_next and ts, respectively.
2468 	 */
2469 	rte_swx_pipeline_table_state_set(ctl->p, ctl->ts_next);
2470 	usleep(100);
2471 	ts = ctl->ts;
2472 	ctl->ts = ctl->ts_next;
2473 	ctl->ts_next = ts;
2474 
2475 	/* Operate the changes on the current ts_next, which is the previous ts, in order to get
2476 	 * the current ts_next in sync with the current ts. Since the changes that can fail did
2477 	 * not fail on the previous ts_next, it is guaranteed that they will not fail on the
2478 	 * current ts_next, hence no error checking is needed.
2479 	 */
2480 	for (i = 0; i < ctl->info.n_tables; i++) {
2481 		table_rollfwd0(ctl, i, 1);
2482 		table_rollfwd1(ctl, i);
2483 		table_rollfwd2(ctl, i);
2484 	}
2485 
2486 	for (i = 0; i < ctl->info.n_selectors; i++) {
2487 		selector_rollfwd(ctl, i);
2488 		selector_rollfwd_finalize(ctl, i);
2489 	}
2490 
2491 	for (i = 0; i < ctl->info.n_learners; i++) {
2492 		learner_rollfwd(ctl, i);
2493 		learner_rollfwd_finalize(ctl, i);
2494 	}
2495 
2496 	return 0;
2497 
2498 rollback:
2499 	for (i = 0; i < ctl->info.n_tables; i++) {
2500 		table_rollback(ctl, i);
2501 		if (abort_on_fail)
2502 			table_abort(ctl, i);
2503 	}
2504 
2505 	for (i = 0; i < ctl->info.n_selectors; i++) {
2506 		selector_rollback(ctl, i);
2507 		if (abort_on_fail)
2508 			selector_abort(ctl, i);
2509 	}
2510 
2511 	if (abort_on_fail)
2512 		for (i = 0; i < ctl->info.n_learners; i++)
2513 			learner_abort(ctl, i);
2514 
2515 	return status;
2516 }
2517 
2518 void
2519 rte_swx_ctl_pipeline_abort(struct rte_swx_ctl_pipeline *ctl)
2520 {
2521 	uint32_t i;
2522 
2523 	if (!ctl)
2524 		return;
2525 
2526 	for (i = 0; i < ctl->info.n_tables; i++)
2527 		table_abort(ctl, i);
2528 
2529 	for (i = 0; i < ctl->info.n_selectors; i++)
2530 		selector_abort(ctl, i);
2531 
2532 	for (i = 0; i < ctl->info.n_learners; i++)
2533 		learner_abort(ctl, i);
2534 }
2535 
2536 static int
2537 mask_to_prefix(uint64_t mask, uint32_t mask_length, uint32_t *prefix_length)
2538 {
2539 	uint32_t n_trailing_zeros = 0, n_ones = 0, i;
2540 
2541 	if (!mask) {
2542 		*prefix_length = 0;
2543 		return 0;
2544 	}
2545 
2546 	/* Count trailing zero bits. */
2547 	for (i = 0; i < 64; i++) {
2548 		if (mask & (1LLU << i))
2549 			break;
2550 
2551 		n_trailing_zeros++;
2552 	}
2553 
2554 	/* Count the one bits that follow. */
2555 	for ( ; i < 64; i++) {
2556 		if (!(mask & (1LLU << i)))
2557 			break;
2558 
2559 		n_ones++;
2560 	}
2561 
2562 	/* Check that no more one bits are present */
2563 	for ( ; i < 64; i++)
2564 		if (mask & (1LLU << i))
2565 			return -EINVAL;
2566 
2567 	/* Check that the input mask is a prefix or the right length. */
2568 	if (n_ones + n_trailing_zeros != mask_length)
2569 		return -EINVAL;
2570 
2571 	*prefix_length = n_ones;
2572 	return 0;
2573 }
2574 
2575 static int
2576 token_is_comment(const char *token)
2577 {
2578 	if ((token[0] == '#') ||
2579 	    (token[0] == ';') ||
2580 	    ((token[0] == '/') && (token[1] == '/')))
2581 		return 1; /* TRUE. */
2582 
2583 	return 0; /* FALSE. */
2584 }
2585 
2586 #define RTE_SWX_CTL_ENTRY_TOKENS_MAX 256
2587 
2588 struct rte_swx_table_entry *
2589 rte_swx_ctl_pipeline_table_entry_read(struct rte_swx_ctl_pipeline *ctl,
2590 				      const char *table_name,
2591 				      const char *string,
2592 				      int *is_blank_or_comment)
2593 {
2594 	char *token_array[RTE_SWX_CTL_ENTRY_TOKENS_MAX], **tokens;
2595 	struct table *table;
2596 	struct action *action;
2597 	struct rte_swx_table_entry *entry = NULL;
2598 	char *s0 = NULL, *s;
2599 	uint32_t n_tokens = 0, arg_offset = 0, lpm_prefix_length_max = 0, lpm_prefix_length = 0, i;
2600 	int lpm = 0, blank_or_comment = 0;
2601 
2602 	/* Check input arguments. */
2603 	if (!ctl)
2604 		goto error;
2605 
2606 	if (!table_name || !table_name[0])
2607 		goto error;
2608 
2609 	table = table_find(ctl, table_name);
2610 	if (!table)
2611 		goto error;
2612 
2613 	if (!string || !string[0])
2614 		goto error;
2615 
2616 	/* Memory allocation. */
2617 	s0 = strdup(string);
2618 	if (!s0)
2619 		goto error;
2620 
2621 	entry = table_entry_alloc(table);
2622 	if (!entry)
2623 		goto error;
2624 
2625 	/* Parse the string into tokens. */
2626 	for (s = s0; ; ) {
2627 		char *token;
2628 
2629 		token = strtok_r(s, " \f\n\r\t\v", &s);
2630 		if (!token || token_is_comment(token))
2631 			break;
2632 
2633 		if (n_tokens >= RTE_SWX_CTL_ENTRY_TOKENS_MAX)
2634 			goto error;
2635 
2636 		token_array[n_tokens] = token;
2637 		n_tokens++;
2638 	}
2639 
2640 	if (!n_tokens) {
2641 		blank_or_comment = 1;
2642 		goto error;
2643 	}
2644 
2645 	tokens = token_array;
2646 
2647 	/*
2648 	 * Match.
2649 	 */
2650 	if (!(n_tokens && !strcmp(tokens[0], "match")))
2651 		goto action;
2652 
2653 	if (n_tokens < 1 + table->info.n_match_fields)
2654 		goto error;
2655 
2656 	for (i = 0; i < table->info.n_match_fields; i++) {
2657 		struct rte_swx_ctl_table_match_field_info *mf = &table->mf[i];
2658 		char *mf_val = tokens[1 + i], *mf_mask = NULL;
2659 		uint64_t val, mask = UINT64_MAX;
2660 		uint32_t offset = (mf->offset - table->mf_first->offset) / 8;
2661 
2662 		/*
2663 		 * Mask.
2664 		 */
2665 		mf_mask = strchr(mf_val, '/');
2666 		if (mf_mask) {
2667 			*mf_mask = 0;
2668 			mf_mask++;
2669 
2670 			/* Parse. */
2671 			mask = strtoull(mf_mask, &mf_mask, 0);
2672 			if (mf_mask[0])
2673 				goto error;
2674 
2675 			/* LPM. */
2676 			if (mf->match_type == RTE_SWX_TABLE_MATCH_LPM) {
2677 				int status;
2678 
2679 				lpm = 1;
2680 
2681 				lpm_prefix_length_max = mf->n_bits;
2682 
2683 				status = mask_to_prefix(mask, mf->n_bits, &lpm_prefix_length);
2684 				if (status)
2685 					goto error;
2686 			}
2687 
2688 			/* Endianness conversion. */
2689 			if (mf->is_header)
2690 				mask = field_hton(mask, mf->n_bits);
2691 		}
2692 
2693 		/* Copy to entry. */
2694 		if (entry->key_mask)
2695 			memcpy(&entry->key_mask[offset],
2696 			       (uint8_t *)&mask,
2697 			       mf->n_bits / 8);
2698 
2699 		/*
2700 		 * Value.
2701 		 */
2702 		/* Parse. */
2703 		val = strtoull(mf_val, &mf_val, 0);
2704 		if (mf_val[0])
2705 			goto error;
2706 
2707 		/* Endianness conversion. */
2708 		if (mf->is_header)
2709 			val = field_hton(val, mf->n_bits);
2710 
2711 		/* Copy to entry. */
2712 		memcpy(&entry->key[offset],
2713 		       (uint8_t *)&val,
2714 		       mf->n_bits / 8);
2715 	}
2716 
2717 	tokens += 1 + table->info.n_match_fields;
2718 	n_tokens -= 1 + table->info.n_match_fields;
2719 
2720 	/*
2721 	 * Match priority.
2722 	 */
2723 	if (n_tokens && !strcmp(tokens[0], "priority")) {
2724 		char *priority = tokens[1];
2725 		uint32_t val;
2726 
2727 		if (n_tokens < 2)
2728 			goto error;
2729 
2730 		/* Parse. */
2731 		val = strtoul(priority, &priority, 0);
2732 		if (priority[0])
2733 			goto error;
2734 
2735 		/* Copy to entry. */
2736 		entry->key_priority = val;
2737 
2738 		tokens += 2;
2739 		n_tokens -= 2;
2740 	}
2741 
2742 	/* LPM. */
2743 	if (lpm)
2744 		entry->key_priority = lpm_prefix_length_max - lpm_prefix_length;
2745 
2746 	/*
2747 	 * Action.
2748 	 */
2749 action:
2750 	if (!(n_tokens && !strcmp(tokens[0], "action")))
2751 		goto other;
2752 
2753 	if (n_tokens < 2)
2754 		goto error;
2755 
2756 	action = action_find(ctl, tokens[1]);
2757 	if (!action)
2758 		goto error;
2759 
2760 	if (n_tokens < 2 + action->info.n_args * 2)
2761 		goto error;
2762 
2763 	/* action_id. */
2764 	entry->action_id = action - ctl->actions;
2765 
2766 	/* action_data. */
2767 	for (i = 0; i < action->info.n_args; i++) {
2768 		struct rte_swx_ctl_action_arg_info *arg = &action->args[i];
2769 		char *arg_name, *arg_val;
2770 		uint64_t val;
2771 
2772 		arg_name = tokens[2 + i * 2];
2773 		arg_val = tokens[2 + i * 2 + 1];
2774 
2775 		if (strcmp(arg_name, arg->name))
2776 			goto error;
2777 
2778 		val = strtoull(arg_val, &arg_val, 0);
2779 		if (arg_val[0])
2780 			goto error;
2781 
2782 		/* Endianness conversion. */
2783 		if (arg->is_network_byte_order)
2784 			val = field_hton(val, arg->n_bits);
2785 
2786 		/* Copy to entry. */
2787 		memcpy(&entry->action_data[arg_offset],
2788 		       (uint8_t *)&val,
2789 		       arg->n_bits / 8);
2790 
2791 		arg_offset += arg->n_bits / 8;
2792 	}
2793 
2794 	tokens += 2 + action->info.n_args * 2;
2795 	n_tokens -= 2 + action->info.n_args * 2;
2796 
2797 other:
2798 	if (n_tokens)
2799 		goto error;
2800 
2801 	free(s0);
2802 	return entry;
2803 
2804 error:
2805 	table_entry_free(entry);
2806 	free(s0);
2807 	if (is_blank_or_comment)
2808 		*is_blank_or_comment = blank_or_comment;
2809 	return NULL;
2810 }
2811 
2812 struct rte_swx_table_entry *
2813 rte_swx_ctl_pipeline_learner_default_entry_read(struct rte_swx_ctl_pipeline *ctl,
2814 						const char *learner_name,
2815 						const char *string,
2816 						int *is_blank_or_comment)
2817 {
2818 	char *token_array[RTE_SWX_CTL_ENTRY_TOKENS_MAX], **tokens;
2819 	struct learner *l;
2820 	struct action *action;
2821 	struct rte_swx_table_entry *entry = NULL;
2822 	char *s0 = NULL, *s;
2823 	uint32_t n_tokens = 0, arg_offset = 0, i;
2824 	int blank_or_comment = 0;
2825 
2826 	/* Check input arguments. */
2827 	if (!ctl)
2828 		goto error;
2829 
2830 	if (!learner_name || !learner_name[0])
2831 		goto error;
2832 
2833 	l = learner_find(ctl, learner_name);
2834 	if (!l)
2835 		goto error;
2836 
2837 	if (!string || !string[0])
2838 		goto error;
2839 
2840 	/* Memory allocation. */
2841 	s0 = strdup(string);
2842 	if (!s0)
2843 		goto error;
2844 
2845 	entry = learner_default_entry_alloc(l);
2846 	if (!entry)
2847 		goto error;
2848 
2849 	/* Parse the string into tokens. */
2850 	for (s = s0; ; ) {
2851 		char *token;
2852 
2853 		token = strtok_r(s, " \f\n\r\t\v", &s);
2854 		if (!token || token_is_comment(token))
2855 			break;
2856 
2857 		if (n_tokens >= RTE_SWX_CTL_ENTRY_TOKENS_MAX)
2858 			goto error;
2859 
2860 		token_array[n_tokens] = token;
2861 		n_tokens++;
2862 	}
2863 
2864 	if (!n_tokens) {
2865 		blank_or_comment = 1;
2866 		goto error;
2867 	}
2868 
2869 	tokens = token_array;
2870 
2871 	/*
2872 	 * Action.
2873 	 */
2874 	if (!(n_tokens && !strcmp(tokens[0], "action")))
2875 		goto other;
2876 
2877 	if (n_tokens < 2)
2878 		goto error;
2879 
2880 	action = action_find(ctl, tokens[1]);
2881 	if (!action)
2882 		goto error;
2883 
2884 	if (n_tokens < 2 + action->info.n_args * 2)
2885 		goto error;
2886 
2887 	/* action_id. */
2888 	entry->action_id = action - ctl->actions;
2889 
2890 	/* action_data. */
2891 	for (i = 0; i < action->info.n_args; i++) {
2892 		struct rte_swx_ctl_action_arg_info *arg = &action->args[i];
2893 		char *arg_name, *arg_val;
2894 		uint64_t val;
2895 
2896 		arg_name = tokens[2 + i * 2];
2897 		arg_val = tokens[2 + i * 2 + 1];
2898 
2899 		if (strcmp(arg_name, arg->name))
2900 			goto error;
2901 
2902 		val = strtoull(arg_val, &arg_val, 0);
2903 		if (arg_val[0])
2904 			goto error;
2905 
2906 		/* Endianness conversion. */
2907 		if (arg->is_network_byte_order)
2908 			val = field_hton(val, arg->n_bits);
2909 
2910 		/* Copy to entry. */
2911 		memcpy(&entry->action_data[arg_offset],
2912 		       (uint8_t *)&val,
2913 		       arg->n_bits / 8);
2914 
2915 		arg_offset += arg->n_bits / 8;
2916 	}
2917 
2918 	tokens += 2 + action->info.n_args * 2;
2919 	n_tokens -= 2 + action->info.n_args * 2;
2920 
2921 other:
2922 	if (n_tokens)
2923 		goto error;
2924 
2925 	free(s0);
2926 	return entry;
2927 
2928 error:
2929 	table_entry_free(entry);
2930 	free(s0);
2931 	if (is_blank_or_comment)
2932 		*is_blank_or_comment = blank_or_comment;
2933 	return NULL;
2934 }
2935 
2936 static void
2937 table_entry_printf(FILE *f,
2938 		   struct rte_swx_ctl_pipeline *ctl,
2939 		   struct table *table,
2940 		   struct rte_swx_table_entry *entry)
2941 {
2942 	struct action *action = &ctl->actions[entry->action_id];
2943 	uint32_t i;
2944 
2945 	fprintf(f, "match ");
2946 	for (i = 0; i < table->params.key_size; i++)
2947 		fprintf(f, "%02x", entry->key[i]);
2948 
2949 	if (entry->key_mask) {
2950 		fprintf(f, "/");
2951 		for (i = 0; i < table->params.key_size; i++)
2952 			fprintf(f, "%02x", entry->key_mask[i]);
2953 	}
2954 
2955 	fprintf(f, " priority %u", entry->key_priority);
2956 
2957 	fprintf(f, " action %s ", action->info.name);
2958 	for (i = 0; i < action->data_size; i++)
2959 		fprintf(f, "%02x", entry->action_data[i]);
2960 
2961 	fprintf(f, "\n");
2962 }
2963 
2964 int
2965 rte_swx_ctl_pipeline_table_fprintf(FILE *f,
2966 				   struct rte_swx_ctl_pipeline *ctl,
2967 				   const char *table_name)
2968 {
2969 	struct table *table;
2970 	struct rte_swx_table_entry *entry;
2971 	uint32_t n_entries = 0, i;
2972 
2973 	if (!f || !ctl || !table_name || !table_name[0])
2974 		return -EINVAL;
2975 
2976 	table = table_find(ctl, table_name);
2977 	if (!table)
2978 		return -EINVAL;
2979 
2980 	/* Table. */
2981 	fprintf(f, "# Table %s: key size %u bytes, key offset %u, key mask [",
2982 		table->info.name,
2983 		table->params.key_size,
2984 		table->params.key_offset);
2985 
2986 	for (i = 0; i < table->params.key_size; i++)
2987 		fprintf(f, "%02x", table->params.key_mask0[i]);
2988 
2989 	fprintf(f, "], action data size %u bytes\n",
2990 		table->params.action_data_size);
2991 
2992 	/* Table entries. */
2993 	TAILQ_FOREACH(entry, &table->entries, node) {
2994 		table_entry_printf(f, ctl, table, entry);
2995 		n_entries++;
2996 	}
2997 
2998 	TAILQ_FOREACH(entry, &table->pending_modify0, node) {
2999 		table_entry_printf(f, ctl, table, entry);
3000 		n_entries++;
3001 	}
3002 
3003 	TAILQ_FOREACH(entry, &table->pending_delete, node) {
3004 		table_entry_printf(f, ctl, table, entry);
3005 		n_entries++;
3006 	}
3007 
3008 	fprintf(f, "# Table %s currently has %u entries.\n",
3009 		table_name,
3010 		n_entries);
3011 	return 0;
3012 }
3013 
3014 int
3015 rte_swx_ctl_pipeline_selector_fprintf(FILE *f,
3016 				      struct rte_swx_ctl_pipeline *ctl,
3017 				      const char *selector_name)
3018 {
3019 	struct selector *s;
3020 	uint32_t group_id;
3021 
3022 	if (!f || !ctl || !selector_name || !selector_name[0])
3023 		return -EINVAL;
3024 
3025 	s = selector_find(ctl, selector_name);
3026 	if (!s)
3027 		return -EINVAL;
3028 
3029 	/* Selector. */
3030 	fprintf(f, "# Selector %s: max groups %u, max members per group %u\n",
3031 		s->info.name,
3032 		s->info.n_groups_max,
3033 		s->info.n_members_per_group_max);
3034 
3035 	/* Groups. */
3036 	for (group_id = 0; group_id < s->info.n_groups_max; group_id++) {
3037 		struct rte_swx_table_selector_group *group = s->groups[group_id];
3038 		struct rte_swx_table_selector_member *m;
3039 		uint32_t n_members = 0;
3040 
3041 		fprintf(f, "Group %u = [", group_id);
3042 
3043 		/* Non-empty group. */
3044 		if (group)
3045 			TAILQ_FOREACH(m, &group->members, node) {
3046 				fprintf(f, "%u:%u ", m->member_id, m->member_weight);
3047 				n_members++;
3048 			}
3049 
3050 		/* Empty group. */
3051 		if (!n_members)
3052 			fprintf(f, "0:1 ");
3053 
3054 		fprintf(f, "]\n");
3055 	}
3056 
3057 	return 0;
3058 }
3059