Skip to content

Commit

Permalink
Implementation of InternetChecksum in P4TC
Browse files Browse the repository at this point in the history
  • Loading branch information
komaljai committed Jul 2, 2024
1 parent 061cb5a commit e97ec29
Show file tree
Hide file tree
Showing 58 changed files with 1,563 additions and 381 deletions.
39 changes: 39 additions & 0 deletions backends/tc/ebpfCodeGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,20 @@ void EBPFPnaParser::emitRejectState(EBPF::CodeBuilder *builder) {
builder->endOfStatement(true);
}

void EBPFPnaParser::emitDeclaration(EBPF::CodeBuilder *builder, const IR::Declaration *decl) {
if (auto di = decl->to<IR::Declaration_Instance>()) {
cstring name = di->name.name;
if (EBPFObject::getTypeName(di) == "InternetChecksum") {
auto instance = new EBPFInternetChecksumPNA(program, di, name);
checksums.emplace(name, instance);
instance->emitVariables(builder);
return;
}
}

EBPFParser::emitDeclaration(builder, decl);
}

// This code is similar to compileExtractField function in PsaStateTranslationVisitor.
// Handled TC "macaddr" annotation.
void PnaStateTranslationVisitor::compileExtractField(const IR::Expression *expr,
Expand Down Expand Up @@ -1223,6 +1237,21 @@ void IngressDeparserPNA::emit(EBPF::CodeBuilder *builder) {
builder->newline();
}

void IngressDeparserPNA::emitDeclaration(EBPF::CodeBuilder *builder, const IR::Declaration *decl) {
if (auto di = decl->to<IR::Declaration_Instance>()) {
cstring name = di->name.name;

if (EBPF::EBPFObject::getTypeName(di) == "InternetChecksum") {
auto instance = new EBPFInternetChecksumPNA(program, di, name);
checksums.emplace(name, instance);
instance->emitVariables(builder);
return;
}
}

EBPFDeparser::emitDeclaration(builder, decl);
}

// =====================ConvertToEbpfPNA=============================
const PNAEbpfGenerator *ConvertToEbpfPNA::build(const IR::ToplevelBlock *tlb) {
/*
Expand Down Expand Up @@ -2277,6 +2306,16 @@ void DeparserHdrEmitTranslatorPNA::emitField(EBPF::CodeBuilder *builder, cstring
builder->newline();
}

EBPF::EBPFHashAlgorithmPSA *EBPFHashAlgorithmTypeFactoryPNA::create(
int type, const EBPF::EBPFProgram *program, cstring name) {
if (type == EBPF::EBPFHashAlgorithmPSA::HashAlgorithm::ONES_COMPLEMENT16 ||
type == EBPF::EBPFHashAlgorithmPSA::HashAlgorithm::TARGET_DEFAULT) {
return new InternetChecksumAlgorithmPNA(program, name);
}

return nullptr;
}

void CRCChecksumAlgorithmPNA::emitUpdateMethod(EBPF::CodeBuilder *builder, int crcWidth) {
// Note that this update method is optimized for our CRC16 and CRC32, custom
// version may require other method of update. When data_size <= 64 bits,
Expand Down
6 changes: 5 additions & 1 deletion backends/tc/ebpfCodeGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ class EBPFPnaParser : public EBPF::EBPFPsaParser {
const P4::TypeMap *typeMap);
void emit(EBPF::CodeBuilder *builder) override;
void emitRejectState(EBPF::CodeBuilder *) override;
void emitDeclaration(EBPF::CodeBuilder *builder, const IR::Declaration *decl);

DECLARE_TYPEINFO(EBPFPnaParser, EBPF::EBPFPsaParser);
};
Expand Down Expand Up @@ -188,6 +189,7 @@ class IngressDeparserPNA : public EBPF::EBPFDeparserPSA {
bool build() override;
void emit(EBPF::CodeBuilder *builder) override;
void emitPreDeparser(EBPF::CodeBuilder *builder) override;
void emitDeclaration(EBPF::CodeBuilder *builder, const IR::Declaration *decl);

DECLARE_TYPEINFO(IngressDeparserPNA, EBPF::EBPFDeparserPSA);
};
Expand Down Expand Up @@ -441,11 +443,13 @@ class EBPFHashAlgorithmTypeFactoryPNA : public EBPF::EBPFHashAlgorithmTypeFactor
static EBPFHashAlgorithmTypeFactoryPNA factory;
return &factory;
}

void emitGlobals(EBPF::CodeBuilder *builder) {
CRC16ChecksumAlgorithmPNA::emitGlobals(builder);
CRC32ChecksumAlgorithmPNA::emitGlobals(builder);
EBPF::InternetChecksumAlgorithm::emitGlobals(builder);
}

EBPF::EBPFHashAlgorithmPSA *create(int type, const EBPF::EBPFProgram *program, cstring name);
};

} // namespace TC
Expand Down
18 changes: 13 additions & 5 deletions backends/tc/runtime/pna.h
Original file line number Diff line number Diff line change
Expand Up @@ -278,32 +278,40 @@ bpf_p4tc_ext_csum_crc16_clear(struct p4tc_ext_csum_params *params) __ksym;
/* Basic checksums are not implemented in DPDK */
extern u32
bpf_p4tc_ext_csum_crc32_add(struct p4tc_ext_csum_params *params,
const u32 params__sz,
const void *data, const u32 data__sz) __ksym;

extern u32
bpf_p4tc_ext_csum_crc32_get(struct p4tc_ext_csum_params *params) __ksym;
bpf_p4tc_ext_csum_crc32_get(struct p4tc_ext_csum_params *params,
const u32 params__sz) __ksym;

extern void
bpf_p4tc_ext_csum_crc32_clear(struct p4tc_ext_csum_params *params) __ksym;
bpf_p4tc_ext_csum_crc32_clear(struct p4tc_ext_csum_params *params,
const u32 params__sz) __ksym;

extern u16
bpf_p4tc_ext_csum_16bit_complement_get(struct p4tc_ext_csum_params *params) __ksym;
bpf_p4tc_ext_csum_16bit_complement_get(struct p4tc_ext_csum_params *params,
const u32 params__sz) __ksym;

/* Equivalent to PNA 16bit complement checksum (incremental checksum) */
extern __wsum
bpf_p4tc_ext_csum_16bit_complement_add(struct p4tc_ext_csum_params *params,
const u32 params__sz,
const void *data, int len) __ksym;

extern int
bpf_p4tc_ext_csum_16bit_complement_sub(struct p4tc_ext_csum_params *params,
const u32 params__sz,
const void *data, const u32 data__sz) __ksym;

extern void
bpf_p4tc_ext_csum_16bit_complement_clear(struct p4tc_ext_csum_params *params) __ksym;
bpf_p4tc_ext_csum_16bit_complement_clear(struct p4tc_ext_csum_params *params,
const u32 params__sz) __ksym;

extern void
bpf_p4tc_ext_csum_16bit_complement_set_state(struct p4tc_ext_csum_params *params,
u16 csum) __ksym;
const u32 params__sz,
u16 csum) __ksym;

/* Equivalent to PNA crc16 hash */
extern u16
Expand Down
255 changes: 255 additions & 0 deletions backends/tc/tcExterns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,4 +179,259 @@ void EBPFCounterPNA::emitCount(EBPF::CodeBuilder *builder, const P4::ExternMetho
}
}

void EBPFChecksumPNA::init(const EBPF::EBPFProgram *program, cstring name, int type) {
engine = EBPFHashAlgorithmTypeFactoryPNA::instance()->create(type, program, name);

if (engine == nullptr) {
if (declaration->arguments->empty())
::error(ErrorType::ERR_UNSUPPORTED, "InternetChecksum not yet implemented");
else
::error(ErrorType::ERR_UNSUPPORTED, "Hash algorithm not yet implemented: %1%",
declaration->arguments->at(0));
}
}

void EBPFInternetChecksumPNA::processMethod(EBPF::CodeBuilder *builder, cstring method,
const IR::MethodCallExpression *expr,
Visitor *visitor) {
engine->setVisitor(visitor);
if (method == "add") {
engine->emitAddData(builder, 0, expr);
} else if (method == "subtract") {
engine->emitSubtractData(builder, 0, expr);
} else if (method == "get_state") {
engine->emitGetInternalState(builder);
} else if (method == "set_state") {
engine->emitSetInternalState(builder, expr);
} else if (method == "clear") {
engine->emitClear(builder);
} else if (method == "get") {
engine->emitGet(builder);
} else {
::error(ErrorType::ERR_UNEXPECTED, "Unexpected method call %1%", expr);
}
}

void InternetChecksumAlgorithmPNA::emitVariables(EBPF::CodeBuilder *builder,
const IR::Declaration_Instance *decl) {
(void)decl;
stateVar = program->refMap->newName(baseName + "_state");
csumVar = program->refMap->newName(baseName + "_csum");
builder->emitIndent();
builder->appendFormat("u16 %s = 0", stateVar.c_str());
builder->endOfStatement(true);
builder->appendFormat("struct p4tc_ext_csum_params %s = {};", csumVar.c_str());
builder->newline();
}

void InternetChecksumAlgorithmPNA::emitClear(EBPF::CodeBuilder *builder) {
builder->emitIndent();
builder->appendFormat("bpf_p4tc_ext_csum_16bit_complement_clear(&%s, sizeof(%s));",
csumVar.c_str(), csumVar.c_str());
builder->newline();
}

void InternetChecksumAlgorithmPNA::emitAddData(EBPF::CodeBuilder *builder,
const ArgumentsList &arguments) {
updateChecksum(builder, arguments, true);
}

void InternetChecksumAlgorithmPNA::emitGet(EBPF::CodeBuilder *builder) {
builder->appendFormat("(u16) bpf_p4tc_ext_csum_16bit_complement_get(&%s, sizeof(%s));",
csumVar.c_str(), csumVar.c_str());
builder->newline();
}

void InternetChecksumAlgorithmPNA::emitSubtractData(EBPF::CodeBuilder *builder,
const ArgumentsList &arguments) {
updateChecksum(builder, arguments, false);
}

void InternetChecksumAlgorithmPNA::emitGetInternalState(EBPF::CodeBuilder *builder) {
builder->append(stateVar);
}

void InternetChecksumAlgorithmPNA::emitSetInternalState(EBPF::CodeBuilder *builder,
const IR::MethodCallExpression *expr) {
if (expr->arguments->size() != 1) {
::error(ErrorType::ERR_UNEXPECTED, "Expected exactly 1 argument %1%", expr);
return;
}
builder->emitIndent();
builder->appendFormat("%s = ", stateVar.c_str());
visitor->visit(expr->arguments->at(0)->expression);
builder->endOfStatement(true);
builder->emitIndent();
builder->appendFormat("bpf_p4tc_ext_csum_16bit_complement_set_state(&%s, sizeof(%s), ",
csumVar.c_str(), csumVar.c_str());
visitor->visit(expr->arguments->at(0)->expression);
builder->appendLine(");");
}

void InternetChecksumAlgorithmPNA::updateChecksum(EBPF::CodeBuilder *builder,
const ArgumentsList &arguments, bool addData) {
cstring tmpVar = program->refMap->newName(baseName + "_tmp");

builder->emitIndent();
builder->blockStart();

builder->emitIndent();
builder->appendFormat("u16 %s = 0", tmpVar.c_str());
builder->endOfStatement(true);

int remainingBits = 16, bitsToRead;
for (auto field : arguments) {
auto fieldType = field->type->to<IR::Type_Bits>();
if (fieldType == nullptr) {
::error(ErrorType::ERR_UNSUPPORTED, "Unsupported field type: %1%", field->type);
return;
}
const int width = fieldType->width_bits();
bitsToRead = width;

cstring field_temp = "_temp"_cs;
cstring fieldByteOrder = "HOST"_cs;
auto tcTarget = dynamic_cast<const EBPF::P4TCTarget *>(builder->target);
fieldByteOrder = tcTarget->getByteOrder(program->typeMap, NULL, field);
if (fieldByteOrder == "NETWORK"_cs) {
const char *strToken = field->toString().findlast('.');
if (strToken != nullptr) {
cstring extract(strToken + 1);
field_temp = program->refMap->newName(extract + "_temp");
}
}
if (width > 64) {
if (remainingBits != 16) {
::error(ErrorType::ERR_UNSUPPORTED,
"%1%: field wider than 64 bits must be aligned to 16 bits in input data",
field);
continue;
}
if (width % 16 != 0) {
::error(ErrorType::ERR_UNSUPPORTED,
"%1%: field wider than 64 bits must have size in bits multiply of 16 bits",
field);
continue;
}

// Let's convert internal array into an array of u16 and calc csum for such entries.
// Byte order conversion is required, because csum is calculated in host byte order
// but data is preserved in network byte order.
const unsigned arrayEntries = width / 16;
for (unsigned i = 0; i < arrayEntries; ++i) {
builder->emitIndent();
builder->appendFormat("%s = htons(((u16 *)(", tmpVar.c_str());
visitor->visit(field);
builder->appendFormat("))[%u])", i);
builder->endOfStatement(true);

// update checksum
builder->target->emitTraceMessage(builder, "InternetChecksum: word=0x%llx", 1,
tmpVar.c_str());
builder->emitIndent();
if (addData) {
builder->appendFormat(
"bpf_p4tc_ext_csum_16bit_complement_add(&%s, sizeof(%s), "
"&%s, sizeof(%s))",
csumVar.c_str(), csumVar.c_str(), tmpVar.c_str(), tmpVar.c_str());
} else {
builder->appendFormat(
"bpf_p4tc_ext_csum_16bit_complement_sub(&%s, sizeof(%s), "
"&%s, sizeof(%s))",
csumVar.c_str(), csumVar.c_str(), tmpVar.c_str(), tmpVar.c_str());
}
builder->endOfStatement(true);
}
} else { // fields smaller or equal than 64 bits
while (bitsToRead > 0) {
if (fieldByteOrder == "NETWORK"_cs && bitsToRead == width) {
auto etype = EBPF::EBPFTypeFactory::instance->create(field->type);
builder->emitIndent();
etype->declare(builder, field_temp, false);
builder->appendFormat(" = %s(",
getConvertByteOrderFunction(width, fieldByteOrder));
visitor->visit(field);
builder->appendLine(");");
}
if (remainingBits == 16) {
builder->emitIndent();
builder->appendFormat("%s = ", tmpVar.c_str());
} else {
builder->append(" | ");
}

// TODO: add masks for fields, however they should not exceed declared width.
if (bitsToRead < remainingBits) {
remainingBits -= bitsToRead;
builder->append("(");
if (fieldByteOrder == "NETWORK"_cs) {
builder->appendFormat("%s", field_temp);
} else {
visitor->visit(field);
}
builder->appendFormat(" << %d)", remainingBits);
bitsToRead = 0;
} else if (bitsToRead == remainingBits) {
remainingBits = 0;
if (fieldByteOrder == "NETWORK"_cs) {
builder->appendFormat("%s", field_temp);
} else {
visitor->visit(field);
}
bitsToRead = 0;
} else if (bitsToRead > remainingBits) {
bitsToRead -= remainingBits;
remainingBits = 0;
builder->append("(");
if (fieldByteOrder == "NETWORK"_cs) {
builder->appendFormat("%s", field_temp);
} else {
visitor->visit(field);
}
builder->appendFormat(" >> %d)", bitsToRead);
}

if (remainingBits == 0) {
remainingBits = 16;
builder->endOfStatement(true);

// update checksum
builder->target->emitTraceMessage(builder, "InternetChecksum: word=0x%llx", 1,
tmpVar.c_str());
builder->emitIndent();
if (addData) {
builder->appendFormat(
"bpf_p4tc_ext_csum_16bit_complement_add(&%s, "
"sizeof(%s), &%s, sizeof(%s))",
csumVar.c_str(), csumVar.c_str(), tmpVar.c_str(), tmpVar.c_str());
} else {
builder->appendFormat(
"bpf_p4tc_ext_csum_16bit_complement_sub(&%s, "
"sizeof(%s), &%s, sizeof(%s))",
csumVar.c_str(), csumVar.c_str(), tmpVar.c_str(), tmpVar.c_str());
}
builder->endOfStatement(true);
}
}
}
}

builder->target->emitTraceMessage(builder, "InternetChecksum: new state=0x%llx", 1,
stateVar.c_str());
builder->blockEnd(true);
}

cstring InternetChecksumAlgorithmPNA::getConvertByteOrderFunction(unsigned widthToEmit,
cstring byte_order) {
cstring emit;
if (widthToEmit <= 16) {
emit = byte_order == "HOST" ? "bpf_ntohs"_cs : "bpf_htons"_cs;
} else if (widthToEmit <= 32) {
emit = byte_order == "HOST" ? "bpf_ntohl"_cs : "bpf_htonl"_cs;
} else if (widthToEmit <= 64) {
emit = byte_order == "HOST" ? "ntohll"_cs : "bpf_cpu_to_be64"_cs;
}
return emit;
}

} // namespace TC
Loading

0 comments on commit e97ec29

Please sign in to comment.