Skip to content

[feature] ASIC-focused multicast replication and dendrite API #14

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

Merged
merged 28 commits into from
Jul 16, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
df62508
[feature] ASIC-focused 1st draft of multicast PRE
zeeshanlakhani Mar 4, 2025
4a945a8
[fix] change stage max
zeeshanlakhani Apr 15, 2025
bb876fa
update chaos tables, revert any p4 formatting for review
zeeshanlakhani Apr 16, 2025
2dcb98e
[minor] comments/align
zeeshanlakhani Apr 16, 2025
3ef78f0
Merge remote-tracking branch 'origin/multicast' into zl/p4-mcast-3
zeeshanlakhani Apr 21, 2025
37498e4
[review] address first pass of review
zeeshanlakhani Apr 21, 2025
d4953df
[review+] meta -> metadata, fix test issues
zeeshanlakhani Apr 21, 2025
dbe684f
[stages] test back to 14
zeeshanlakhani Apr 21, 2025
5537794
..
zeeshanlakhani Apr 22, 2025
1e29a70
[review] validation and underlying api transactions
zeeshanlakhani Apr 23, 2025
8a9fd45
[major changes] Rework groups+dataplane to handle external/underlay/b…
zeeshanlakhani Apr 23, 2025
105a296
Merge remote-tracking branch 'origin/multicast' into zl/p4-mcast
zeeshanlakhani May 22, 2025
1c0e660
[minor] chaos and remove pub
zeeshanlakhani May 22, 2025
57804d1
[minor] openapi update
zeeshanlakhani May 22, 2025
e55ddbc
[update] link-local hop limit handling + counter categories
zeeshanlakhani Jun 3, 2025
09fcec0
Change API into internal/replication vs external
zeeshanlakhani Jun 19, 2025
e3dda26
Merge remote-tracking branch 'origin/multicast' into zl/p4-mcast
zeeshanlakhani Jun 21, 2025
da0c3b3
..
zeeshanlakhani Jun 22, 2025
7158e91
..
zeeshanlakhani Jun 23, 2025
5a26a07
[review] updates on lock handling, ipv6 use, more
zeeshanlakhani Jun 29, 2025
5c096e9
..
zeeshanlakhani Jun 30, 2025
8167280
..
zeeshanlakhani Jun 30, 2025
91cd396
..
zeeshanlakhani Jul 1, 2025
4d6d726
..
zeeshanlakhani Jul 1, 2025
21d9274
[review] updates: locking cleanup and scoped free id(s)
zeeshanlakhani Jul 3, 2025
6f4082e
[review] keep mcast group lock for entire action
zeeshanlakhani Jul 4, 2025
9660726
[review] last bits++
zeeshanlakhani Jul 10, 2025
a3a8abb
minor: squiggly
zeeshanlakhani Jul 10, 2025
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
4 changes: 2 additions & 2 deletions .github/buildomat/jobs/image.sh
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,11 @@ pfexec chown "$UID" /out

banner "P4 Codegen"
# Add gcc-12 so the p4 compiler can find cpp
# The tofino2 has 20 stages, but the current sidecar.p4 will fit into 14. We
# The tofino2 has 20 stages, but the current sidecar.p4 will fit into 19. We
# add the "--stages 14" here to detect if/when the program grows beyond that
# limit. It's not necessarily a problem if we grow, but given the limited space
# on the ASIC, we want to grow deliberatately and thoughtfully.
PATH=/opt/gcc-12/bin:$PATH cargo xtask codegen --stages 14
PATH=/opt/gcc-12/bin:$PATH cargo xtask codegen --stages 19

# Preserve all the diagnostics spit out by the compiler
mkdir -p /out/p4c-diags
Expand Down
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ omicron-common = { git = "https://github.com/oxidecomputer/omicron", branch= "ma
oximeter = { git = "https://github.com/oxidecomputer/omicron", branch = "main" }
oximeter-producer = { git = "https://github.com/oxidecomputer/omicron", branch = "main" }
oximeter-instruments = { git = "https://github.com/oxidecomputer/omicron", branch = "main", default-features = false, features = ["kstat"] }
oxnet = { version = "0.1.0", default-features = false, features = ["schemars", "serde"] }
oxnet = { version = "0.1.1", default-features = false, features = ["schemars", "serde"] }
propolis = { git = "https://github.com/oxidecomputer/propolis" }
sled-agent-client = { git = "https://github.com/oxidecomputer/omicron", branch = "main" }
smf = { git = "https://github.com/illumos/smf-rs" }
Expand Down
15 changes: 15 additions & 0 deletions aal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,21 @@ pub trait AsicOps {
/// Destroy a multicast group.
fn mc_group_destroy(&self, group_id: u16) -> AsicResult<()>;

/// Check if a multicast group exists.
fn mc_group_exists(&self, group_id: u16) -> bool {
self.mc_domains().contains(&group_id)
}

/// Get the total number of multicast groups.
fn mc_groups_count(&self) -> AsicResult<usize>;

/// Set the maximum number of multicast nodes.
fn mc_set_max_nodes(
&self,
max_nodes: u32,
max_link_aggregated_nodes: u32,
) -> AsicResult<()>;

/// Get sidecar identifiers of the device being managed.
fn get_sidecar_identifiers(&self) -> AsicResult<impl SidecarIdentifiers>;

Expand Down
2 changes: 1 addition & 1 deletion aal/src/match_action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ impl MatchData {
/// The MatchParse trait defines the behavior needed to convert a high-level
/// Match field into our intermediate representation.
pub trait MatchParse {
/// Return all the name sand values of the key fields as strings
/// Return all the names and values of the key fields as strings
fn key_values(&self) -> BTreeMap<String, String>;
/// Convert the key Struct to a MatchData struct
fn key_to_ir(&self) -> AsicResult<MatchData>;
Expand Down
24 changes: 23 additions & 1 deletion asic/src/chaos/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ impl TableChaos {
(table::SWITCH_IPV4_ADDR, v),
(table::SWITCH_IPV6_ADDR, v),
(table::NAT_INGRESS_IPV4, v),
(table::NAT_INGRESS_IPV6, v)
(table::NAT_INGRESS_IPV6, v),
(table::MCAST_IPV4_TABLE_NAME, v),
(table::MCAST_IPV6_TABLE_NAME, v)
)
}

Expand Down Expand Up @@ -141,6 +143,8 @@ pub struct AsicConfig {
pub mc_port_remove: Chaos,
pub mc_group_create: Chaos,
pub mc_group_destroy: Chaos,
pub mc_groups_count: Chaos,
pub mc_set_max_nodes: Chaos,
pub get_sidecar_identifiers: Chaos,
pub table_new: TableChaos,
pub table_clear: TableChaos,
Expand Down Expand Up @@ -177,6 +181,8 @@ impl AsicConfig {
mc_port_remove: Chaos::new(v),
mc_group_create: Chaos::new(v),
mc_group_destroy: Chaos::new(v),
mc_groups_count: Chaos::new(v),
mc_set_max_nodes: Chaos::new(v),
get_sidecar_identifiers: Chaos::new(v),
table_new: TableChaos::uniform(v),
table_clear: TableChaos::uniform(v),
Expand All @@ -203,6 +209,7 @@ impl AsicConfig {
port_enable_get: Chaos::new(v),
connector_avail_channels: Chaos::new(v),
mc_port_count: Chaos::new(v),
mc_groups_count: Chaos::new(v),
get_sidecar_identifiers: Chaos::new(v),
..Default::default()
}
Expand All @@ -224,6 +231,7 @@ impl AsicConfig {
mc_port_remove: Chaos::new(v),
mc_group_create: Chaos::new(v),
mc_group_destroy: Chaos::new(v),
mc_set_max_nodes: Chaos::new(v),
// TODO this can cause dpd to fail to start
//table_clear: TableChaos::uniform(v),
table_default_set: TableChaos::uniform(v),
Expand Down Expand Up @@ -496,6 +504,20 @@ impl AsicOps for Handle {
Ok(())
}

fn mc_groups_count(&self) -> AsicResult<usize> {
unfurl!(self, mc_groups_count);
Ok(self.ports.lock().unwrap().len())
}

fn mc_set_max_nodes(
&self,
_max_nodes: u32,
_max_link_aggregated_nodes: u32,
) -> AsicResult<()> {
unfurl!(self, mc_set_max_nodes);
Ok(())
}

fn get_sidecar_identifiers(&self) -> AsicResult<impl SidecarIdentifiers> {
unfurl!(self, get_sidecar_identifiers);
Ok(Identifiers::default())
Expand Down
4 changes: 4 additions & 0 deletions asic/src/chaos/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ pub const SWITCH_IPV4_ADDR: &str = "pipe.Ingress.filter.switch_ipv4_addr";
pub const SWITCH_IPV6_ADDR: &str = "pipe.Ingress.filter.switch_ipv6_addr";
pub const NAT_INGRESS_IPV4: &str = "pipe.Ingress.nat_ingress.ingress_ipv4";
pub const NAT_INGRESS_IPV6: &str = "pipe.Ingress.nat_ingress.ingress_ipv6";
pub const MCAST_IPV4_TABLE_NAME: &str =
"pipe.Ingress.multicast.mcast_route_ipv4";
pub const MCAST_IPV6_TABLE_NAME: &str =
"pipe.Ingress.multicast.mcast_route_ipv6";

pub struct Table {
name: String,
Expand Down
12 changes: 12 additions & 0 deletions asic/src/softnpu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,18 @@ impl AsicOps for Handle {
Ok(())
}

fn mc_groups_count(&self) -> AsicResult<usize> {
Ok(self.ports.lock().unwrap().len())
}

fn mc_set_max_nodes(
&self,
_max_nodes: u32,
_max_link_aggregated_nodes: u32,
) -> AsicResult<()> {
Ok(())
}

fn get_sidecar_identifiers(&self) -> AsicResult<impl SidecarIdentifiers> {
Ok(Identifiers {
id: Uuid::new_v4(),
Expand Down
2 changes: 2 additions & 0 deletions asic/src/tofino_asic/imported_bf_functions
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ bf_mc_create_session
bf_mc_destroy_session
bf_mc_mgrp_create
bf_mc_mgrp_destroy
bf_mc_mgrp_get_count
bf_mc_node_create
bf_mc_node_destroy
bf_mc_node_update
bf_mc_associate_node
bf_mc_dissociate_node
bf_mc_set_max_node_threshold

# bf_rt calls
bf_rt_table_from_name_get
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,19 @@ fn mgrp_destroy(
Ok(())
}

fn mgrp_get_count(
mcast_hdl: &Handle,
dev_id: bf_dev_id_t,
mut count: u32,
) -> AsicResult<usize> {
unsafe {
bf_mc_mgrp_get_count(mcast_hdl.bf_get().mcast_hdl, dev_id, &mut count)
.check_error("getting total count of multicast groups")?;
}

Ok(count as usize)
}

fn associate_node(
mcast_hdl: bf_mc_session_hdl_t,
dev_id: bf_dev_id_t,
Expand Down Expand Up @@ -166,6 +179,26 @@ fn cleanup_node(
node_destroy(bf.mcast_hdl, bf.dev_id, port_state.node_hdl)
}

fn set_max_node_threshold(
mcast_hdl: bf_mc_session_hdl_t,
dev_id: bf_dev_id_t,
node_count: i32,
node_port_lag_count: i32,
) -> AsicResult<()> {
unsafe {
bf_mc_set_max_node_threshold(
mcast_hdl,
dev_id,
node_count,
node_port_lag_count,
)
.check_error("setting max node threshold")?;
}

Ok(())
}

/// All multicast domains.
pub fn domains(hdl: &Handle) -> Vec<u16> {
let mut list = Vec::new();
let domains = hdl.domains.lock().unwrap();
Expand All @@ -190,6 +223,7 @@ fn domain_ports(domain: &DomainState) -> Vec<u16> {
list
}

/// Get the number of ports in a multicast domain.
pub fn domain_port_count(hdl: &Handle, group_id: u16) -> AsicResult<usize> {
let mut domains = hdl.domains.lock().unwrap();
match domains.get_mut(&group_id) {
Expand All @@ -198,6 +232,7 @@ pub fn domain_port_count(hdl: &Handle, group_id: u16) -> AsicResult<usize> {
}
}

/// Add a port to a multicast domain.
pub fn domain_add_port(
hdl: &Handle,
group_id: u16,
Expand Down Expand Up @@ -259,6 +294,7 @@ pub fn domain_add_port(
}
}

/// Remove a port from a multicast domain.
pub fn domain_remove_port(
hdl: &Handle,
group_id: u16,
Expand Down Expand Up @@ -287,6 +323,7 @@ pub fn domain_remove_port(
Ok(())
}

/// Create a multicast domain.
pub fn domain_create(hdl: &Handle, group_id: u16) -> AsicResult<()> {
info!(hdl.log, "creating multicast domain {}", group_id);
let mut domains = hdl.domains.lock().unwrap();
Expand All @@ -308,6 +345,7 @@ pub fn domain_create(hdl: &Handle, group_id: u16) -> AsicResult<()> {
Ok(())
}

/// Destroy a multicast domain.
pub fn domain_destroy(hdl: &Handle, group_id: u16) -> AsicResult<()> {
info!(hdl.log, "destroying multicast domain {}", group_id);
let mut domains = hdl.domains.lock().unwrap();
Expand All @@ -333,3 +371,30 @@ pub fn domain_destroy(hdl: &Handle, group_id: u16) -> AsicResult<()> {

mgrp_destroy(bf.mcast_hdl, bf.dev_id, domain.mgrp_hdl)
}

/// Domain exists.
pub fn domain_exists(hdl: &Handle, group_id: u16) -> bool {
let domains = hdl.domains.lock().unwrap();
domains.contains_key(&group_id)
}

/// Get the total number of multicast domains.
pub fn domains_count(hdl: &Handle) -> AsicResult<usize> {
let bf = hdl.bf_get();
mgrp_get_count(hdl, bf.dev_id, 0)
}

/// Set the maximum number of multicast nodes.
pub fn set_max_nodes(
hdl: &Handle,
node_count: u32,
node_port_lag_count: u32,
) -> AsicResult<()> {
let bf = hdl.bf_get();
set_max_node_threshold(
bf.mcast_hdl,
bf.dev_id,
node_count as i32,
node_port_lag_count as i32,
)
}
34 changes: 25 additions & 9 deletions asic/src/tofino_asic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ mod bf_wrapper;
mod genpd;

mod link_fsm;
pub mod multicast;
pub mod mcast;
pub mod ports;
pub mod qsfp;
mod sde_log;
Expand Down Expand Up @@ -150,27 +150,43 @@ impl AsicOps for Handle {
}

fn mc_domains(&self) -> Vec<u16> {
multicast::domains(self)
mcast::domains(self)
}

fn mc_port_count(&self, group_id: u16) -> AsicResult<usize> {
multicast::domain_port_count(self, group_id)
mcast::domain_port_count(self, group_id)
}

fn mc_port_add(&self, group_id: u16, port: u16) -> AsicResult<()> {
multicast::domain_add_port(self, group_id, port)
mcast::domain_add_port(self, group_id, port)
}

fn mc_port_remove(&self, group_id: u16, port: u16) -> AsicResult<()> {
multicast::domain_remove_port(self, group_id, port)
mcast::domain_remove_port(self, group_id, port)
}

fn mc_group_create(&self, group_id: u16) -> AsicResult<()> {
multicast::domain_create(self, group_id)
mcast::domain_create(self, group_id)
}

fn mc_group_destroy(&self, group_id: u16) -> AsicResult<()> {
multicast::domain_destroy(self, group_id)
mcast::domain_destroy(self, group_id)
}

fn mc_group_exists(&self, group_id: u16) -> bool {
mcast::domain_exists(self, group_id)
}

fn mc_groups_count(&self) -> AsicResult<usize> {
mcast::domains_count(self)
}

fn mc_set_max_nodes(
&self,
max_nodes: u32,
max_link_aggregated_nodes: u32,
) -> AsicResult<()> {
mcast::set_max_nodes(self, max_nodes, max_link_aggregated_nodes)
}

// Ideally we would get some sort of sidecar-level ID from the FRUID.
Expand Down Expand Up @@ -238,7 +254,7 @@ pub struct Handle {
rt: tofino_common::BfRt,
log: slog::Logger,
phys_ports: Mutex<ports::PortData>,
domains: Mutex<HashMap<u16, multicast::DomainState>>,
domains: Mutex<HashMap<u16, mcast::DomainState>>,
eth_connector_id: Option<u32>,
}

Expand Down Expand Up @@ -322,7 +338,7 @@ impl Handle {

// Note: we assume that bf_mc_init() has been called as part of the
// bf_switch_init() operation.
bf.mcast_hdl = multicast::create_session()?;
bf.mcast_hdl = mcast::create_session()?;

Ok(Handle {
dev_id,
Expand Down
Loading