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

Implement PSA for eBPF backend #3139

Merged
merged 14 commits into from
Mar 31, 2022
Merged

Implement PSA for eBPF backend #3139

merged 14 commits into from
Mar 31, 2022

Conversation

osinstom
Copy link
Contributor

This PR implements the PSA model for eBPF backend. Please read the included README to learn about the design and implementation details. This PR adds the PSA model that leverages the eBFP/TC hook. In subsequent PRs we're planning to add PTF-based test cases, support for XDP-based design, support for all PSA externs and ternary matching.

The basic usage model:

$ p4c-ebpf --arch psa --target kernel -o out.c program.p4

Note that current code contains many placeholders that are not used now, but they will make adding support for PSA externs easier.

This is a big milestone for the team that have been working on this project for over 1 year. Thanks to main developers @kmateuszssak @tatry and other folks that contributed to this project: @fdangtran and @elfadel.

Tomasz Osinski and others added 2 commits March 18, 2022 20:26
Co-authored-by: Mateusz Kossakowski <[email protected]>
Co-authored-by: Jan Palimąka <[email protected]>
@osinstom
Copy link
Contributor Author

@mbudiu-vmw @rst0git I've synced with master and minimized build dependencies.

@mihaibudiu
Copy link
Contributor

So I guess this is now ready for review?

@osinstom
Copy link
Contributor Author

So I guess this is now ready for review?

I think so

protected:
const EBPFControl* control;
std::set<const IR::Parameter*> toDereference;
std::vector<cstring> saveAction;
P4::P4CoreLibrary& p4lib;

int commentDescriptionDepth;
Copy link
Contributor

Choose a reason for hiding this comment

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

you should document this variable.

// eBPF can pass 64 bits of data as one argument passed in 64 bit register,
// so value of the field is printed only when it fits into that register
// eBPF can pass 64 bits of data as one argument, so value of the field is
// printed only when its fits into register
Copy link
Contributor

Choose a reason for hiding this comment

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

it fits

backends/ebpf/ebpfParser.cpp Show resolved Hide resolved
backends/ebpf/ebpfParser.cpp Outdated Show resolved Hide resolved
backends/ebpf/ebpfTable.h Outdated Show resolved Hide resolved
backends/ebpf/psa/ebpfPsaArch.cpp Outdated Show resolved Hide resolved
/*
* 6. BPF map initialization
*/
emitInitializer(builder);
Copy link
Contributor

Choose a reason for hiding this comment

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

interesting, I expected that this is done at runtime. I wonder how bpf does it, since maps do not exist until the program is started.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you mean "loaded" by "started"? This initialization function is actually called by psabpf pipeline load. I'll add a section to documentation about this.


parser = new EBPFPsaParser(program, prsr, typemap);

auto it = pl->parameters.begin();
Copy link
Contributor

Choose a reason for hiding this comment

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

check?

}

// use 1024 by default
size_t size = 1024;
Copy link
Contributor

Choose a reason for hiding this comment

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

hidden like this it will be hard to find and change. Perhaps it should also be a compiler option.


bool ConvertToEBPFControlPSA::preorder(const IR::AssignmentStatement *a) {
// the condition covers both ingress and egress timestamp
if (a->right->toString().endsWith("timestamp")) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is somewhat ugly.

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, I agree it is. The goal is to find out if ingress_timestamp or egress_timestamp is used in a control block. We haven't found a better way to do this so far.

@mihaibudiu
Copy link
Contributor

Please make any changes as new commits, I can't read this much code again.

@osinstom
Copy link
Contributor Author

@mbudiu-vmw Thanks for review! I believe I've address all of your comments.


bool ConvertToEBPFControlPSA::preorder(const IR::AssignmentStatement *a) {
// the condition covers both ingress and egress timestamp
if (a->right->toString().endsWith("timestamp")) {
Copy link
Contributor

Choose a reason for hiding this comment

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

A more robust way is to look for a Member named "timestamp" from a type that is like the metadata. Or at least a member...

CE2E is implemented by invoking `bpf_clone_redirect()` helper in the Egress path. Output ports are determined based on the
`clone_session_id` and lookup to "clone_session" BPF map, which is shared among TC ingress and egress (eBPF subsystem allows for map sharing between programs).

## Sending packet to CPU
Copy link
Member

@rst0git rst0git Mar 26, 2022

Choose a reason for hiding this comment

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

The subtitle "Sending packet to CPU" is slightly confusing in the context of eBPF because everything is running on CPU. Perhaps it would be more appropriate to change the subtitle to "Sending packet to Control Plane" or "Sending packet to Userspace"?

@jafingerhut What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

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

The current PSA specification might be contributing to some confusion here, since that spec does refer to a "CPU port" to send packets from the data plane to the controller. That specification does have at least several places with a kind of built-in assumption that the P4 data plane is separate from a nearby control CPU, at least in how it names things.

I'm not sure if there are only a few small surgical changes in the PSA spec that would match your suggestion here, but unfortunately if we want to avoid changing names in the P4 include file for the architecture, there are already a few names like these in there (https://github.com/p4lang/p4c/blob/main/p4include/bmv2/psa.p4):

const PortId_t PSA_PORT_CPU = (PortId_t) 0xfffffffd;

const CloneSessionId_t PSA_CLONE_SESSION_TO_CPU = (CloneSessionId_t) 0;

@osinstom
Copy link
Contributor Author

@mbudiu-vmw I've addressed your last comments. Please resolve conversations/let me know what else I need to address.

emitCommonPreamble(builder);
builder->newline();

// TODO: enable configuring MAX_PORTS/MAX_INSTANCES/MAX_SESSIONS using compiler optios.
Copy link
Contributor

Choose a reason for hiding this comment

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

options

// the condition covers both ingress and egress timestamp
if (a->right->toString().endsWith("timestamp")) {
control->timestampIsUsed = true;
if (auto m = a->right->to<IR::Member>()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you can do even better: just use the IR::Member visitor function and check there.

@mihaibudiu
Copy link
Contributor

Will give you a chance to address the last two small comments.

@mihaibudiu mihaibudiu merged commit c794fb4 into p4lang:main Mar 31, 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.

4 participants