Skip to content

Commit

Permalink
P4TC - Support add_on_miss, add entry externs (#4522)
Browse files Browse the repository at this point in the history
* Implemented add_entry extern

* Support type for action parameters

* Implement add_on_miss, added error checks and Addressed comments

* Added error testcases for add_entry
Fix serialization of output files

* Addressed comments

* Update testcases output

* Address compile time issue.

* Addressed comments

* Addressed comments

* Address compiler issue
  • Loading branch information
komaljai committed Mar 20, 2024
1 parent 87f8353 commit 71d79c8
Show file tree
Hide file tree
Showing 31 changed files with 2,408 additions and 14 deletions.
3 changes: 1 addition & 2 deletions backends/ebpf/psa/ebpfPsaTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,6 @@ class EBPFTablePSA : public EBPFTable {
void emitKeysAndValues(CodeBuilder *builder, EntriesGroup_t &sameMaskEntries,
std::vector<cstring> &keyNames, std::vector<cstring> &valueNames);

const IR::PathExpression *getActionNameExpression(const IR::Expression *expr) const;

public:
// We use vectors to keep an order of Direct Meters or Counters from a P4 program.
// This order is important from CLI tool point of view.
Expand All @@ -95,6 +93,7 @@ class EBPFTablePSA : public EBPFTable {
void emitCacheInstance(CodeBuilder *builder);
void emitCacheLookup(CodeBuilder *builder, cstring key, cstring value) override;
void emitCacheUpdate(CodeBuilder *builder, cstring key, cstring value) override;
const IR::PathExpression *getActionNameExpression(const IR::Expression *expr) const;
bool cacheEnabled() override { return tableCacheEnabled; }

EBPFCounterPSA *getDirectCounter(cstring name) const {
Expand Down
19 changes: 14 additions & 5 deletions backends/tc/backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,14 @@ bool Backend::ebpfCodeGen(P4::ReferenceMap *refMapEBPF, P4::TypeMap *typeMapEBPF

void Backend::serialize() const {
cstring progName = tcIR->getPipelineName();
if (ebpf_program == nullptr) return;
EBPF::CodeBuilder c(target), p(target), h(target);
ebpf_program->emit(&c);
ebpf_program->emitParser(&p);
ebpf_program->emitHeader(&h);
if (::errorCount() > 0) {
return;
}
cstring outputFile = progName + ".template";
if (!options.outputFolder.isNullOrEmpty()) {
outputFile = options.outputFolder + outputFile;
Expand Down Expand Up @@ -154,11 +162,6 @@ void Backend::serialize() const {
::error("Unable to open File %1%", headerFile);
return;
}
if (ebpf_program == nullptr) return;
EBPF::CodeBuilder c(target), p(target), h(target);
ebpf_program->emit(&c);
ebpf_program->emitParser(&p);
ebpf_program->emitHeader(&h);
*cstream << c.toString();
*pstream << p.toString();
*hstream << h.toString();
Expand Down Expand Up @@ -271,6 +274,11 @@ void ConvertToBackendIR::postorder(const IR::P4Action *action) {
}
}

void ConvertToBackendIR::updateTimerProfiles(IR::TCTable *tabledef) {
if (options.timerProfiles > DEFAULT_TIMER_PROFILES) {
tabledef->addTimerProfiles(options.timerProfiles);
}
}
void ConvertToBackendIR::updateConstEntries(const IR::P4Table *t, IR::TCTable *tabledef) {
// Check if there are const entries.
auto entriesList = t->getEntries();
Expand Down Expand Up @@ -562,6 +570,7 @@ void ConvertToBackendIR::postorder(const IR::P4Table *t) {
updateDefaultMissAction(t, tableDefinition);
updateMatchType(t, tableDefinition);
updateConstEntries(t, tableDefinition);
updateTimerProfiles(tableDefinition);
tcPipeline->addTableDefinition(tableDefinition);
}
}
Expand Down
8 changes: 8 additions & 0 deletions backends/tc/backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class ConvertToBackendIR : public Inspector {
void updateDefaultMissAction(const IR::P4Table *t, IR::TCTable *tdef);
void updateConstEntries(const IR::P4Table *t, IR::TCTable *tdef);
void updateMatchType(const IR::P4Table *t, IR::TCTable *tabledef);
void updateTimerProfiles(IR::TCTable *tabledef);
bool isPnaParserMeta(const IR::Member *mem);
bool isPnaMainInputMeta(const IR::Member *mem);
bool isPnaMainOutputMeta(const IR::Member *mem);
Expand All @@ -88,6 +89,13 @@ class ConvertToBackendIR : public Inspector {
unsigned getActionId(cstring actionName) const;
unsigned getTableKeysize(unsigned tableId) const;
cstring externalName(const IR::IDeclaration *declaration) const;
void updateAddOnMissTable(cstring tblname) const {
for (auto table : tcPipeline->tableDefs) {
if (table->tableName == tblname) {
((IR::TCTable *)table)->setTableAddOnMiss();
}
}
}
};

class Extern {
Expand Down
155 changes: 153 additions & 2 deletions backends/tc/ebpfCodeGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1452,6 +1452,58 @@ cstring ControlBodyTranslatorPNA::getParamName(const IR::PathExpression *expr) {
return expr->path->name.name;
}

bool ControlBodyTranslatorPNA::IsTableAddOnMiss(const IR::P4Table *table) {
auto property = table->getBooleanProperty("add_on_miss");
if (property && property->value) {
return true;
}
return false;
}

const IR::P4Action *ControlBodyTranslatorPNA::GetAddOnMissHitAction(cstring actionName) {
for (auto a : table->actionList->actionList) {
auto adecl = control->program->refMap->getDeclaration(a->getPath(), true);
auto action = adecl->getNode()->to<IR::P4Action>();
if (action->name.originalName == actionName) {
auto annotations = a->getAnnotations();
if (annotations && annotations->getSingle("defaultonly")) {
::error(ErrorType::ERR_UNEXPECTED,
"add_entry hit action %1% cannot be annotated with defaultonly.",
actionName);
}
return action;
}
}
::error(ErrorType::ERR_UNEXPECTED,
"add_entry extern can only be applied for one of the hit action of table "
"%1%. %2% is not hit action of table.",
table->instanceName, actionName);
return nullptr;
}

void ControlBodyTranslatorPNA::ValidateAddOnMissMissAction(const IR::P4Action *act) {
if (!act) {
::error(ErrorType::ERR_UNEXPECTED, "%1% add_entry extern can only be used in an action",
act);
}
const IR::P4Table *t = table->table->container;
cstring tblname = t->name.originalName;
const IR::Expression *defaultAction = t->getDefaultAction();
CHECK_NULL(defaultAction);
auto defaultActionName = table->getActionNameExpression(defaultAction);
CHECK_NULL(defaultActionName);
if (defaultActionName->path->name.originalName != act->name.originalName) {
::error(ErrorType::ERR_UNEXPECTED,
"add_entry extern can only be applied in default action of the table.");
}
if (!IsTableAddOnMiss(t)) {
::warning(ErrorType::WARN_MISSING,
"add_entry extern can only be used in an action"
" of a table with property add_on_miss equals to true.");
}
tcIR->updateAddOnMissTable(tblname);
}

void ControlBodyTranslatorPNA::processFunction(const P4::ExternFunction *function) {
if (function->expr->method->toString() == "send_to_port" ||
function->expr->method->toString() == "drop_packet") {
Expand Down Expand Up @@ -1497,10 +1549,109 @@ void ControlBodyTranslatorPNA::processFunction(const P4::ExternFunction *functio
builder->emitIndent();
builder->appendLine("};");
builder->emitIndent();
builder->appendLine(
"bpf_p4tc_entry_update(skb, &update_params, &key, sizeof(key), act_bpf);");
builder->append(
"bpf_p4tc_entry_update(skb, &update_params, &key, sizeof(key), act_bpf)");
}
return;
} else if (function->expr->method->toString() == "add_entry") {
/*
add_entry() to be called with the following restrictions:
* Only from within an action
* Only if the action is a default action of a table with property add_on_miss equal to true.
* Only with an action name that is one of the hit actions of that same table. This action
has parameters that are all directionless.
* The type T is a struct containing one member for each directionless parameter of the hit
action to be added. The member names must match the hit action parameter names, and their
types must be the same as the corresponding hit action parameters.
*/
auto act = findContext<IR::P4Action>();
ValidateAddOnMissMissAction(act);
BUG_CHECK(function->expr->arguments->size() == 3,
"%1%: expected 3 arguments in add_entry extern", function);

auto action = function->expr->arguments->at(0);
auto actionName = action->expression->to<IR::StringLiteral>()->value;
auto param = function->expr->arguments->at(1)->expression;
auto expire_time_profile_id = function->expr->arguments->at(2);
auto controlName = control->controlBlock->getName().originalName;

if (auto action = GetAddOnMissHitAction(actionName)) {
builder->emitIndent();
builder->appendLine("struct p4tc_table_entry_act_bpf update_act_bpf = {};");

if (param->is<IR::StructExpression>()) {
auto paramList = action->getParameters();
auto components = param->to<IR::StructExpression>()->components;
if (paramList->parameters.size() != components.size()) {
::error(ErrorType::ERR_UNEXPECTED,
"Action params in add_entry should be same as no of action "
"parameters. %1%",
action);
}
if (paramList->parameters.size() != 0) {
builder->emitIndent();
builder->appendFormat(
"struct %s *update_act_bpf_val = (struct %s*) &update_act_bpf;",
table->valueTypeName.c_str(), table->valueTypeName.c_str());
builder->newline();
cstring actionExtName = EBPF::EBPFObject::externalName(action);
// Assign Variables
for (size_t index = 0; index < components.size(); index++) {
auto param = paramList->getParameter(index);
if (param->direction != IR::Direction::None) {
::error(ErrorType::ERR_UNEXPECTED,
"Parameters of action called from add_entry should be "
"directionless. %1%",
actionName);
}
builder->emitIndent();
builder->appendFormat("update_act_bpf_val->u.%s.%s = ", actionExtName,
param->toString());
visit(components.at(index)->expression);
builder->endOfStatement();
builder->newline();
}
builder->emitIndent();
builder->appendFormat("update_act_bpf_val->action = %s;",
table->p4ActionToActionIDName(action));
builder->newline();
} else {
builder->emitIndent();
builder->appendFormat("update_act_bpf.act_id = %s;",
table->p4ActionToActionIDName(action));
builder->newline();
}

} else {
::error(ErrorType::ERR_UNEXPECTED,
"action parameters of add_entry extern should be a structure only. %1%",
param);
}
}
builder->newline();
builder->emitIndent();
builder->appendLine("/* construct key */");
builder->emitIndent();
builder->appendLine("struct p4tc_table_entry_create_bpf_params__local update_params = {");
builder->emitIndent();
builder->appendLine(" .pipeid = p4tc_filter_fields.pipeid,");
builder->emitIndent();
auto tableName = table->instanceName.substr(controlName.size() + 1);
auto tblId = tcIR->getTableId(tableName);
BUG_CHECK(tblId != 0, "Table ID not found");
builder->appendFormat(" .tblid = %d,", tblId);
builder->newline();
builder->emitIndent();
builder->append(" .profile_id = ");
visit(expire_time_profile_id);
builder->newline();
builder->emitIndent();
builder->appendLine("};");
builder->emitIndent();
builder->append(
"bpf_p4tc_entry_create_on_miss(skb, &update_params, &key, sizeof(key), "
"&update_act_bpf)");
return;
}
processCustomExternFunction(function, EBPF::EBPFTypeFactory::instance);
}
Expand Down
3 changes: 3 additions & 0 deletions backends/tc/ebpfCodeGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,9 @@ class ControlBodyTranslatorPNA : public EBPF::ControlBodyTranslator {
bool preorder(const IR::AssignmentStatement *a) override;
void processMethod(const P4::ExternMethod *method) override;
bool preorder(const IR::Member *) override;
bool IsTableAddOnMiss(const IR::P4Table *table);
const IR::P4Action *GetAddOnMissHitAction(cstring actionName);
void ValidateAddOnMissMissAction(const IR::P4Action *act);
};

// Similar to class ActionTranslationVisitorPSA in backends/ebpf/psa/ebpfPsaControl.h
Expand Down
8 changes: 8 additions & 0 deletions backends/tc/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class TCOptions : public CompilerOptions {
bool emitTraceMessages = false;
// XDP2TC mode for PSA-eBPF
enum XDP2TC xdp2tcMode = XDP2TC_META;
unsigned timerProfiles = 4;

TCOptions() {
registerOption(
Expand Down Expand Up @@ -69,6 +70,13 @@ class TCOptions : public CompilerOptions {
},
"Select the mode used to pass metadata from XDP to TC "
"(possible values: meta, head, cpumap).");
registerOption(
"--num-timer-profiles", "profiles",
[this](const char *arg) {
timerProfiles = std::atoi(arg);
return true;
},
"Defines the number of timer profiles. Default is 4.");
}
};

Expand Down
2 changes: 1 addition & 1 deletion backends/tc/runtime/pna.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ struct __attribute__((__packed__)) p4tc_table_entry_act_bpf {
struct p4tc_table_entry_create_bpf_params__local {
u32 pipeid;
u32 tblid;
u64 aging_ms;
u32 profile_id;
};
extern struct p4tc_table_entry_act_bpf *bpf_p4tc_tbl_read(
struct __sk_buff *skb, struct p4tc_table_entry_act_bpf_params__local *params, void *key,
Expand Down
4 changes: 2 additions & 2 deletions backends/tc/tc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ int main(int argc, char *const argv[]) {
}
TC::Backend backend(toplevel, &midEnd.refMap, &midEnd.typeMap, options);
if (!backend.process()) return 1;

cstring progName = backend.tcIR->getPipelineName();
cstring introspecFile = progName + ".json";
if (!options.outputFolder.isNullOrEmpty()) {
Expand All @@ -108,9 +107,10 @@ int main(int argc, char *const argv[]) {
return 1;
}
}
backend.serialize();
if (::errorCount() > 0) {
std::remove(introspecFile);
return 1;
}
backend.serialize();
return ::errorCount() > 0;
}
17 changes: 17 additions & 0 deletions backends/tc/tc.def
Original file line number Diff line number Diff line change
Expand Up @@ -232,11 +232,14 @@ class TCTable {
unsigned tableEntriesCount;
unsigned numMask;
unsigned matchType;
unsigned timerProfiles;
unsigned defaultTimerProfiles = 4;
TCAction defaultHitAction;
bool isDefaultHitConst;
TCAction defaultMissAction;
optional safe_vector<TCDefaultActionParam> defaultMissActionParams;
bool isDefaultMissConst;
bool isTableAddOnMiss;
bool isTcMayOverride;
ordered_map<TCAction, unsigned> actionList;
safe_vector<TCEntry> const_entries;
Expand Down Expand Up @@ -265,6 +268,9 @@ class TCTable {
void setDefaultMissConst(bool i) {
isDefaultMissConst = i;
}
void setTableAddOnMiss() {
isTableAddOnMiss = true;
}
void setTcMayOverride() {
isTcMayOverride = true;
}
Expand All @@ -274,6 +280,9 @@ class TCTable {
void addConstEntries(TCEntry entry) {
const_entries.push_back(entry);
}
void addTimerProfiles(unsigned tp) {
timerProfiles = tp;
}
cstring getTableName() const {
return tableName;
}
Expand All @@ -298,13 +307,15 @@ class TCTable {
controlName = cN;
pipelineName = pN;
keySize = 0;
timerProfiles = 0;
tableEntriesCount = TC::DEFAULT_TABLE_ENTRIES;
numMask = TC::DEFAULT_KEY_MASK;
matchType = TC::EXACT_TYPE;
defaultHitAction = nullptr;
defaultMissAction = nullptr;
isDefaultHitConst = false;
isDefaultMissConst = false;
isTableAddOnMiss = false;
isTcMayOverride = false;
}
toString {
Expand All @@ -316,6 +327,12 @@ class TCTable {
tcTable += "\n\tkeysz " + Util::toString(keySize);
tcTable += " nummasks " + Util::toString(numMask);
tcTable += " tentries " + Util::toString(tableEntriesCount);
if (isTableAddOnMiss) {
tcTable += " permissions 0x3DE6";
if(timerProfiles > defaultTimerProfiles) {
tcTable += " num_timer_profiles " + Util::toString(timerProfiles);
}
}

if (!actionList.empty()) {
tcTable += " \\";
Expand Down
1 change: 1 addition & 0 deletions backends/tc/tc_defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ inline constexpr auto PORTID_BITWIDTH = 32;
inline constexpr auto DEFAULT_KEY_ID = 1;
inline constexpr auto DEFAULT_METADATA_ID = 1;
inline constexpr auto BITWIDTH = 32;
inline constexpr auto DEFAULT_TIMER_PROFILES = 4;

// Supported data types.
inline constexpr auto BIT_TYPE = 0;
Expand Down
Loading

0 comments on commit 71d79c8

Please sign in to comment.