Skip to content

Commit

Permalink
Add a P4Info API to the control-plane folder and P4Tools. (#4381)
Browse files Browse the repository at this point in the history
* Add a P4Runtime API to P4Tools.

* Tests and refactoring.

* Switch back to pointer based unpacking for now.

* Apply fix-its.

* Review comments.
  • Loading branch information
fruffy committed Feb 2, 2024
1 parent a16fd93 commit 4c849a6
Show file tree
Hide file tree
Showing 11 changed files with 881 additions and 237 deletions.
15 changes: 15 additions & 0 deletions backends/p4tools/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,18 @@ target_include_directories(
)

add_dependencies(p4tools-common ir-generated frontend)

set(
P4C_TOOLS_CONTROL_PLANE_SOURCES
control_plane/p4info_map.cpp
)

add_p4tools_library(p4tools-control-plane ${P4C_TOOLS_CONTROL_PLANE_SOURCES})

target_include_directories(
p4tools-control-plane
SYSTEM BEFORE PUBLIC ${Protobuf_INCLUDE_DIRS}
)

add_dependencies(p4tools-control-plane controlplane)

93 changes: 93 additions & 0 deletions backends/p4tools/common/control_plane/p4info_map.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#include "backends/p4tools/common/control_plane/p4info_map.h"

namespace P4::ControlPlaneAPI {

P4InfoMaps::P4InfoMaps(const p4::config::v1::P4Info &p4Info) { buildP4InfoMaps(p4Info); }

uint64_t szudzikPairing(p4rt_id_t x, p4rt_id_t y) {
// See https://en.wikipedia.org/wiki/Pairing_function#Other_pairing_functions
// This static assert ensures that p4rt_id_t used here is a uint32_t.
// In case things change down the road.
static_assert(std::is_convertible_v<p4rt_id_t, uint32_t> &&
std::is_same_v<p4rt_id_t, uint32_t>);
return x >= y ? static_cast<uint64_t>(x) * x + x + y : x + static_cast<uint64_t>(y) * y;
}

void P4InfoMaps::buildP4InfoMaps(const p4::config::v1::P4Info &p4Info) {
for (const auto &table : p4Info.tables()) {
nameToIdMap[table.preamble().name()] = table.preamble().id();
idToNameMap[table.preamble().id()] = table.preamble().name();
for (const auto &matchField : table.match_fields()) {
auto combinedName = table.preamble().name() + "_" + matchField.name();
nameToIdMap[combinedName] = matchField.id();
idToNameMap[szudzikPairing(table.preamble().id(), matchField.id())] = matchField.name();
}
}
for (const auto &action : p4Info.actions()) {
nameToIdMap[action.preamble().name()] = action.preamble().id();
idToNameMap[action.preamble().id()] = action.preamble().name();
for (const auto &param : action.params()) {
auto combinedName = action.preamble().name() + "_" + param.name();
nameToIdMap[combinedName] = param.id();
idToNameMap[szudzikPairing(action.preamble().id(), param.id())] = param.name();
}
}
for (const auto &actionProfile : p4Info.action_profiles()) {
nameToIdMap[actionProfile.preamble().name()] = actionProfile.preamble().id();
idToNameMap[actionProfile.preamble().id()] = actionProfile.preamble().name();
}
for (const auto &counter : p4Info.counters()) {
nameToIdMap[counter.preamble().name()] = counter.preamble().id();
idToNameMap[counter.preamble().id()] = counter.preamble().name();
}
for (const auto &counter : p4Info.direct_counters()) {
nameToIdMap[counter.preamble().name()] = counter.preamble().id();
idToNameMap[counter.preamble().id()] = counter.preamble().name();
}
for (const auto &meter : p4Info.meters()) {
nameToIdMap[meter.preamble().name()] = meter.preamble().id();
idToNameMap[meter.preamble().id()] = meter.preamble().name();
}
for (const auto &directMeter : p4Info.direct_meters()) {
nameToIdMap[directMeter.preamble().name()] = directMeter.preamble().id();
idToNameMap[directMeter.preamble().id()] = directMeter.preamble().name();
}
for (const auto &controllerMetadata : p4Info.controller_packet_metadata()) {
nameToIdMap[controllerMetadata.preamble().name()] = controllerMetadata.preamble().id();
idToNameMap[controllerMetadata.preamble().id()] = controllerMetadata.preamble().name();
}
for (const auto &valueSet : p4Info.value_sets()) {
nameToIdMap[valueSet.preamble().name()] = valueSet.preamble().id();
idToNameMap[valueSet.preamble().id()] = valueSet.preamble().name();
}
for (const auto &p4Register : p4Info.registers()) {
nameToIdMap[p4Register.preamble().name()] = p4Register.preamble().id();
idToNameMap[p4Register.preamble().id()] = p4Register.preamble().name();
}
for (const auto &digest : p4Info.digests()) {
nameToIdMap[digest.preamble().name()] = digest.preamble().id();
idToNameMap[digest.preamble().id()] = digest.preamble().name();
}
for (const auto &p4Extern : p4Info.externs()) {
nameToIdMap[p4Extern.extern_type_name()] = p4Extern.extern_type_id();
idToNameMap[p4Extern.extern_type_id()] = p4Extern.extern_type_name();
}
}

std::optional<uint64_t> P4InfoMaps::lookUpP4RuntimeId(cstring controlPlaneName) const {
auto it = nameToIdMap.find(controlPlaneName);
if (it == nameToIdMap.end()) {
return std::nullopt;
}
return it->second;
}

std::optional<cstring> P4InfoMaps::lookUpControlPlaneName(uint64_t id) const {
auto it = idToNameMap.find(id);
if (it == idToNameMap.end()) {
return std::nullopt;
}
return it->second;
}

} // namespace P4::ControlPlaneAPI
66 changes: 66 additions & 0 deletions backends/p4tools/common/control_plane/p4info_map.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#ifndef BACKENDS_P4TOOLS_COMMON_CONTROL_PLANE_P4INFO_MAP_H_
#define BACKENDS_P4TOOLS_COMMON_CONTROL_PLANE_P4INFO_MAP_H_
#include <cstdint>
#include <map>
#include <optional>

#include "control-plane/p4RuntimeArchHandler.h"
#include "control-plane/p4RuntimeSerializer.h"
#include "lib/cstring.h"

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wpedantic"
#include "p4/config/v1/p4info.pb.h"
#pragma GCC diagnostic pop

/// TODO: Consider migrating this API to the top-level control-plane folder.
/// The reason we have not already done this is because that folder already provides similar utility
/// functions. However, these functions are tied to the P4RuntimeTableIface, which is fairly
/// inflexible. We just need an API that can perform lookup operations on a P4Info or P4RuntimeAPI
/// object.
namespace P4::ControlPlaneAPI {

/// Computes a unique pairing of two input numbers. We use this to generate unique P4Runtime IDs
/// for combinations of tables and key elements, or actions and parameters.
/// https://en.wikipedia.org/wiki/Pairing_function#Other_pairing_functions
/// The maximum szudzikPairing value is 2^64 - 1, which is why we use uint64_t for the p4rt_id_t,
/// which is uint32_t bit.
uint64_t szudzikPairing(p4rt_id_t x, p4rt_id_t y);

/// This object maps P4 control plane names to their P4Runtime IDs and vice versa.
/// It uses the P4Info object to populate the maps.
/// Since ids for action parameters and table keys are not unique, we use a pairing function to
/// compute a unique identifier. This pairing function uses the id of the parent object (e.g., a
/// table or action) and combines it with the id of the parameter or key element to create a unique
/// identifier.
class P4InfoMaps {
/// Type definitions for convenience.
using P4RuntimeIdToControlPlaneNameMap = std::map<uint64_t, cstring>;
using ControlPlaneNameToP4RuntimeIdMap = std::map<cstring, uint64_t>;

protected:
/// Maps P4Runtime IDs to control plane names.
P4RuntimeIdToControlPlaneNameMap idToNameMap;

/// Maps control plane names to P4Runtime IDs.
ControlPlaneNameToP4RuntimeIdMap nameToIdMap;

/// Iterate over the P4Info object and build a mapping from P4 control plane names to their ids.
virtual void buildP4InfoMaps(const p4::config::v1::P4Info &p4Info);

public:
explicit P4InfoMaps(const p4::config::v1::P4Info &p4Info);

/// Looks up the P4Runtime id for the given control plane name in the pre-computed P4Runtime-ID
/// map. @returns std::nullopt if the name is not in the map.
[[nodiscard]] std::optional<uint64_t> lookUpP4RuntimeId(cstring controlPlaneName) const;

/// Looks up the control plane name for the given P4Runtime id in the pre-computed P4Runtime-ID
/// map. @returns std::nullopt if the id is not in the map.
[[nodiscard]] std::optional<cstring> lookUpControlPlaneName(uint64_t id) const;
};

} // namespace P4::ControlPlaneAPI

#endif /* BACKENDS_P4TOOLS_COMMON_CONTROL_PLANE_P4INFO_MAP_H_ */
2 changes: 2 additions & 0 deletions backends/p4tools/modules/testgen/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ set(

test/gtest_utils.cpp
test/lib/format_int.cpp
test/lib/p4info_api.cpp
test/lib/taint.cpp
test/small-step/util.cpp
test/z3-solver/constraints.cpp
Expand All @@ -71,6 +72,7 @@ fetchcontent_makeavailable_but_exclude_install(inja)
set(
TESTGEN_LIBS
PRIVATE p4tools-common
PRIVATE p4tools-control-plane
PUBLIC inja
)
set(TESTGEN_INCLUDES)
Expand Down
25 changes: 25 additions & 0 deletions backends/p4tools/modules/testgen/test/lib/p4info_api.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include "backends/p4tools/modules/testgen/test/lib/p4info_api.h"

#include "backends/p4tools/common/control_plane/p4info_map.h"

namespace Test {

namespace {

using P4::ControlPlaneAPI::p4rt_id_t;
using P4::ControlPlaneAPI::szudzikPairing;

TEST_F(P4RuntimeApiTest, SzudzikPairingisCorrect) {
EXPECT_EQ(szudzikPairing(0, 0), 0);
EXPECT_EQ(szudzikPairing(0, 1), 1);
EXPECT_EQ(szudzikPairing(1, 0), 2);
EXPECT_EQ(szudzikPairing(3, 5), 28);
EXPECT_EQ(szudzikPairing(5, 3), 33);
EXPECT_EQ(szudzikPairing(UINT32_MAX, UINT32_MAX), UINT64_MAX);
EXPECT_EQ(szudzikPairing(0, UINT32_MAX), 18446744065119617025U);
EXPECT_EQ(szudzikPairing(UINT32_MAX, 0), 18446744069414584320U);
}

} // namespace

} // namespace Test
13 changes: 13 additions & 0 deletions backends/p4tools/modules/testgen/test/lib/p4info_api.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#ifndef BACKENDS_P4TOOLS_MODULES_TESTGEN_TEST_LIB_P4INFO_API_H_
#define BACKENDS_P4TOOLS_MODULES_TESTGEN_TEST_LIB_P4INFO_API_H_

#include "backends/p4tools/modules/testgen/test/gtest_utils.h"

namespace Test {

/// Helper methods to build configurations for P4RuntimeApi Tests.
class P4RuntimeApiTest : public P4ToolsTest {};

} // namespace Test

#endif /* BACKENDS_P4TOOLS_MODULES_TESTGEN_TEST_LIB_P4INFO_API_H_ */
2 changes: 2 additions & 0 deletions control-plane/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ set (CONTROLPLANE_SRCS
addMissingIds.cpp
bytestrings.cpp
flattenHeader.cpp
p4infoApi.cpp
p4RuntimeArchHandler.cpp
p4RuntimeArchStandard.cpp
p4RuntimeSerializer.cpp
Expand All @@ -84,6 +85,7 @@ set (CONTROLPLANE_HDRS
addMissingIds.h
bytestrings.h
flattenHeader.h
p4infoApi.h
p4RuntimeArchHandler.h
p4RuntimeArchStandard.h
p4RuntimeSerializer.h
Expand Down
4 changes: 4 additions & 0 deletions control-plane/p4RuntimeArchHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ class P4RuntimeSymbolType {
return P4RuntimeSymbolType(::p4::config::v1::P4Ids::CONTROLLER_HEADER);
}

static P4RuntimeSymbolType P4RT_OTHER_EXTERNS_START() {
return P4RuntimeSymbolType(::p4::config::v1::P4Ids::OTHER_EXTERNS_START);
}

bool operator==(const P4RuntimeSymbolType &other) const { return id == other.id; }

bool operator!=(const P4RuntimeSymbolType &other) const { return !(*this == other); }
Expand Down
Loading

0 comments on commit 4c849a6

Please sign in to comment.