Skip to content

Commit

Permalink
P4TC - Implementation of Hash Extern (#4980)
Browse files Browse the repository at this point in the history
* Implementation of Hash Extern

Signed-off-by: Komal, Jain <[email protected]>

* Addressed comments

Signed-off-by: Komal, Jain <[email protected]>

---------

Signed-off-by: Komal, Jain <[email protected]>
  • Loading branch information
komaljai authored Oct 24, 2024
1 parent eb7a897 commit 0916051
Show file tree
Hide file tree
Showing 69 changed files with 647 additions and 5,271 deletions.
170 changes: 15 additions & 155 deletions backends/tc/ebpfCodeGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,6 @@ void PNAArchTC::emitHeader(EBPF::CodeBuilder *builder) const {
emitP4TCFilterFields(builder);
// BPF map definitions.
emitInstances(builder);
EBPFHashAlgorithmTypeFactoryPNA::instance()->emitGlobals(builder);
}

// =====================TCIngressPipelinePNA=============================
Expand Down Expand Up @@ -1394,6 +1393,11 @@ bool ConvertToEBPFParserPNA::preorder(const IR::P4ValueSet *pvs) {
return false;
}

void EBPFControlPNA::emit(EBPF::CodeBuilder *builder) {
for (auto h : pna_hashes) h.second->emitVariables(builder);
EBPF::EBPFControl::emit(builder);
}

// =====================ConvertToEBPFControlPNA=============================
bool ConvertToEBPFControlPNA::preorder(const IR::ControlBlock *ctrl) {
control = new EBPFControlPNA(program, ctrl, parserHeaders);
Expand Down Expand Up @@ -1491,8 +1495,8 @@ bool ConvertToEBPFControlPNA::preorder(const IR::ExternBlock *instance) {
cstring name = EBPF::EBPFObject::externalName(di);
cstring typeName = instance->type->getName().name;
if (typeName == "Hash") {
auto hash = new EBPF::EBPFHashPSA(program, di, name);
control->hashes.emplace(name, hash);
auto hash = new EBPFHashPNA(program, di, name);
control->pna_hashes.emplace(name, hash);
} else if (typeName == "Register") {
auto reg = new EBPFRegisterPNA(program, name, di, control->codeGen);
control->pna_registers.emplace(name, reg);
Expand Down Expand Up @@ -2009,7 +2013,7 @@ void ControlBodyTranslatorPNA::processMethod(const P4::ExternMethod *method) {
pna_meter->emitExecute(builder, method, this, nullptr);
return;
} else if (declType->name.name == "Hash") {
auto hash = control->to<EBPF::EBPFControlPSA>()->getHash(name);
auto hash = pnaControl->getHash(name);
hash->processMethod(builder, method->method->name.name, method->expr, this);
return;
} else {
Expand Down Expand Up @@ -2048,7 +2052,7 @@ bool ControlBodyTranslatorPNA::preorder(const IR::AssignmentStatement *a) {
return false;
} else if (ext->originalExternType->name.name == "Hash") {
cstring name = EBPF::EBPFObject::externalName(ext->object);
auto hash = control->to<EBPF::EBPFControlPSA>()->getHash(name);
auto hash = pnaControl->getHash(name);
// Before assigning a value to a left expression we have to calculate a hash.
// Then the hash value is stored in a registerVar variable.
hash->calculateHash(builder, ext->expr, this);
Expand Down Expand Up @@ -2380,160 +2384,16 @@ void DeparserHdrEmitTranslatorPNA::emitField(EBPF::CodeBuilder *builder, cstring

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) {
if (type == EBPF::EBPFHashAlgorithmPSA::HashAlgorithm::CRC16) {
return new CRCChecksumAlgorithmPNA(program, name, 16);
} else if (type == EBPF::EBPFHashAlgorithmPSA::HashAlgorithm::CRC32) {
return new CRCChecksumAlgorithmPNA(program, name, 32);
} else 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,
// applies host byte order for input data, otherwise network byte order is expected.
if (crcWidth == 16) {
// This function calculates CRC16 by definition, it is bit by bit. If input data has more
// than 64 bit, the outer loop process bytes in network byte order - data pointer is
// incremented. For data shorter than or equal 64 bits, bytes are processed in little endian
// byte order - data pointer is decremented by outer loop in this case.
// There is no need for lookup table.
const char *code =
"static __always_inline\n"
"void crc16_update(u16 * reg, const u8 * data, "
"u16 data_size, const u16 poly) {\n"
" if (data_size <= 8)\n"
" data += data_size - 1;\n"
" #pragma clang loop unroll(full)\n"
" for (u16 i = 0; i < data_size; i++) {\n"
" bpf_trace_message(\"CRC16: data byte: %x\\n\", *data);\n"
" *reg ^= *data;\n"
" for (u8 bit = 0; bit < 8; bit++) {\n"
" *reg = (*reg) & 1 ? ((*reg) >> 1) ^ poly : (*reg) >> 1;\n"
" }\n"
" if (data_size <= 8)\n"
" data--;\n"
" else\n"
" data++;\n"
" }\n"
"}";
builder->appendLine(code);
} else if (crcWidth == 32) {
// This function calculates CRC32 using two optimisations: slice-by-8 and Standard
// Implementation. Both algorithms have to properly handle byte order depending on data
// length. There are four cases which must be handled:
// 1. Data size below 8 bytes - calculated using Standard Implementation in little endian
// byte order.
// 2. Data size equal to 8 bytes - calculated using slice-by-8 in little endian byte order.
// 3. Data size more than 8 bytes and multiply of 8 bytes - calculated using slice-by-8 in
// big endian byte order.
// 4. Data size more than 8 bytes and not multiply of 8 bytes - calculated using slice-by-8
// and Standard Implementation both in big endian byte order.
// Lookup table is necessary for both algorithms.
const char *code =
"static __always_inline\n"
"void crc32_update(u32 * reg, const u8 * data, u16 data_size, const u32 poly) {\n"
" u32* current = (u32*) data;\n"
" u32 index = 0;\n"
" u32 lookup_key = 0;\n"
" u32 lookup_value = 0;\n"
" u32 lookup_value1 = 0;\n"
" u32 lookup_value2 = 0;\n"
" u32 lookup_value3 = 0;\n"
" u32 lookup_value4 = 0;\n"
" u32 lookup_value5 = 0;\n"
" u32 lookup_value6 = 0;\n"
" u32 lookup_value7 = 0;\n"
" u32 lookup_value8 = 0;\n"
" u16 tmp = 0;\n"
" if (crc32_table != NULL) {\n"
" for (u16 i = data_size; i >= 8; i -= 8) {\n"
" /* Vars one and two will have swapped byte order if data_size == 8 */\n"
" if (data_size == 8) current = (u32 *)(data + 4);\n"
" bpf_trace_message(\"CRC32: data dword: %x\\n\", *current);\n"
" u32 one = (data_size == 8 ? __builtin_bswap32(*current--) : *current++) ^ "
"*reg;\n"
" bpf_trace_message(\"CRC32: data dword: %x\\n\", *current);\n"
" u32 two = (data_size == 8 ? __builtin_bswap32(*current--) : *current++);\n"
" lookup_key = (one & 0x000000FF);\n"
" lookup_value8 = crc32_table[(u16)(1792 + (u8)lookup_key)];\n"
" lookup_key = (one >> 8) & 0x000000FF;\n"
" lookup_value7 = crc32_table[(u16)(1536 + (u8)lookup_key)];\n"
" lookup_key = (one >> 16) & 0x000000FF;\n"
" lookup_value6 = crc32_table[(u16)(1280 + (u8)lookup_key)];\n"
" lookup_key = one >> 24;\n"
" lookup_value5 = crc32_table[(u16)(1024 + (u8)(lookup_key))];\n"
" lookup_key = (two & 0x000000FF);\n"
" lookup_value4 = crc32_table[(u16)(768 + (u8)lookup_key)];\n"
" lookup_key = (two >> 8) & 0x000000FF;\n"
" lookup_value3 = crc32_table[(u16)(512 + (u8)lookup_key)];\n"
" lookup_key = (two >> 16) & 0x000000FF;\n"
" lookup_value2 = crc32_table[(u16)(256 + (u8)lookup_key)];\n"
" lookup_key = two >> 24;\n"
" lookup_value1 = crc32_table[(u8)(lookup_key)];\n"
" *reg = lookup_value8 ^ lookup_value7 ^ lookup_value6 ^ lookup_value5 ^\n"
" lookup_value4 ^ lookup_value3 ^ lookup_value2 ^ lookup_value1;\n"
" tmp += 8;\n"
" }\n"
" volatile int std_algo_lookup_key = 0;\n"
" if (data_size < 8) {\n"
// Standard Implementation for little endian byte order
" unsigned char *currentChar = (unsigned char *) current;\n"
" currentChar += data_size - 1;\n"
" for (u16 i = tmp; i < data_size; i++) {\n"
" bpf_trace_message(\"CRC32: data byte: %x\\n\", *currentChar);\n"
" std_algo_lookup_key = (u32)(((*reg) & 0xFF) ^ *currentChar--);\n"
" if (std_algo_lookup_key >= 0) {\n"
" lookup_value = "
"crc32_table[(u8)(std_algo_lookup_key & 255)];\n"
" }\n"
" *reg = ((*reg) >> 8) ^ lookup_value;\n"
" }\n"
" } else {\n"
// Standard Implementation for big endian byte order
" /* Consume data not processed by slice-by-8 algorithm above, "
"these data are in network byte order */\n"
" unsigned char *currentChar = (unsigned char *) current;\n"
" for (u16 i = tmp; i < data_size; i++) {\n"
" bpf_trace_message(\"CRC32: data byte: %x\\n\", *currentChar);\n"
" std_algo_lookup_key = (u32)(((*reg) & 0xFF) ^ *currentChar++);\n"
" if (std_algo_lookup_key >= 0) {\n"
" lookup_value = "
"crc32_table[(u8)(std_algo_lookup_key & 255)];\n"
" }\n"
" *reg = ((*reg) >> 8) ^ lookup_value;\n"
" }\n"
" }\n"
" }\n"
"}";
builder->appendLine(code);
}
}

// ===========================CRC16ChecksumAlgorithmPNA===========================

void CRC16ChecksumAlgorithmPNA::emitGlobals(EBPF::CodeBuilder *builder) {
CRCChecksumAlgorithmPNA::emitUpdateMethod(builder, 16);

const char *code =
"static __always_inline "
"u16 crc16_finalize(u16 reg) {\n"
" return reg;\n"
"}";
builder->appendLine(code);
}

// ===========================CRC32ChecksumAlgorithmPNA===========================

void CRC32ChecksumAlgorithmPNA::emitGlobals(EBPF::CodeBuilder *builder) {
CRCChecksumAlgorithmPNA::emitUpdateMethod(builder, 32);

const char *code =
"static __always_inline "
"u32 crc32_finalize(u32 reg) {\n"
" return reg ^ 0xFFFFFFFF;\n"
"}";
builder->appendLine(code);
}

} // namespace P4::TC
50 changes: 7 additions & 43 deletions backends/tc/ebpfCodeGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ using namespace P4::literals;
class ConvertToBackendIR;
class EBPFPnaParser;
class EBPFRegisterPNA;
class EBPFHashPNA;

// Similar to class PSAEbpfGenerator in backends/ebpf/psa/ebpfPsaGen.h

Expand Down Expand Up @@ -279,6 +280,7 @@ class EBPFControlPNA : public EBPF::EBPFControlPSA {
public:
bool addExternDeclaration = false;
std::map<cstring, EBPFRegisterPNA *> pna_registers;
std::map<cstring, EBPFHashPNA *> pna_hashes;

EBPFControlPNA(const EBPF::EBPFProgram *program, const IR::ControlBlock *control,
const IR::Parameter *parserHeaders)
Expand All @@ -289,6 +291,10 @@ class EBPFControlPNA : public EBPF::EBPFControlPSA {
BUG_CHECK(result != nullptr, "No register named %1%", name);
return result;
}
EBPFHashPNA *getHash(cstring name) const {
auto result = ::P4::get(pna_hashes, name);
return result;
}
void emitExternDefinition(EBPF::CodeBuilder *builder) {
if (addExternDeclaration) {
builder->emitIndent();
Expand All @@ -300,6 +306,7 @@ class EBPFControlPNA : public EBPF::EBPFControlPSA {
}
}
void emitTableTypes(EBPF::CodeBuilder *builder) { EBPF::EBPFControl::emitTableTypes(builder); }
void emit(EBPF::CodeBuilder *builder);
};

// Similar to class ConvertToEBPFControlPSA in backends/ebpf/psa/ebpfPsaGen.h
Expand Down Expand Up @@ -415,56 +422,13 @@ class DeparserHdrEmitTranslatorPNA : public EBPF::DeparserPrepareBufferTranslato
unsigned alignment, EBPF::EBPFType *type, bool isMAC);
};

class CRCChecksumAlgorithmPNA : public EBPF::CRCChecksumAlgorithm {
public:
CRCChecksumAlgorithmPNA(const EBPF::EBPFProgram *program, cstring name, int width)
: EBPF::CRCChecksumAlgorithm(program, name, width) {}

static void emitUpdateMethod(EBPF::CodeBuilder *builder, int crcWidth);
};

class CRC16ChecksumAlgorithmPNA : public CRCChecksumAlgorithmPNA {
public:
CRC16ChecksumAlgorithmPNA(const EBPF::EBPFProgram *program, cstring name)
: CRCChecksumAlgorithmPNA(program, name, 16) {
initialValue = "0"_cs;
// We use a 0x8005 polynomial.
// 0xA001 comes from 0x8005 value bits reflection.
polynomial = "0xA001"_cs;
updateMethod = "crc16_update"_cs;
finalizeMethod = "crc16_finalize"_cs;
}

static void emitGlobals(EBPF::CodeBuilder *builder);
};

class CRC32ChecksumAlgorithmPNA : public CRCChecksumAlgorithmPNA {
public:
CRC32ChecksumAlgorithmPNA(const EBPF::EBPFProgram *program, cstring name)
: CRCChecksumAlgorithmPNA(program, name, 32) {
initialValue = "0xffffffff"_cs;
// We use a 0x04C11DB7 polynomial.
// 0xEDB88320 comes from 0x04C11DB7 value bits reflection.
polynomial = "0xEDB88320"_cs;
updateMethod = "crc32_update"_cs;
finalizeMethod = "crc32_finalize"_cs;
}

static void emitGlobals(EBPF::CodeBuilder *builder);
};

class EBPFHashAlgorithmTypeFactoryPNA : public EBPF::EBPFHashAlgorithmTypeFactoryPSA {
public:
static EBPFHashAlgorithmTypeFactoryPNA *instance() {
static EBPFHashAlgorithmTypeFactoryPNA factory;
return &factory;
}

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

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

Expand Down
70 changes: 70 additions & 0 deletions backends/tc/tcExterns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,76 @@ void EBPFDigestPNA::emitInitializer(EBPF::CodeBuilder *builder) const {
builder->newline();
}

void EBPFHashPNA::emitVariables(EBPF::CodeBuilder *builder) {
if (engine) {
engine->emitVariables(builder, declaration);
}
}

void EBPFHashPNA::processMethod(EBPF::CodeBuilder *builder, cstring method,
const IR::MethodCallExpression *expr, Visitor *visitor) {
engine->setVisitor(visitor);

if (method == "get_hash") {
BUG_CHECK(expr->arguments->size() == 1 || expr->arguments->size() == 3,
"Expected 1 or 3 arguments: %1%", expr);
engine->emitGet(builder);
} else {
::P4::error(ErrorType::ERR_UNEXPECTED, "Unexpected method call %1%", expr);
}
}

void EBPFHashPNA::calculateHash(EBPF::CodeBuilder *builder, const IR::MethodCallExpression *expr,
Visitor *visitor) {
engine->setVisitor(visitor);
engine->emitClear(builder);
engine->emitAddData(builder, expr->arguments->size() == 3 ? 1 : 0, expr);
builder->newline();
}

void CRCChecksumAlgorithmPNA::emitGet(EBPF::CodeBuilder *builder) {
if (crcWidth == 16) {
builder->appendFormat("%s", registerVar.c_str());
} else {
builder->appendFormat("%s ^ 0xFFFFFFFF", registerVar.c_str());
}
}

void CRCChecksumAlgorithmPNA::emitAddData(EBPF::CodeBuilder *builder, int dataPos,
const IR::MethodCallExpression *expr) {
emitAddData(builder, unpackArguments(expr, dataPos), expr);
}

void CRCChecksumAlgorithmPNA::emitAddData(EBPF::CodeBuilder *builder,
const ArgumentsList &arguments,
const IR::MethodCallExpression *expr) {
cstring kfunc;
if (crcWidth == 16) {
kfunc = expr->arguments->size() == 3 ? "bpf_p4tc_ext_hash_base_crc16"_cs
: "bpf_p4tc_ext_hash_crc16"_cs;
} else {
kfunc = expr->arguments->size() == 3 ? "bpf_p4tc_ext_hash_base_crc32"_cs
: "bpf_p4tc_ext_hash_crc32"_cs;
}
for (auto field : arguments) {
builder->newline();
builder->emitIndent();
builder->appendFormat("%s(&", kfunc);
visitor->visit(field);
builder->append(", sizeof(");
visitor->visit(field);
if (expr->arguments->size() == 3) {
builder->append("), ");
visitor->visit(expr->arguments->at(0));
builder->append(", ");
visitor->visit(expr->arguments->at(2));
builder->appendFormat(", %s);", registerVar);
} else {
builder->appendFormat("), %s);", registerVar);
}
}
}

void EBPFDigestPNA::emitPushElement(EBPF::CodeBuilder *builder, const IR::Expression *elem,
Inspector *codegen) const {
emitInitializer(builder);
Expand Down
Loading

0 comments on commit 0916051

Please sign in to comment.