Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/buildomat/jobs/build-and-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#:
#: name = "build-and-test"
#: variety = "basic"
#: target = "helios-2.0"
#: target = "helios-3.0"
#: rust_toolchain = "stable"
#: output_rules = [
#: "/work/debug/*",
Expand Down
54 changes: 54 additions & 0 deletions codegen/rust/src/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ impl<'a> PipelineGenerator<'a> {
self.add_table_entry_method(ingress, egress);
let remove_table_entry_method =
self.remove_table_entry_method(ingress, egress);
let clear_table_entries_method =
self.clear_table_entries_method(ingress, egress);
let get_table_entries_method =
self.get_table_entries_method(ingress, egress);
let get_table_ids_method = self.get_table_ids_method(ingress, egress);
Expand Down Expand Up @@ -156,6 +158,7 @@ impl<'a> PipelineGenerator<'a> {
#pipeline_impl_process_packet
#add_table_entry_method
#remove_table_entry_method
#clear_table_entries_method
#get_table_entries_method
#get_table_ids_method
}
Expand Down Expand Up @@ -650,6 +653,45 @@ impl<'a> PipelineGenerator<'a> {
}
}

fn clear_table_entries_method(
&mut self,
ingress: &Control,
egress: &Control,
) -> TokenStream {
let mut body = TokenStream::new();

for control in &[ingress, egress] {
let tables = control.tables(self.ast);
for (cs, table) in tables.iter() {
let qtn = qualified_table_name(Some(control), cs, table);
let qtfn =
qualified_table_function_name(Some(control), cs, table);
let call = format_ident!("clear_{qtfn}_entries");
body.extend(quote! {
#qtn => {
self.#call();
true
}
});
}
}

body.extend(quote! {
x => {
println!("clear table entries: unknown table id {x}, ignoring");
false
}
});

quote! {
fn clear_table_entries(&mut self, table_id: &str) -> bool {
match table_id {
#body
}
}
}
}

fn get_table_ids_method(
&mut self,
ingress: &Control,
Expand Down Expand Up @@ -736,6 +778,7 @@ impl<'a> PipelineGenerator<'a> {
table_control,
&qtfn,
));
tokens.extend(self.clear_table_entries_function(&qtfn));
tokens.extend(self.get_table_entries_function(
table,
table_control,
Expand Down Expand Up @@ -1086,6 +1129,17 @@ impl<'a> PipelineGenerator<'a> {
}
}

fn clear_table_entries_function(&mut self, qtfn: &str) -> TokenStream {
let name = format_ident!("clear_{qtfn}_entries");
let tname = format_ident!("{qtfn}");

quote! {
pub fn #name(&mut self) {
self.#tname.entries.clear();
}
}
}

fn get_table_entries_function(
&mut self,
_table: &Table,
Expand Down
10 changes: 10 additions & 0 deletions lang/p4rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,16 @@ pub trait Pipeline: Send {
/// Remove an entry from a table identified by table_id.
fn remove_table_entry(&mut self, table_id: &str, keyset_data: &[u8]);

/// Remove all entries from a table identified by table_id.
///
/// Returns `true` if the table was cleared, or `false` if `table_id` maps
/// to no table in this pipeline. This return type distinguishes an unknown
/// table from an already-empty one, like [`Pipeline::get_table_entries`]
/// does with `None`.
// NOTE: This maps to dendrite's SoftNPU `TableOps::clear` implementation,
// which is the analogue of `bf_rt_table_clear` on Tofino, for example.
fn clear_table_entries(&mut self, table_id: &str) -> bool;

/// Get all the entries in a table.
fn get_table_entries(&self, table_id: &str) -> Option<Vec<TableEntry>>;

Expand Down
2 changes: 2 additions & 0 deletions test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ mod slice_assign_empty;
#[cfg(test)]
mod slice_read;
#[cfg(test)]
mod table_clear;
#[cfg(test)]
mod table_in_egress_and_ingress;
#[cfg(test)]
mod vlan;
Expand Down
61 changes: 61 additions & 0 deletions test/src/p4/table_clear.p4
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#include <core.p4>
#include <softnpu.p4>
#include <headers.p4>

SoftNPU(
parse(),
ingress(),
egress()
) main;

struct headers_t {
ethernet_h eth;
}

parser parse(
packet_in pkt,
out headers_t headers,
inout ingress_metadata_t ingress,
){
state start {
transition accept;
}
}

control bakery(
inout headers_t hdr,
inout ingress_metadata_t ingress,
inout egress_metadata_t egress,
) {

action drop() { }
action forward(bit<16> port) { egress.port = port; }
table muffins {
key = { ingress.port: exact; }
actions = { drop; forward; }
default_action = drop;
}

}

control ingress(
inout headers_t hdr,
inout ingress_metadata_t ingress,
inout egress_metadata_t egress,
) {
bakery() bakery;
apply {
bakery.apply(hdr, ingress, egress);
}
}

control egress(
inout headers_t hdr,
inout ingress_metadata_t ingress,
inout egress_metadata_t egress,
) {
bakery() bakery;
apply {
bakery.apply(hdr, ingress, egress);
}
}
54 changes: 54 additions & 0 deletions test/src/table_clear.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
p4_macro::use_p4!(
p4 = "test/src/p4/table_clear.p4",
pipeline_name = "table_clear",
);

const INGRESS_TBL: &str = "ingress.bakery.muffins";
const EGRESS_TBL: &str = "egress.bakery.muffins";

fn entry_count(pipeline: &main_pipeline, table: &str) -> usize {
pipeline.get_table_entries(table).unwrap().len()
}

fn add_forward(pipeline: &mut main_pipeline, table: &str, port: u16) {
pipeline.add_table_entry(
table,
"forward",
&port.to_le_bytes(),
&1u16.to_le_bytes(),
0,
);
}

#[test]
fn clear_table_entries() {
let mut pipeline = main_pipeline::new(4);

let ids = pipeline.get_table_ids();
assert!(ids.contains(&INGRESS_TBL));
assert!(ids.contains(&EGRESS_TBL));

for port in 0..3 {
add_forward(&mut pipeline, INGRESS_TBL, port);
add_forward(&mut pipeline, EGRESS_TBL, port);
}
assert_eq!(entry_count(&pipeline, INGRESS_TBL), 3);
assert_eq!(entry_count(&pipeline, EGRESS_TBL), 3);

assert!(pipeline.clear_table_entries(INGRESS_TBL));
assert_eq!(entry_count(&pipeline, INGRESS_TBL), 0);
assert_eq!(entry_count(&pipeline, EGRESS_TBL), 3);

assert!(pipeline.clear_table_entries(INGRESS_TBL));
assert_eq!(entry_count(&pipeline, INGRESS_TBL), 0);

assert!(!pipeline.clear_table_entries("egress.bakery.scones"));
assert_eq!(entry_count(&pipeline, EGRESS_TBL), 3);

add_forward(&mut pipeline, INGRESS_TBL, 7);
assert_eq!(entry_count(&pipeline, INGRESS_TBL), 1);

assert!(pipeline.clear_table_entries(EGRESS_TBL));
assert_eq!(entry_count(&pipeline, EGRESS_TBL), 0);
assert_eq!(entry_count(&pipeline, INGRESS_TBL), 1);
}
Loading