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

create dpdk specific pna.p4 and extend it #3658

Merged
merged 11 commits into from
Nov 11, 2022
Prev Previous commit
Next Next commit
Added test and updated readme
  • Loading branch information
kamleshbhalui committed Nov 11, 2022
commit e22a36d212a30a2724308e2bb5c4f73ff4ea0f7d
20 changes: 20 additions & 0 deletions backends/dpdk/DpdkXfail.cmake
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
p4c_add_xfail_reason("dpdk"
"use dpdk specific `dpdk_execute` method"
testdata/p4_16_samples/psa-example-dpdk-meter-execute-err.p4
)

p4c_add_xfail_reason("dpdk"
"Expected packet length argument for count method of indirect counter"
testdata/p4_16_samples/psa-example-counters-bmv2.p4
)

p4c_add_xfail_reason("dpdk"
"error: Name .* is used for multiple direct counter objects in the P4Info message"
testdata/p4_16_samples/psa-counter6.p4
)

p4c_add_xfail_reason("dpdk"
"Expected psa_implementation property value for table.* to resolve to an extern instance"
testdata/p4_16_samples/psa-action-profile2.p4
)

p4c_add_xfail_reason("dpdk"
"not implemented"
testdata/p4_16_samples/psa-example-dpdk-byte-alignment_4.p4
Expand Down
7 changes: 7 additions & 0 deletions backends/dpdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ In P4 the second argument of the extract method is the number of bits.
Compiler generates instructions which compute the size in bytes from the value in bits.
If the value in bits is not a multiple of 8, the value is rounded down to the lower
multiple of 8 bits.
- Currently dpdk target does not support standard count and execute as defined in PSA and PNA, it requires packet length in methods count and execute which are part of Counter and Meter externs. Adding param ``` in bit<32> pkt_len``` to execute (part of Meter extern) leads to overload resolution failure due to ambiguous candidates, as p4c do overload resolution based on number of parameter and does not consider types. To workaround this we introduced new method in Meter extern `dpdk_execute` which has extra param.
```Meter
Copy link
Contributor

Choose a reason for hiding this comment

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

Does DPDK development have any plans in the future to support the standard count and execute methods, without a packet length parameter?

Copy link
Contributor

Choose a reason for hiding this comment

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

There is no plan in the near future. There was an earlier discussion of providing ethernet frame size as default packet length, however it seems that it wouldn't be correct as DPDK implements trCM which do not work on ethernet frame length.

Choose a reason for hiding this comment

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

"which do not work on ethernet frame length" Meters/policers can work on whatever length in bytes you give for a packet, whether it matches the packet's length or not, so it isn't clear to me what your statement means. If DPDK implements meters by default with ethernet frame length, then I would recommend enabling them to do so, and DOCUMENTING that the methods that do not take a packet length as input, use the ethernet frame length in bytes. If they work as documented, then I'd say that they work :-)

Copy link
Contributor

Choose a reason for hiding this comment

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

I will take this up with the DPDK target team again and see if they can have this in their worklist. We can enable the p4c support when we have the default packet length available from the target side.

PNA_MeterColor_t dpdk_execute(in S index, in PNA_MeterColor_t color, in bit<32> pkt_len);
```
```Counter
void count(in S index, in bit<32> pkt_len);
```

## Contacts

Expand Down
16 changes: 11 additions & 5 deletions backends/dpdk/dpdkHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,8 +348,14 @@ bool ConvertStatementToDpdk::preorder(const IR::AssignmentStatement *a) {
left, e->object->getName(), intermediate);
}
} else if (e->originalExternType->getName().name == "Meter") {
if (e->method->getName().name == "execute"
|| e->method->getName().name == "dpdk_execute") {
if (e->method->getName().name == "execute") {
::error(ErrorType::ERR_UNEXPECTED,
"use dpdk specific `dpdk_execute` method, `%1%`"
" not supported by dpdk",
e->method->getName());
return false;
}
if (e->method->getName().name == "dpdk_execute") {
auto argSize = e->expr->arguments->size();

// DPDK target needs index and packet length as mandatory parameters
Expand Down Expand Up @@ -1055,9 +1061,9 @@ bool ConvertStatementToDpdk::preorder(const IR::MethodCallStatement *s) {
}
}
} else if (a->originalExternType->getName().name == "Meter") {
if (a->method->getName().name != "execute"
&& a->method->getName().name != "dpdk_execute") {
BUG("Meter function %1% not implemented.", a->method->getName());
if (a->method->getName().name != "dpdk_execute") {
BUG("Meter function %1% not implemented, use dpdk_execute",
a->method->getName());
}
} else if (a->originalExternType->getName().name == "DirectMeter") {
if (a->method->getName().name != "execute") {
Expand Down
139 changes: 139 additions & 0 deletions testdata/p4_16_samples/psa-example-dpdk-meter-execute-err.p4
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#include <core.p4>
#include <dpdk/psa.p4>

typedef bit<48> EthernetAddress;
header ethernet_t {
EthernetAddress dstAddr;
EthernetAddress srcAddr;
bit<16> etherType;
}

header ipv4_t {
bit<4> version;
bit<4> ihl;
bit<8> diffserv;
bit<32> totalLen;
bit<16> identification;
bit<3> flags;
bit<13> fragOffset;
bit<8> ttl;
bit<8> protocol;
bit<16> hdrChecksum;
bit<32> srcAddr;
bit<32> dstAddr;
}

struct empty_metadata_t {
}

struct metadata_t {
bit<32> port_in;
bit<32> port_out;
}

struct headers {
ethernet_t ethernet;
ipv4_t ipv4;
}

// Define additional error values, one of them for packets with
// incorrect IPv4 header checksums.
error {
UnhandledIPv4Options,
BadIPv4HeaderChecksum
}

parser IngressParserImpl(packet_in buffer, out headers hdr, inout metadata_t user_meta, in psa_ingress_parser_input_metadata_t istd, in empty_metadata_t resubmit_meta, in empty_metadata_t recirculate_meta) {
state start {
buffer.extract(hdr.ethernet);
transition select(hdr.ethernet.etherType) {
0x800: parse_ipv4;
default: accept;
}
}
state parse_ipv4 {
buffer.extract(hdr.ipv4);
verify(hdr.ipv4.ihl == 5, error.UnhandledIPv4Options);
transition select(hdr.ipv4.version) {
default: accept;
}
}
}

control ingress(inout headers hdr, inout metadata_t user_meta, in psa_ingress_input_metadata_t istd, inout psa_ingress_output_metadata_t ostd) {
Counter<bit<10>, bit<12>>(1024, PSA_CounterType_t.PACKETS_AND_BYTES) counter0;
Counter<bit<10>, bit<12>>(1024, PSA_CounterType_t.PACKETS) counter1;
Counter<bit<10>, bit<12>>(1024, PSA_CounterType_t.BYTES) counter2;
Register<bit<32>, bit<12>>(1024) reg;
Meter<bit<12>>(1024, PSA_MeterType_t.BYTES) meter0;
PSA_MeterColor_t color_out;
PSA_MeterColor_t color_in = PSA_MeterColor_t.RED;
action execute(bit<12> index) {
hdr.ipv4.ihl = 5;
color_out = meter0.execute(index, color_in/*, hdr.ipv4.totalLen*/);
user_meta.port_out = (color_out == PSA_MeterColor_t.GREEN ? 32w1 : 32w0);
reg.write(index, user_meta.port_out);
if (hdr.ipv4.hdrChecksum[5:0] == 6)
hdr.ipv4.ihl = 5;
if (hdr.ipv4.version == 6)
hdr.ipv4.ihl = 6;
}

action test(bit<4> version) {
if (version == 4) {
hdr.ipv4.hdrChecksum[3:0] = hdr.ipv4.version + 5;
}
}
table tbl {
key = {
hdr.ethernet.srcAddr: exact;
}
actions = {
NoAction;
execute;
}
}
apply {
if (user_meta.port_out == 1) {
tbl.apply();
counter0.count(1023, 20);
counter1.count(512, 32);
counter2.count(1023, 64);
user_meta.port_out = reg.read(1);
test(hdr.ipv4.version);
} else {
return;
}
}
}

parser EgressParserImpl(packet_in buffer, out headers hdr, inout metadata_t user_meta, in psa_egress_parser_input_metadata_t istd, in empty_metadata_t normal_meta, in empty_metadata_t clone_i2e_meta, in empty_metadata_t clone_e2e_meta) {
state start {
transition accept;
}
}

control egress(inout headers hdr, inout metadata_t user_meta, in psa_egress_input_metadata_t istd, inout psa_egress_output_metadata_t ostd) {
apply {
}
}

control IngressDeparserImpl(packet_out packet, out empty_metadata_t clone_i2e_meta, out empty_metadata_t resubmit_meta, out empty_metadata_t normal_meta, inout headers hdr, in metadata_t meta, in psa_ingress_output_metadata_t istd) {
apply {
hdr.ipv4.hdrChecksum = 4; // Works fine
packet.emit(hdr.ethernet);
packet.emit(hdr.ipv4);
}
}

control EgressDeparserImpl(packet_out packet, out empty_metadata_t clone_e2e_meta, out empty_metadata_t recirculate_meta, inout headers hdr, in metadata_t meta, in psa_egress_output_metadata_t istd, in psa_egress_deparser_input_metadata_t edstd) {
apply {
}
}

IngressPipeline(IngressParserImpl(), ingress(), IngressDeparserImpl()) ip;

EgressPipeline(EgressParserImpl(), egress(), EgressDeparserImpl()) ep;

PSA_Switch(ip, PacketReplicationEngine(), ep, BufferingQueueingEngine()) main;

Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
error {
UnhandledIPv4Options,
BadIPv4HeaderChecksum
}
#include <core.p4>
#include <dpdk/psa.p4>

typedef bit<48> EthernetAddress;
header ethernet_t {
EthernetAddress dstAddr;
EthernetAddress srcAddr;
bit<16> etherType;
}

header ipv4_t {
bit<4> version;
bit<4> ihl;
bit<8> diffserv;
bit<32> totalLen;
bit<16> identification;
bit<3> flags;
bit<13> fragOffset;
bit<8> ttl;
bit<8> protocol;
bit<16> hdrChecksum;
bit<32> srcAddr;
bit<32> dstAddr;
}

struct empty_metadata_t {
}

struct metadata_t {
bit<32> port_in;
bit<32> port_out;
}

struct headers {
ethernet_t ethernet;
ipv4_t ipv4;
}

parser IngressParserImpl(packet_in buffer, out headers hdr, inout metadata_t user_meta, in psa_ingress_parser_input_metadata_t istd, in empty_metadata_t resubmit_meta, in empty_metadata_t recirculate_meta) {
state start {
buffer.extract<ethernet_t>(hdr.ethernet);
transition select(hdr.ethernet.etherType) {
16w0x800: parse_ipv4;
default: accept;
}
}
state parse_ipv4 {
buffer.extract<ipv4_t>(hdr.ipv4);
verify(hdr.ipv4.ihl == 4w5, error.UnhandledIPv4Options);
transition select(hdr.ipv4.version) {
default: accept;
}
}
}

control ingress(inout headers hdr, inout metadata_t user_meta, in psa_ingress_input_metadata_t istd, inout psa_ingress_output_metadata_t ostd) {
Counter<bit<10>, bit<12>>(32w1024, PSA_CounterType_t.PACKETS_AND_BYTES) counter0;
Counter<bit<10>, bit<12>>(32w1024, PSA_CounterType_t.PACKETS) counter1;
Counter<bit<10>, bit<12>>(32w1024, PSA_CounterType_t.BYTES) counter2;
Register<bit<32>, bit<12>>(32w1024) reg;
Meter<bit<12>>(32w1024, PSA_MeterType_t.BYTES) meter0;
PSA_MeterColor_t color_out;
PSA_MeterColor_t color_in = PSA_MeterColor_t.RED;
action execute(bit<12> index) {
hdr.ipv4.ihl = 4w5;
color_out = meter0.execute(index, color_in);
user_meta.port_out = (color_out == PSA_MeterColor_t.GREEN ? 32w1 : 32w0);
reg.write(index, user_meta.port_out);
if (hdr.ipv4.hdrChecksum[5:0] == 6w6) {
hdr.ipv4.ihl = 4w5;
}
if (hdr.ipv4.version == 4w6) {
hdr.ipv4.ihl = 4w6;
}
}
action test(bit<4> version) {
if (version == 4w4) {
hdr.ipv4.hdrChecksum[3:0] = hdr.ipv4.version + 4w5;
}
}
table tbl {
key = {
hdr.ethernet.srcAddr: exact @name("hdr.ethernet.srcAddr") ;
}
actions = {
NoAction();
execute();
}
default_action = NoAction();
}
apply {
if (user_meta.port_out == 32w1) {
tbl.apply();
counter0.count(12w1023, 32w20);
counter1.count(12w512, 32w32);
counter2.count(12w1023, 32w64);
user_meta.port_out = reg.read(12w1);
test(hdr.ipv4.version);
} else {
return;
}
}
}

parser EgressParserImpl(packet_in buffer, out headers hdr, inout metadata_t user_meta, in psa_egress_parser_input_metadata_t istd, in empty_metadata_t normal_meta, in empty_metadata_t clone_i2e_meta, in empty_metadata_t clone_e2e_meta) {
state start {
transition accept;
}
}

control egress(inout headers hdr, inout metadata_t user_meta, in psa_egress_input_metadata_t istd, inout psa_egress_output_metadata_t ostd) {
apply {
}
}

control IngressDeparserImpl(packet_out packet, out empty_metadata_t clone_i2e_meta, out empty_metadata_t resubmit_meta, out empty_metadata_t normal_meta, inout headers hdr, in metadata_t meta, in psa_ingress_output_metadata_t istd) {
apply {
hdr.ipv4.hdrChecksum = 16w4;
packet.emit<ethernet_t>(hdr.ethernet);
packet.emit<ipv4_t>(hdr.ipv4);
}
}

control EgressDeparserImpl(packet_out packet, out empty_metadata_t clone_e2e_meta, out empty_metadata_t recirculate_meta, inout headers hdr, in metadata_t meta, in psa_egress_output_metadata_t istd, in psa_egress_deparser_input_metadata_t edstd) {
apply {
}
}

IngressPipeline<headers, metadata_t, empty_metadata_t, empty_metadata_t, empty_metadata_t, empty_metadata_t>(IngressParserImpl(), ingress(), IngressDeparserImpl()) ip;

EgressPipeline<headers, metadata_t, empty_metadata_t, empty_metadata_t, empty_metadata_t, empty_metadata_t>(EgressParserImpl(), egress(), EgressDeparserImpl()) ep;

PSA_Switch<headers, metadata_t, headers, metadata_t, empty_metadata_t, empty_metadata_t, empty_metadata_t, empty_metadata_t, empty_metadata_t>(ip, PacketReplicationEngine(), ep, BufferingQueueingEngine()) main;

Loading