Skip to content

Add allow_ddm_traffic parameter for link creation - #346

Merged
andrewjstone merged 8 commits into
mainfrom
ajs/ddm-link-settings
Aug 21, 2026
Merged

Add allow_ddm_traffic parameter for link creation#346
andrewjstone merged 8 commits into
mainfrom
ajs/ddm-link-settings

Conversation

@andrewjstone

Copy link
Copy Markdown
Contributor

Plumb this through such front ports can run ddm for multirack.

Plumb this through such front ports can run ddm for multirack.
Comment thread dpd-types/versions/src/allow_ddm_traffic/link.rs
Comment thread dpd-types/versions/src/allow_ddm_traffic/link.rs
Comment thread dpd/src/link.rs Outdated

@taspelund taspelund left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks pretty straightforward to me. Just one typo and a couple questions about struct naming + which struct owns the new param.

@cfzimmerman cfzimmerman left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Lgtm!

I appreciate this is time sensitive, so feel free to merge. However I'm curious about validation precedent.

Given more time, how might we test this? What do you think is warranted validation for a change like this? Here are some ideas:

  • A basic dpd-client test could apply these settings and verify that uplink == true on the resulting link. This is pretty low value, but I guess it would help something like a messy git resolution or big refactor.
  • Do we have any existing regression tests on the omicron/dendrite API? Guessing these would live on the omicron side?
  • I feel that we should eventually have an automated CI test somewhere for multirack networking. Since it combines (at least) omicron, maghemite, and dendrite systems, would we orchestrate this on a4x2/voxel? Do we have precedent/infra for this kind of automated testing?

Comment thread dpd-api/src/lib.rs
Comment on lines +1670 to +1678
/**
* Apply port settings atomically.
*
* These settings will be applied holistically, and to the extent possible
* atomically to a given port. In the event of a failure a rollback is
* attempted. If the rollback fails there will be inconsistent state. This
* failure mode returns the error code "rollback failure". For more details see
* the docs on the [`PortSettings`] type.
*/

@cfzimmerman cfzimmerman Aug 19, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Is there a convention for when we use /** ... */ versus /// doc comments? This module has both. Aesthetically, sticking to one is nice. Feel like I don't see /** */ in the wild very often.

But you don't need to change this. Just curious.

@cfzimmerman cfzimmerman Aug 19, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

That said, the contents of the comment are great. When thinking of future design, it would be nice to never need "inconsistent state" in documentation 😅

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This comment was part of the original docs and just copied verbatim to the new version of the API. I personally think we should use /// everywhere. But in this case it didn't make sense for me to modify it.

@andrewjstone

Copy link
Copy Markdown
Contributor Author

Lgtm!

I appreciate this is time sensitive, so feel free to merge. However I'm curious about validation precedent.

Given more time, how might we test this? What do you think is warranted validation for a change like this? Here are some ideas:

* A basic dpd-client test could apply these settings and verify that `uplink == true` on the resulting link. This is pretty low value, but I guess it would help something like a messy git resolution or big refactor.

* Do we have any existing regression tests on the omicron/dendrite API? Guessing these would live on the omicron side?

* I feel that we should eventually have an automated CI test somewhere for multirack networking. Since it combines (at least) omicron, maghemite, and dendrite systems, would we orchestrate this on a4x2/voxel? Do we have precedent/infra for this kind of automated testing?

Thanks for taking a look Cory. I'm in the process of testing this in voxel, although it requires a few more changes in omicron to do so. I'm not going to merge until all the related PRs are ready to go.

Besides that I have no idea how to test this. I don't know what tests do or don't exist and what people normally do for changes like this.

@cfzimmerman

Copy link
Copy Markdown

Besides that I have no idea how to test this. I don't know what tests do or don't exist and what people normally do for changes like this.

Sgtm, thx for clarifying. Figuring this out and adding any documentation or tooling to standardize dendrite PR validation is on my to-do list.

@nicolaskagami nicolaskagami left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Found a few problems that need looking into:

  • Easy: new LinkSpec field but no modify_link change.
  • Easy: I think we want to pass it through in api_server.rs, no?
  • Annoying: I thought it was wrong to set it to false for rear ports, but as I mentioned on another comment I think the problem is that the documented semantics don't match the implementation. It currently only seems to apply for Qsfp, more specifically just setting Ipv6 and disabling uplink. At the very least we need to communicate this better for maintainability.

Comment thread dpd/src/main.rs
kr: true,
lane: Some(dpd_types::link::LinkId(0)),
tx_eq: None,
allow_ddm_traffic: false,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Shouldn't DDM run on rear ports?

Comment thread dpd/src/api_server.rs Outdated
autoneg: l.config.autoneg,
kr: l.config.kr,
tx_eq: l.tx_eq,
allow_ddm_traffic: false,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Shouldn't we pass it through here?

Suggested change
allow_ddm_traffic: false,
allow_ddm_traffic: l.config.allow_ddm_traffic,

Comment on lines +43 to +47
/// Whether DDM traffic is allowed on this link.
///
/// This defaults to `false`.
#[serde(default)]
pub allow_ddm_traffic: bool,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think the semantics are not matching the behavior. allow_ddm_traffic: false still allows DDM traffic is it isn't a Qsfp port. We should either change the description (and name) to match the current behavior or the other way around.

Comment thread dpd/src/port_settings.rs
pub ipv4: BTreeSet<Ipv4Addr>,
pub ipv6: BTreeSet<Ipv6Addr>,
pub tx_eq: Option<TxEq>,
pub allow_ddm_traffic: bool,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We're adding a new field here but the modify_link wasn't changed to apply it. It would definitely benefit from a bit of a refactor, or at least a destructuring pattern, so we'd get compilation errors next time this happens.

Comment thread dpd/src/link.rs
Comment on lines +424 to +435
//
// For multirack ddm traffic on the front ports we must also enable
// ipv6.
let ipv6_enabled =
!matches!(port_id, PortId::Qsfp(_)) || params.allow_ddm_traffic;
// By default we expect external-facing links to be used as uplinks and
// internal-facing links for backplane traffic.
let uplink = matches!(port_id, PortId::Qsfp(_));
//
// We need to disable uplinks for multirack ddm traffic on the front
// ports.
let uplink =
matches!(port_id, PortId::Qsfp(_)) && !params.allow_ddm_traffic;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this the only place where allow_ddm_traffic effects change? If so, it seems to only affect Qsfp ports, which is different semantics than what it's described as.

@nicolaskagami

Copy link
Copy Markdown
Contributor

Applied the two "easy" fixes (after talking to @andrewjstone). There's a bunch around that could be improved long-term. Particularly:

  • There's some duplication of business logic going on.
  • The whole Modify<_> could be simplified and made more robust.

@andrewjstone

Copy link
Copy Markdown
Contributor Author

Thanks for the fixes @nicolaskagami. Much appreciated.

@andrewjstone

Copy link
Copy Markdown
Contributor Author

@nicolaskagami I think I updated all the relevant comments to reflect reality.

@nicolaskagami nicolaskagami left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for bearing with me!

Comment thread dpd-api/src/lib.rs
Comment on lines 720 to 724
@@ -721,6 +723,25 @@ pub trait DpdApi {
params: TypedBody<latest::link::LinkCreate>,
) -> Result<HttpResponseCreated<latest::link::LinkId>, HttpError>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think the guidance in RFD 619 is to always use fully qualified type versions in the dropshot trait, rather than latest. For the endpoints you're bumping, I would also change the signature to use the specific version in the path i.e. v13::link::LinkCreate

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We only use specif versions for prior prior versions of the API. The latest version should use a floating identifier .

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

FWIW, the claude skill in omicron handled this correctly.

@andrewjstone

Copy link
Copy Markdown
Contributor Author

Good news, this appears to work correctly. With a small change to voxel, which brings up an uplink on qsfp0/0 and interconnect port on qsfp2/0, we see the settings correctly set in dpd.

Here's the relevant voxel patch in voxel/src/rss_request.rs:

@@ -132,6 +133,7 @@ fn uplink_port(p: &UplinkPort, mode: RouterMode) -> Result<PortConfig> {
         autoneg: false,
         lldp: Some(lldp(&p.switch, &p.lldp)),
         tx_eq: None,
+        allow_ddm_traffic: false,
     })
 }

@@ -152,6 +154,7 @@ fn interconnect_port(switch: &str, port: &str) -> Result<PortConfig> {
         autoneg: false,
         lldp: Some(lldp(switch, &format!("interconnect-{port}"))),
         tx_eq: None,
+        allow_ddm_traffic: true,
     })
 }

That triggers the PortConfig in the RackNetworkConfig for those ports to be send to RSS which puts them in the bootstore. The dpd-reconciler reads them and calls port_settiings_apply on the dpd client.

Looking at the output after RSS we see:

root@oxz_switch:~# swadm link get-prop qsfp0/0 uplink
true
root@oxz_switch:~# swadm link get-prop qsfp0/0 ipv6-enabled
false
root@oxz_switch:~# swadm link get-prop qsfp2/0 uplink
false
root@oxz_switch:~# swadm link get-prop qsfp2/0 ipv6-enabled
true

@andrewjstone
andrewjstone merged commit 2df9101 into main Aug 21, 2026
6 checks passed
@andrewjstone
andrewjstone deleted the ajs/ddm-link-settings branch August 21, 2026 15:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants