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 Meter extern for eBPF backend #3231

Merged
merged 2 commits into from
Apr 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion backends/ebpf/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ set (P4C_EBPF_SRCS
psa/externs/ebpfPsaChecksum.cpp
psa/externs/ebpfPsaHashAlgorithm.cpp
psa/externs/ebpfPsaTableImplementation.cpp
psa/externs/ebpfPsaRegister.cpp)
psa/externs/ebpfPsaRegister.cpp
psa/externs/ebpfPsaMeter.cpp
)

set (P4C_EBPF_HDRS
codeGen.h
Expand Down Expand Up @@ -81,6 +83,7 @@ set (P4C_EBPF_HDRS
psa/externs/ebpfPsaHashAlgorithm.h
psa/externs/ebpfPsaTableImplementation.h
psa/externs/ebpfPsaRegister.h
psa/externs/ebpfPsaMeter.h
)

add_cpplint_files(${CMAKE_CURRENT_SOURCE_DIR} "${P4C_EBPF_SRCS};${P4C_EBPF_HDRS}")
Expand Down
15 changes: 15 additions & 0 deletions backends/ebpf/psa/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,21 @@ If a deparser triggers the `pack()` method, an eBPF program inserts data defined
A user space application is responsible for performing periodic queries to this map to read a Digest message. It can use either
`psabpf-ctl digest get pipe`, `psabpf_digest_get_next` from psabpf C API or `bpf_map_lookup_and_delete_elem` from `libbpf` API.

### Meters
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you also please edit ../README to make it point to this new backend?

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 to add info about PSA-eBPF backend as another architecture? I think this is for another PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps, but the implementation is so featured now that it should be mentioned in the toplevel README.


[Meters](https://p4.org/p4-spec/docs/PSA.html#sec-meters) are a mechanism for "marking" packets that exceed an average packet or bit rate.
Meters implement Dual Token Bucket Algorithm with both "color aware" and "color blind" modes. The PSA-eBPF implementation uses a BPF hash map
to store a Meter state. The current implementation in eBPF uses BPF spinlocks to make operations on Meters atomic. The `bpf_ktime_get_ns()` helper is used to get a packet arrival timestamp.

The best way to configure a Meter is to use `psabpf-ctl meter` tool as in the following example:
```bash
# 1Mb/s -> 128 000 bytes/s (132 kbytes/s PIR, 128 kbytes/s CIR), let CBS, PBS -> 10 kbytes
$ psabpf-ctl meter update pipe "$PIPELINE" DemoIngress_meter index 0 132000:10000 128000:10000
```

`psabpf-ctl` accepts PIR and CIR values in bytes/s units or packets/s. PBS and CBS in bytes or packets.


# Getting started

## Installation
Expand Down
5 changes: 4 additions & 1 deletion backends/ebpf/psa/ebpfPipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ class EBPFPipeline : public EBPFProgram {
void emitHeadersFromCPUMAP(CodeBuilder* builder);
void emitMetadataFromCPUMAP(CodeBuilder *builder);

bool hasAnyMeter() const {
return !control->meters.empty();
}
/*
* Returns whether the compiler should generate
* timestamp retrieved by bpf_ktime_get_ns().
Expand All @@ -126,7 +129,7 @@ class EBPFPipeline : public EBPFProgram {
* if the timestamp field is not used within a pipeline.
*/
bool shouldEmitTimestamp() const {
return control->timestampIsUsed;
return hasAnyMeter() || control->timestampIsUsed;
}
};

Expand Down
18 changes: 18 additions & 0 deletions backends/ebpf/psa/ebpfPsaControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ bool ControlBodyTranslatorPSA::preorder(const IR::AssignmentStatement* a) {
// Then the hash value is stored in a registerVar variable.
hash->calculateHash(builder, ext->expr, this);
builder->emitIndent();
} else if (ext->originalExternType->name.name == "Meter") {
// It is just for trace message before meter execution
cstring name = EBPFObject::externalName(ext->object);
auto msgStr = Util::printf_format("Executing meter: %s", name);
builder->target->emitTraceMessage(builder, msgStr.c_str());
}
}

Expand All @@ -61,6 +66,10 @@ void ControlBodyTranslatorPSA::processMethod(const P4::ExternMethod* method) {
auto counterMap = control->getCounter(name);
counterMap->to<EBPFCounterPSA>()->emitMethodInvocation(builder, method, this);
return;
} else if (declType->name.name == "Meter") {
auto meter = control->to<EBPFControlPSA>()->getMeter(name);
meter->emitExecute(builder, method, this);
return;
} else if (declType->name.name == "Hash") {
auto hash = control->to<EBPFControlPSA>()->getHash(name);
hash->processMethod(builder, method->method->name.name, method->expr, this);
Expand Down Expand Up @@ -95,6 +104,13 @@ void EBPFControlPSA::emitTableTypes(CodeBuilder *builder) {

for (auto it : registers)
it.second->emitTypes(builder);
for (auto it : meters)
it.second->emitKeyType(builder);

// Value type for any indirect meter is the same
if (!meters.empty()) {
meters.begin()->second->emitValueType(builder);
}
}

void EBPFControlPSA::emitTableInstances(CodeBuilder* builder) {
Expand All @@ -104,6 +120,8 @@ void EBPFControlPSA::emitTableInstances(CodeBuilder* builder) {
it.second->emitInstance(builder);
for (auto it : registers)
it.second->emitInstance(builder);
for (auto it : meters)
it.second->emitInstance(builder);
}

void EBPFControlPSA::emitTableInitializers(CodeBuilder* builder) {
Expand Down
7 changes: 7 additions & 0 deletions backends/ebpf/psa/ebpfPsaControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class EBPFControlPSA : public EBPFControl {

std::map<cstring, EBPFHashPSA*> hashes;
std::map<cstring, EBPFRegisterPSA*> registers;
std::map<cstring, EBPFMeterPSA*> meters;

EBPFControlPSA(const EBPFProgram* program, const IR::ControlBlock* control,
const IR::Parameter* parserHeaders) :
Expand All @@ -86,6 +87,12 @@ class EBPFControlPSA : public EBPFControl {
BUG_CHECK(result != nullptr, "No hash named %1%", name);
return result;
}

EBPFMeterPSA* getMeter(cstring name) const {
auto result = ::get(meters, name);
BUG_CHECK(result != nullptr, "No meter named %1%", name);
return result;
}
};

} // namespace EBPF
Expand Down
14 changes: 14 additions & 0 deletions backends/ebpf/psa/ebpfPsaGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ limitations under the License.
#include "externs/ebpfPsaCounter.h"
#include "externs/ebpfPsaHashAlgorithm.h"
#include "externs/ebpfPsaTableImplementation.h"
#include "externs/ebpfPsaMeter.h"

namespace EBPF {

Expand Down Expand Up @@ -92,6 +93,9 @@ void PSAEbpfGenerator::emitTypes(CodeBuilder *builder) const {
type->emit(builder);
}

if (ingress->hasAnyMeter() || egress->hasAnyMeter())
EBPFMeterPSA::emitValueStruct(builder, ingress->refMap);

ingress->parser->emitTypes(builder);
ingress->control->emitTableTypes(builder);
egress->parser->emitTypes(builder);
Expand Down Expand Up @@ -267,6 +271,13 @@ void PSAEbpfGenerator::emitHelperFunctions(CodeBuilder *builder) const {

builder->appendLine(pktClonesFunc);
builder->newline();

if (ingress->hasAnyMeter() || egress->hasAnyMeter()) {
cstring meterExecuteFunc =
EBPFMeterPSA::meterExecuteFunc(options.emitTraceMessages, ingress->refMap);
builder->appendLine(meterExecuteFunc);
builder->newline();
}
}

// =====================PSAArchTC=============================
Expand Down Expand Up @@ -646,6 +657,9 @@ bool ConvertToEBPFControlPSA::preorder(const IR::ExternBlock* instance) {
} else if (typeName == "Hash") {
auto hash = new EBPFHashPSA(program, di, name);
control->hashes.emplace(name, hash);
} else if (typeName == "Meter") {
auto met = new EBPFMeterPSA(program, name, di, control->codeGen);
control->meters.emplace(name, met);
} else {
::error(ErrorType::ERR_UNEXPECTED, "Unexpected block %s nested within control",
instance);
Expand Down
1 change: 1 addition & 0 deletions backends/ebpf/psa/ebpfPsaTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ limitations under the License.
#include "frontends/p4/methodInstance.h"
#include "backends/ebpf/ebpfTable.h"
#include "backends/ebpf/psa/externs/ebpfPsaCounter.h"
#include "backends/ebpf/psa/externs/ebpfPsaMeter.h"

namespace EBPF {

Expand Down
7 changes: 2 additions & 5 deletions backends/ebpf/psa/externs/ebpfPsaCounter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,11 +200,8 @@ void EBPFCounterPSA::emitDirectMethodInvocation(CodeBuilder* builder,

cstring target = valuePtr + "->" + instanceName;
auto pipeline = dynamic_cast<const EBPFPipeline *>(program);
if (pipeline == nullptr) {
::error(ErrorType::ERR_UNSUPPORTED,
"DirectCounter used outside of pipeline %1%", method->expr);
return;
}
CHECK_NULL(pipeline);

cstring msgStr = Util::printf_format("Counter: updating %s, packets=1, bytes=%%u",
instanceName.c_str());
cstring varStr = Util::printf_format("%s", pipeline->lengthVar.c_str());
Expand Down
Loading