Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for ternary match kind to eBPF backend #3256

Merged
merged 5 commits into from
Apr 28, 2022

Conversation

osinstom
Copy link
Contributor

Co-authored-by: Mateusz Kossakowski mateusz.kossakowski@orange.com
Co-authored-by: Jan Palimąka jan.palimaka@orange.com

osinstom and others added 4 commits April 27, 2022 12:43

An `lpm` table is implemented using the BPF `LPM_TRIE` map. A P4 table is considered an `lpm` table if it contains a single `lpm` field and no `ternary` fields.
The PSA-eBPF compiler generates a BPF `LPM_TRIE` map instance for each P4 table instance. The hash map key as a concatenation of P4 match fields translated to eBPF representation.
Moreover, the PSA-eBPF compiler shuffles the match fields and places the `lpm` field in the last position. Each `apply()` operation is translated into a lookup to the `LPM_TRIE` map.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may be a problem since the control-plane has to know this. For example, if you plan p4Runtime support, I am not sure it will be easy. (Perhaps it is, just shuffle the data from the control-plane in the same way).
Wouldn't it be simpler to just require users to put the field last and reject other programs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shuffling the data from control plane shouldn't be a problem, since we have P4Info. IDK we might want to revisit that later (once we start integrating PSA-eBPF with a P4Runtime stack), but I'd keep it as is, for now.

- the `<TBL-NAME>_prefixes` map is a BPF hash map that stores all unique ternary masks. The ternary masks are created based on the runtime table entries that are installed by a user.
- the `<TBL-NAME>_tuples_map` map is a BPF array map of maps that stores all "tuples". A single tuple is a BPF hash map that stores all flow rules with the same ternary mask.

Note that the `psabpf-ctl table add` CLI command greatly simplifies the process of adding/removing flow rules to ternary tables.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume that this provides the illusion of a ternary table to the control-plane. This will be a consideration if you do p4Runtime too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically, psabpf provides not only a CLI tool, but also a C library that we plan to use, when it comes to the P4Runtime integration. So, the psabpf C API hides the underlying complexity.

For each `apply()` operation, the PSA-eBPF compiler generates the piece of code performing lookup to the above maps. The lookup code iterates over the `<TBL-NAME>_prefixes` map to
retrieve a ternary mask. Next, the lookup key (a concatenation of match keys) is masked with the obtained ternary mask and lookup to a corresponding tuple map is performed.
If a match is found, the best match with the highest priority is saved, and the algorithm continues to examine other tuples. If an entry with a higher priority is found in other tuples,
the best match is overwritten. The algorithm exists when there is no more tuples to examine left.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no more tuples are left.


For each `apply()` operation, the PSA-eBPF compiler generates the piece of code performing lookup to the above maps. The lookup code iterates over the `<TBL-NAME>_prefixes` map to
retrieve a ternary mask. Next, the lookup key (a concatenation of match keys) is masked with the obtained ternary mask and lookup to a corresponding tuple map is performed.
If a match is found, the best match with the highest priority is saved, and the algorithm continues to examine other tuples. If an entry with a higher priority is found in other tuples,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove "in other tuples"?

If a match is found, the best match with the highest priority is saved, and the algorithm continues to examine other tuples. If an entry with a higher priority is found in other tuples,
the best match is overwritten. The algorithm exists when there is no more tuples to examine left.

The snippet below shows the example of C code generated by the PSA-eBPF compiler for a lookup into a ternary table. The steps are explained below.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove "example of"

if (val && val->has_next != 0) {
struct ingress_tbl_ternary_1_key_mask next = val->next_tuple_mask;
#pragma clang loop unroll(disable)
for (int i = 0; i < MAX_INGRESS_TBL_TERNARY_1_KEY_MASKS; i++) { // (1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works because the upper bound is a constant?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, exactly.

4. Next, a lookup to the inner BPF map (a tuple map) is performed. The returned value stores the action ID, action params and priority.
5. The priority of an obtained value is compared with a current "best match" entry. An entry that is returned from the ternary classification is the one with the highest priority among different tuples.

Note that the TSS algorithm has linear O(n) packet classification complexity, but the lookup cost depends on the number of unique ternary masks and does not depend on the number of total entries.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just say what n is.

# flow rules for 'tbl_ternary_0'
# 1. ipv4.srcAddr=1.2.3.4/0xffffff00 => action 0 priority 1
# 2. ipv4.srcAddr=1.2.3.4/0xffff00ff => action 1 priority 10
self.table_add(table="ingress_tbl_ternary_0", keys=["1.2.3.4^0xffffff00"], action=0, priority=1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So ^ is the mask operator?
Would it make sense to use &&& to resemble P4 more?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the problem is that &&& collides with the bash "&". Since psabpf-ctl is primarily a CLI tool (the Python functions just wrap it), we found it better to use "^" as the mask operator.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this documented someplace visible?

@osinstom
Copy link
Contributor Author

@mbudiu-vmw I've addressed your comments

@mihaibudiu mihaibudiu merged commit d5546b1 into p4lang:main Apr 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants