From 5736dabfe61fe54af4b6fa9473de4c470d8edb7c Mon Sep 17 00:00:00 2001 From: Nils Nieuwejaar Date: Fri, 1 Nov 2024 16:54:51 -0400 Subject: [PATCH] Allow omicron to configure per-link tx_eq settings (#6914) --- clients/dpd-client/build.rs | 18 +---- clients/sled-agent-client/src/lib.rs | 1 + common/src/api/external/mod.rs | 35 ++++++++ common/src/api/internal/shared.rs | 28 ++++++- nexus/db-model/src/schema.rs | 12 +++ nexus/db-model/src/schema_versions.rs | 3 +- nexus/db-model/src/switch_port.rs | 52 ++++++++++++ .../src/db/datastore/switch_port.rs | 80 ++++++++++++++++++- nexus/src/app/background/tasks/networking.rs | 15 +++- .../tasks/sync_switch_configuration.rs | 31 ++++++- nexus/src/app/rack.rs | 5 +- nexus/tests/integration_tests/switch_port.rs | 1 + nexus/types/src/external_api/params.rs | 5 +- openapi/bootstrap-agent.json | 45 +++++++++++ openapi/nexus-internal.json | 45 +++++++++++ openapi/nexus.json | 64 +++++++++++++++ openapi/sled-agent.json | 53 ++++++++++++ openapi/wicketd.json | 45 +++++++++++ package-manifest.toml | 12 +-- schema/crdb/add-tx-eq/up1.sql | 4 + schema/crdb/add-tx-eq/up2.sql | 8 ++ schema/crdb/dbinit.sql | 12 ++- schema/rss-sled-plan.json | 57 +++++++++++++ sled-agent/src/bootstrap/early_networking.rs | 9 ++- sled-agent/src/rack_setup/service.rs | 9 +++ .../tests/integration_tests/early_network.rs | 1 + .../madrid-rss-sled-plan.json | 6 +- sled-agent/types/src/early_networking.rs | 4 + tools/dendrite_openapi_version | 4 +- tools/dendrite_stub_checksums | 6 +- tools/permslip_production | 2 +- wicket-common/src/example.rs | 28 +++++-- wicket-common/src/rack_setup.rs | 3 + wicket/src/cli/rack_setup/config_toml.rs | 29 +++++++ wicket/src/ui/panes/rack_setup.rs | 40 ++++++++++ wicket/tests/output/example_non_empty.toml | 14 ++++ wicketd/src/preflight_check/uplink.rs | 1 + wicketd/src/rss_config.rs | 8 ++ 38 files changed, 742 insertions(+), 53 deletions(-) create mode 100644 schema/crdb/add-tx-eq/up1.sql create mode 100644 schema/crdb/add-tx-eq/up2.sql diff --git a/clients/dpd-client/build.rs b/clients/dpd-client/build.rs index 02a685632c..313c1a452f 100644 --- a/clients/dpd-client/build.rs +++ b/clients/dpd-client/build.rs @@ -15,7 +15,6 @@ use anyhow::Context; use anyhow::Result; use omicron_zone_package::config::Config; use omicron_zone_package::package::PackageSource; -use progenitor::TypePatch; use quote::quote; use std::env; use std::fs; @@ -89,22 +88,7 @@ fn main() -> Result<()> { slog::debug!(state.log, "client response"; "result" => ?result); } }) - .with_patch("LinkId", &TypePatch::default() - .with_derive("Eq") - .with_derive("PartialEq") - ) - .with_patch("LinkCreate", &TypePatch::default() - .with_derive("Eq") - .with_derive("PartialEq") - ) - .with_patch("LinkSettings", &TypePatch::default() - .with_derive("Eq") - .with_derive("PartialEq") - ) - .with_patch("PortSettings", &TypePatch::default() - .with_derive("Eq") - .with_derive("PartialEq") - ) + .with_derive("PartialEq") ) .generate_tokens(&spec) .with_context(|| { diff --git a/clients/sled-agent-client/src/lib.rs b/clients/sled-agent-client/src/lib.rs index 304a93439a..0398e15e96 100644 --- a/clients/sled-agent-client/src/lib.rs +++ b/clients/sled-agent-client/src/lib.rs @@ -31,6 +31,7 @@ progenitor::generate_api!( BgpConfig = { derives = [Eq, Hash] }, BgpPeerConfig = { derives = [Eq, Hash] }, LldpPortConfig = { derives = [Eq, Hash, PartialOrd, Ord] }, + TxEqConfig = { derives = [Eq, Hash] }, OmicronPhysicalDiskConfig = { derives = [Eq, Hash, PartialOrd, Ord] }, PortConfigV2 = { derives = [Eq, Hash] }, RouteConfig = { derives = [Eq, Hash] }, diff --git a/common/src/api/external/mod.rs b/common/src/api/external/mod.rs index 419e5e94c9..18343ac44a 100644 --- a/common/src/api/external/mod.rs +++ b/common/src/api/external/mod.rs @@ -2358,6 +2358,10 @@ pub struct SwitchPortSettingsView { /// Link-layer discovery protocol (LLDP) settings. pub link_lldp: Vec, + /// TX equalization settings. These are optional, and most links will not + /// need them. + pub tx_eq: Vec>, + /// Layer 3 interface settings. pub interfaces: Vec, @@ -2500,6 +2504,9 @@ pub struct SwitchPortLinkConfig { /// link. pub lldp_link_config_id: Option, + /// The tx_eq configuration id for this link. + pub tx_eq_config_id: Option, + /// The name of this link. pub link_name: String, @@ -2544,6 +2551,34 @@ pub struct LldpLinkConfig { pub management_ip: Option, } +/// Per-port tx-eq overrides. This can be used to fine-tune the transceiver +/// equalization settings to improve signal integrity. +#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize, PartialEq)] +pub struct TxEqConfig { + /// Pre-cursor tap1 + pub pre1: Option, + /// Pre-cursor tap2 + pub pre2: Option, + /// Main tap + pub main: Option, + /// Post-cursor tap2 + pub post2: Option, + /// Post-cursor tap1 + pub post1: Option, +} + +impl From for TxEqConfig { + fn from(x: crate::api::internal::shared::TxEqConfig) -> TxEqConfig { + TxEqConfig { + pre1: x.pre1, + pre2: x.pre2, + main: x.main, + post2: x.post2, + post1: x.post1, + } + } +} + /// Describes the kind of an switch interface. #[derive(Clone, Debug, Deserialize, JsonSchema, Serialize, PartialEq)] #[serde(rename_all = "snake_case")] diff --git a/common/src/api/internal/shared.rs b/common/src/api/internal/shared.rs index a5d93ada2b..5b1a528b36 100644 --- a/common/src/api/internal/shared.rs +++ b/common/src/api/internal/shared.rs @@ -458,6 +458,24 @@ pub struct LldpPortConfig { pub management_addrs: Option>, } +/// Per-port tx-eq overrides. This can be used to fine-tune the transceiver +/// equalization settings to improve signal integrity. +#[derive( + Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq, JsonSchema, +)] +pub struct TxEqConfig { + /// Pre-cursor tap1 + pub pre1: Option, + /// Pre-cursor tap2 + pub pre2: Option, + /// Main tap + pub main: Option, + /// Post-cursor tap2 + pub post2: Option, + /// Post-cursor tap1 + pub post1: Option, +} + #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, JsonSchema)] pub struct PortConfigV2 { /// The set of routes associated with this port. @@ -479,6 +497,8 @@ pub struct PortConfigV2 { pub autoneg: bool, /// LLDP configuration for this port pub lldp: Option, + /// TX-EQ configuration for this port + pub tx_eq: Option, } /// A set of switch uplinks. @@ -497,11 +517,17 @@ pub struct HostPortConfig { pub addrs: Vec, pub lldp: Option, + pub tx_eq: Option, } impl From for HostPortConfig { fn from(x: PortConfigV2) -> Self { - Self { port: x.port, addrs: x.addresses, lldp: x.lldp.clone() } + Self { + port: x.port, + addrs: x.addresses, + lldp: x.lldp.clone(), + tx_eq: x.tx_eq, + } } } diff --git a/nexus/db-model/src/schema.rs b/nexus/db-model/src/schema.rs index aea9c6a6bd..8a97e43639 100644 --- a/nexus/db-model/src/schema.rs +++ b/nexus/db-model/src/schema.rs @@ -145,6 +145,7 @@ table! { speed -> crate::SwitchLinkSpeedEnum, autoneg -> Bool, lldp_link_config_id -> Nullable, + tx_eq_config_id -> Nullable, } } @@ -164,6 +165,17 @@ table! { } } +table! { + tx_eq_config (id) { + id -> Uuid, + pre1 -> Nullable, + pre2 -> Nullable, + main -> Nullable, + post2 -> Nullable, + post1 -> Nullable, + } +} + table! { switch_port_settings_interface_config (id) { port_settings_id -> Uuid, diff --git a/nexus/db-model/src/schema_versions.rs b/nexus/db-model/src/schema_versions.rs index bc43152ace..14eba1cb1c 100644 --- a/nexus/db-model/src/schema_versions.rs +++ b/nexus/db-model/src/schema_versions.rs @@ -17,7 +17,7 @@ use std::collections::BTreeMap; /// /// This must be updated when you change the database schema. Refer to /// schema/crdb/README.adoc in the root of this repository for details. -pub const SCHEMA_VERSION: SemverVersion = SemverVersion::new(112, 0, 0); +pub const SCHEMA_VERSION: SemverVersion = SemverVersion::new(113, 0, 0); /// List of all past database schema versions, in *reverse* order /// @@ -29,6 +29,7 @@ static KNOWN_VERSIONS: Lazy> = Lazy::new(|| { // | leaving the first copy as an example for the next person. // v // KnownVersion::new(next_int, "unique-dirname-with-the-sql-files"), + KnownVersion::new(113, "add-tx-eq"), KnownVersion::new(112, "blueprint-dataset"), KnownVersion::new(111, "drop-omicron-zone-underlay-address"), KnownVersion::new(110, "clickhouse-policy"), diff --git a/nexus/db-model/src/switch_port.rs b/nexus/db-model/src/switch_port.rs index 2420482cce..2db9ef04df 100644 --- a/nexus/db-model/src/switch_port.rs +++ b/nexus/db-model/src/switch_port.rs @@ -11,6 +11,7 @@ use crate::schema::{ switch_port_settings_group, switch_port_settings_groups, switch_port_settings_interface_config, switch_port_settings_link_config, switch_port_settings_port_config, switch_port_settings_route_config, + tx_eq_config, }; use crate::{impl_enum_type, SqlU32}; use crate::{SqlU16, SqlU8}; @@ -387,9 +388,11 @@ pub struct SwitchPortLinkConfig { pub fec: SwitchLinkFec, pub speed: SwitchLinkSpeed, pub autoneg: bool, + pub tx_eq_config_id: Option, } impl SwitchPortLinkConfig { + #[allow(clippy::too_many_arguments)] pub fn new( port_settings_id: Uuid, lldp_link_config_id: Uuid, @@ -398,6 +401,7 @@ impl SwitchPortLinkConfig { fec: SwitchLinkFec, speed: SwitchLinkSpeed, autoneg: bool, + tx_eq_config_id: Option, ) -> Self { Self { port_settings_id, @@ -407,6 +411,7 @@ impl SwitchPortLinkConfig { speed, autoneg, mtu: mtu.into(), + tx_eq_config_id, } } } @@ -416,6 +421,7 @@ impl Into for SwitchPortLinkConfig { external::SwitchPortLinkConfig { port_settings_id: self.port_settings_id, lldp_link_config_id: self.lldp_link_config_id, + tx_eq_config_id: self.tx_eq_config_id, link_name: self.link_name.clone(), mtu: self.mtu.into(), fec: self.fec.into(), @@ -494,6 +500,52 @@ impl Into for LldpLinkConfig { } } +#[derive( + Queryable, + Insertable, + Selectable, + Clone, + Debug, + Serialize, + Deserialize, + AsChangeset, +)] +#[diesel(table_name = tx_eq_config)] +pub struct TxEqConfig { + pub id: Uuid, + pub pre1: Option, + pub pre2: Option, + pub main: Option, + pub post2: Option, + pub post1: Option, +} + +impl TxEqConfig { + pub fn new( + pre1: Option, + pre2: Option, + main: Option, + post2: Option, + post1: Option, + ) -> Self { + Self { id: Uuid::new_v4(), pre1, pre2, main, post2, post1 } + } +} + +// This converts the internal database version of the config into the +// user-facing version. +impl Into for TxEqConfig { + fn into(self) -> external::TxEqConfig { + external::TxEqConfig { + pre1: self.pre1, + pre2: self.pre2, + main: self.main, + post2: self.post2, + post1: self.post1, + } + } +} + #[derive( Queryable, Insertable, diff --git a/nexus/db-queries/src/db/datastore/switch_port.rs b/nexus/db-queries/src/db/datastore/switch_port.rs index 2a0f47f0f6..e0092459b5 100644 --- a/nexus/db-queries/src/db/datastore/switch_port.rs +++ b/nexus/db-queries/src/db/datastore/switch_port.rs @@ -19,7 +19,7 @@ use crate::db::model::{ SwitchPortAddressConfig, SwitchPortBgpPeerConfig, SwitchPortConfig, SwitchPortLinkConfig, SwitchPortRouteConfig, SwitchPortSettings, SwitchPortSettingsGroup, SwitchPortSettingsGroups, - SwitchVlanInterfaceConfig, + SwitchVlanInterfaceConfig, TxEqConfig, }; use crate::db::pagination::paginated; use crate::transaction_retry::OptionalError; @@ -102,6 +102,7 @@ pub struct SwitchPortSettingsCombinedResult { pub port: SwitchPortConfig, pub links: Vec, pub link_lldp: Vec, + pub tx_eq: Vec>, pub interfaces: Vec, pub vlan_interfaces: Vec, pub routes: Vec, @@ -117,6 +118,7 @@ impl SwitchPortSettingsCombinedResult { groups: Vec::new(), links: Vec::new(), link_lldp: Vec::new(), + tx_eq: Vec::new(), interfaces: Vec::new(), vlan_interfaces: Vec::new(), routes: Vec::new(), @@ -136,6 +138,11 @@ impl Into groups: self.groups.into_iter().map(Into::into).collect(), links: self.links.into_iter().map(Into::into).collect(), link_lldp: self.link_lldp.into_iter().map(Into::into).collect(), + tx_eq: self + .tx_eq + .into_iter() + .map(|t| if let Some(t) = t { Some(t.into()) } else { None }) + .collect(), interfaces: self.interfaces.into_iter().map(Into::into).collect(), vlan_interfaces: self .vlan_interfaces @@ -466,6 +473,31 @@ impl DataStore { .load_async::(&conn) .await?; + let tx_eq_ids_and_nulls :Vec>= result + .links + .iter() + .map(|link| link.tx_eq_config_id) + .collect(); + let tx_eq_ids: Vec = tx_eq_ids_and_nulls + .iter() + .cloned() + .flatten() + .collect(); + + use db::schema::tx_eq_config; + let configs = tx_eq_config::dsl::tx_eq_config + .filter(tx_eq_config::id.eq_any(tx_eq_ids)) + .select(TxEqConfig::as_select()) + .limit(1) + .load_async::(&conn) + .await?; + result.tx_eq = tx_eq_ids_and_nulls.iter().map(|x| + if let Some(id) = x { + configs.iter().find(|c| c.id == *id).cloned() + } else { + None + }).collect(); + // get the interface configs use db::schema::switch_port_settings_interface_config::{ self as interface_config, dsl as interface_config_dsl, @@ -887,12 +919,15 @@ impl DataStore { Ok(v) => Ok(Some(v)), Err(e) => { let msg = "failed to check if bgp peer exists in switch port settings"; - error!(opctx.log, "{msg}"; "error" => ?e); match e { diesel::result::Error::NotFound => { + debug!(opctx.log, "{msg}"; "error" => ?e); Ok(None) - } - _ => Err(err.bail(Error::internal_error(msg))), + }, + _ => { + error!(opctx.log, "{msg}"; "error" => ?e); + Err(err.bail(Error::internal_error(msg))) + } } } }?; @@ -1112,6 +1147,7 @@ async fn do_switch_port_settings_create( switch_port_settings_port_config::dsl as port_config_dsl, switch_port_settings_route_config::dsl as route_config_dsl, switch_vlan_interface_config::dsl as vlan_config_dsl, + tx_eq_config::dsl as tx_eq_config_dsl, }; // create the top level port settings object @@ -1146,6 +1182,7 @@ async fn do_switch_port_settings_create( port: db_port_config, links: Vec::new(), link_lldp: Vec::new(), + tx_eq: Vec::new(), interfaces: Vec::new(), vlan_interfaces: Vec::new(), routes: Vec::new(), @@ -1158,6 +1195,7 @@ async fn do_switch_port_settings_create( let mut lldp_config = Vec::with_capacity(params.links.len()); let mut link_config = Vec::with_capacity(params.links.len()); + let mut tx_eq_config = Vec::with_capacity(params.links.len()); for (link_name, c) in ¶ms.links { let lldp_link_config = LldpLinkConfig::new( @@ -1172,6 +1210,20 @@ async fn do_switch_port_settings_create( let lldp_config_id = lldp_link_config.id; lldp_config.push(lldp_link_config); + let tx_eq_config_id = match &c.tx_eq { + Some(t) => { + let config = + TxEqConfig::new(t.pre1, t.pre2, t.main, t.post2, t.post1); + let tx_eq_config_id = config.id; + tx_eq_config.push(Some(config)); + Some(tx_eq_config_id) + } + _ => { + tx_eq_config.push(None); + None + } + }; + link_config.push(SwitchPortLinkConfig::new( psid, lldp_config_id, @@ -1180,6 +1232,7 @@ async fn do_switch_port_settings_create( c.fec.into(), c.speed.into(), c.autoneg, + tx_eq_config_id, )); } result.link_lldp = @@ -1189,6 +1242,16 @@ async fn do_switch_port_settings_create( .get_results_async(conn) .await?; + // We want to insert the Some(config) values into the table, but preserve the + // full vector of None/Some values. + let v: Vec = tx_eq_config.iter().flatten().cloned().collect(); + let _ = diesel::insert_into(tx_eq_config_dsl::tx_eq_config) + .values(v) + .returning(TxEqConfig::as_returning()) + .get_results_async(conn) + .await?; + result.tx_eq = tx_eq_config; + result.links = diesel::insert_into(link_config_dsl::switch_port_settings_link_config) .values(link_config) @@ -1517,6 +1580,15 @@ async fn do_switch_port_settings_delete( .execute_async(conn) .await?; + // delete tx_eq configs + use db::schema::tx_eq_config; + let tx_eq_ids: Vec = + links.iter().filter_map(|link| link.tx_eq_config_id).collect(); + diesel::delete(tx_eq_config::dsl::tx_eq_config) + .filter(tx_eq_config::id.eq_any(tx_eq_ids)) + .execute_async(conn) + .await?; + // delete interface configs use db::schema::switch_port_settings_interface_config::{ self as sps_interface_config, dsl as interface_config_dsl, diff --git a/nexus/src/app/background/tasks/networking.rs b/nexus/src/app/background/tasks/networking.rs index 95005d07cd..ed27409c9b 100644 --- a/nexus/src/app/background/tasks/networking.rs +++ b/nexus/src/app/background/tasks/networking.rs @@ -4,7 +4,7 @@ use db::datastore::SwitchPortSettingsCombinedResult; use dpd_client::types::{ - LinkCreate, LinkId, LinkSettings, PortFec, PortSettings, PortSpeed, + LinkCreate, LinkId, LinkSettings, PortFec, PortSettings, PortSpeed, TxEq, }; use nexus_db_model::{SwitchLinkFec, SwitchLinkSpeed}; use nexus_db_queries::db; @@ -63,7 +63,17 @@ pub(crate) fn api_to_dpd_port_settings( //TODO breakouts let link_id = LinkId(0); - + let tx_eq = if let Some(Some(t)) = settings.tx_eq.get(0) { + Some(TxEq { + pre1: t.pre1.map(Into::into), + pre2: t.pre2.map(Into::into), + main: t.main.map(Into::into), + post2: t.post2.map(Into::into), + post1: t.post2.map(Into::into), + }) + } else { + None + }; for l in settings.links.iter() { dpd_port_settings.links.insert( link_id.to_string(), @@ -72,6 +82,7 @@ pub(crate) fn api_to_dpd_port_settings( autoneg: l.autoneg, lane: Some(LinkId(0)), kr: false, + tx_eq: tx_eq.clone(), fec: match l.fec { SwitchLinkFec::Firecode => PortFec::Firecode, SwitchLinkFec::Rs => PortFec::Rs, diff --git a/nexus/src/app/background/tasks/sync_switch_configuration.rs b/nexus/src/app/background/tasks/sync_switch_configuration.rs index 49dbfb2e52..ceacae7645 100644 --- a/nexus/src/app/background/tasks/sync_switch_configuration.rs +++ b/nexus/src/app/background/tasks/sync_switch_configuration.rs @@ -53,7 +53,7 @@ use sled_agent_client::types::{ BgpConfig as SledBgpConfig, BgpPeerConfig as SledBgpPeerConfig, EarlyNetworkConfig, EarlyNetworkConfigBody, HostPortConfig, LldpAdminStatus, LldpPortConfig, PortConfigV2, RackNetworkConfigV2, - RouteConfig as SledRouteConfig, UplinkAddressConfig, + RouteConfig as SledRouteConfig, TxEqConfig, UplinkAddressConfig, }; use std::{ collections::{hash_map::Entry, HashMap, HashSet}, @@ -935,6 +935,19 @@ impl BackgroundTask for SwitchPortSettingsManager { }, }; + // TODO https://github.com/oxidecomputer/omicron/issues/3062 + let tx_eq = if let Some(Some(c)) = info.tx_eq.get(0) { + Some(TxEqConfig { + pre1: c.pre1.map(Into::into), + pre2: c.pre2.map(Into::into), + main: c.main.map(Into::into), + post2: c.post2.map(Into::into), + post1: c.post1.map(Into::into), + }) + } else { + None + }; + let mut port_config = PortConfigV2 { addresses: info.addresses.iter().map(|a| UplinkAddressConfig { @@ -1012,7 +1025,8 @@ impl BackgroundTask for SwitchPortSettingsManager { system_name: c.system_name.clone(), system_description: c.system_description.clone(), management_addrs:c.management_ip.map(|a| vec![a.ip()]), - }) + }), + tx_eq, } ; @@ -1456,6 +1470,18 @@ fn uplinks( }) }; + let tx_eq = if let Some(Some(c)) = config.tx_eq.get(0) { + Some(TxEqConfig { + pre1: c.pre1, + pre2: c.pre2, + main: c.main, + post2: c.post2, + post1: c.post1, + }) + } else { + None + }; + let config = HostPortConfig { port: port.port_name.clone(), addrs: config @@ -1467,6 +1493,7 @@ fn uplinks( }) .collect(), lldp, + tx_eq, }; match uplinks.entry(*location) { diff --git a/nexus/src/app/rack.rs b/nexus/src/app/rack.rs index 9a361d564d..a6e38e8dcf 100644 --- a/nexus/src/app/rack.rs +++ b/nexus/src/app/rack.rs @@ -650,12 +650,15 @@ impl super::Nexus { }, }, }; + let link = LinkConfigCreate { - mtu: 1500, //TODO https://github.com/oxidecomputer/omicron/issues/2274 + //TODO https://github.com/oxidecomputer/omicron/issues/2274 + mtu: 1500, fec: uplink_config.uplink_port_fec.into(), speed: uplink_config.uplink_port_speed.into(), autoneg: uplink_config.autoneg, lldp, + tx_eq: uplink_config.tx_eq.map(|t| t.into()), }; port_settings_params.links.insert("phy".to_string(), link); diff --git a/nexus/tests/integration_tests/switch_port.rs b/nexus/tests/integration_tests/switch_port.rs index 6500d7249a..9ce686db56 100644 --- a/nexus/tests/integration_tests/switch_port.rs +++ b/nexus/tests/integration_tests/switch_port.rs @@ -130,6 +130,7 @@ async fn test_port_settings_basic_crud(ctx: &ControlPlaneTestContext) { fec: LinkFec::None, speed: LinkSpeed::Speed100G, autoneg: false, + tx_eq: None, }, ); // interfaces diff --git a/nexus/types/src/external_api/params.rs b/nexus/types/src/external_api/params.rs index bb1845efe6..9af9d3be1e 100644 --- a/nexus/types/src/external_api/params.rs +++ b/nexus/types/src/external_api/params.rs @@ -14,7 +14,7 @@ use omicron_common::api::external::{ IdentityMetadataCreateParams, IdentityMetadataUpdateParams, InstanceAutoRestartPolicy, InstanceCpuCount, LinkFec, LinkSpeed, Name, NameOrId, PaginationOrder, RouteDestination, RouteTarget, SemverVersion, - UserId, + TxEqConfig, UserId, }; use omicron_common::disk::DiskVariant; use oxnet::{IpNet, Ipv4Net, Ipv6Net}; @@ -1723,6 +1723,9 @@ pub struct LinkConfigCreate { /// Whether or not to set autonegotiation pub autoneg: bool, + + /// Optional tx_eq settings + pub tx_eq: Option, } /// The LLDP configuration associated with a port. diff --git a/openapi/bootstrap-agent.json b/openapi/bootstrap-agent.json index 54feaa7325..2f3058cd46 100644 --- a/openapi/bootstrap-agent.json +++ b/openapi/bootstrap-agent.json @@ -856,6 +856,15 @@ } ] }, + "tx_eq": { + "nullable": true, + "description": "TX-EQ configuration for this port", + "allOf": [ + { + "$ref": "#/components/schemas/TxEqConfig" + } + ] + }, "uplink_port_fec": { "description": "Port forward error correction type.", "allOf": [ @@ -1536,6 +1545,42 @@ } ] }, + "TxEqConfig": { + "description": "Per-port tx-eq overrides. This can be used to fine-tune the transceiver equalization settings to improve signal integrity.", + "type": "object", + "properties": { + "main": { + "nullable": true, + "description": "Main tap", + "type": "integer", + "format": "int32" + }, + "post1": { + "nullable": true, + "description": "Post-cursor tap1", + "type": "integer", + "format": "int32" + }, + "post2": { + "nullable": true, + "description": "Post-cursor tap2", + "type": "integer", + "format": "int32" + }, + "pre1": { + "nullable": true, + "description": "Pre-cursor tap1", + "type": "integer", + "format": "int32" + }, + "pre2": { + "nullable": true, + "description": "Pre-cursor tap2", + "type": "integer", + "format": "int32" + } + } + }, "TypedUuidForRackInitKind": { "type": "string", "format": "uuid" diff --git a/openapi/nexus-internal.json b/openapi/nexus-internal.json index 8df32aed4e..4392de318d 100644 --- a/openapi/nexus-internal.json +++ b/openapi/nexus-internal.json @@ -4677,6 +4677,15 @@ } ] }, + "tx_eq": { + "nullable": true, + "description": "TX-EQ configuration for this port", + "allOf": [ + { + "$ref": "#/components/schemas/TxEqConfig" + } + ] + }, "uplink_port_fec": { "description": "Port forward error correction type.", "allOf": [ @@ -5746,6 +5755,42 @@ "SwitchPutResponse": { "type": "object" }, + "TxEqConfig": { + "description": "Per-port tx-eq overrides. This can be used to fine-tune the transceiver equalization settings to improve signal integrity.", + "type": "object", + "properties": { + "main": { + "nullable": true, + "description": "Main tap", + "type": "integer", + "format": "int32" + }, + "post1": { + "nullable": true, + "description": "Post-cursor tap1", + "type": "integer", + "format": "int32" + }, + "post2": { + "nullable": true, + "description": "Post-cursor tap2", + "type": "integer", + "format": "int32" + }, + "pre1": { + "nullable": true, + "description": "Pre-cursor tap1", + "type": "integer", + "format": "int32" + }, + "pre2": { + "nullable": true, + "description": "Pre-cursor tap2", + "type": "integer", + "format": "int32" + } + } + }, "TypedUuidForDatasetKind": { "type": "string", "format": "uuid" diff --git a/openapi/nexus.json b/openapi/nexus.json index c44f21bb39..f12ff4730c 100644 --- a/openapi/nexus.json +++ b/openapi/nexus.json @@ -17194,6 +17194,15 @@ "$ref": "#/components/schemas/LinkSpeed" } ] + }, + "tx_eq": { + "nullable": true, + "description": "Optional tx_eq settings", + "allOf": [ + { + "$ref": "#/components/schemas/TxEqConfig" + } + ] } }, "required": [ @@ -20436,6 +20445,12 @@ "$ref": "#/components/schemas/LinkSpeed" } ] + }, + "tx_eq_config_id": { + "nullable": true, + "description": "The tx_eq configuration id for this link.", + "type": "string", + "format": "uuid" } }, "required": [ @@ -20736,6 +20751,18 @@ } ] }, + "tx_eq": { + "description": "TX equalization settings. These are optional, and most links will not need them.", + "type": "array", + "items": { + "nullable": true, + "allOf": [ + { + "$ref": "#/components/schemas/TxEqConfig" + } + ] + } + }, "vlan_interfaces": { "description": "Vlan interface settings.", "type": "array", @@ -20754,6 +20781,7 @@ "port", "routes", "settings", + "tx_eq", "vlan_interfaces" ] }, @@ -20940,6 +20968,42 @@ "items" ] }, + "TxEqConfig": { + "description": "Per-port tx-eq overrides. This can be used to fine-tune the transceiver equalization settings to improve signal integrity.", + "type": "object", + "properties": { + "main": { + "nullable": true, + "description": "Main tap", + "type": "integer", + "format": "int32" + }, + "post1": { + "nullable": true, + "description": "Post-cursor tap1", + "type": "integer", + "format": "int32" + }, + "post2": { + "nullable": true, + "description": "Post-cursor tap2", + "type": "integer", + "format": "int32" + }, + "pre1": { + "nullable": true, + "description": "Pre-cursor tap1", + "type": "integer", + "format": "int32" + }, + "pre2": { + "nullable": true, + "description": "Pre-cursor tap2", + "type": "integer", + "format": "int32" + } + } + }, "UninitializedSled": { "description": "A sled that has not been added to an initialized rack yet", "type": "object", diff --git a/openapi/sled-agent.json b/openapi/sled-agent.json index b79cb467ec..9963526ef8 100644 --- a/openapi/sled-agent.json +++ b/openapi/sled-agent.json @@ -3292,6 +3292,14 @@ "port": { "description": "Switchport to use for external connectivity", "type": "string" + }, + "tx_eq": { + "nullable": true, + "allOf": [ + { + "$ref": "#/components/schemas/TxEqConfig" + } + ] } }, "required": [ @@ -4626,6 +4634,15 @@ } ] }, + "tx_eq": { + "nullable": true, + "description": "TX-EQ configuration for this port", + "allOf": [ + { + "$ref": "#/components/schemas/TxEqConfig" + } + ] + }, "uplink_port_fec": { "description": "Port forward error correction type.", "allOf": [ @@ -5337,6 +5354,42 @@ "sync" ] }, + "TxEqConfig": { + "description": "Per-port tx-eq overrides. This can be used to fine-tune the transceiver equalization settings to improve signal integrity.", + "type": "object", + "properties": { + "main": { + "nullable": true, + "description": "Main tap", + "type": "integer", + "format": "int32" + }, + "post1": { + "nullable": true, + "description": "Post-cursor tap1", + "type": "integer", + "format": "int32" + }, + "post2": { + "nullable": true, + "description": "Post-cursor tap2", + "type": "integer", + "format": "int32" + }, + "pre1": { + "nullable": true, + "description": "Pre-cursor tap1", + "type": "integer", + "format": "int32" + }, + "pre2": { + "nullable": true, + "description": "Pre-cursor tap2", + "type": "integer", + "format": "int32" + } + } + }, "TypedUuidForDatasetKind": { "type": "string", "format": "uuid" diff --git a/openapi/wicketd.json b/openapi/wicketd.json index 55db469210..8e20f23d06 100644 --- a/openapi/wicketd.json +++ b/openapi/wicketd.json @@ -5991,6 +5991,42 @@ } ] }, + "TxEqConfig": { + "description": "Per-port tx-eq overrides. This can be used to fine-tune the transceiver equalization settings to improve signal integrity.", + "type": "object", + "properties": { + "main": { + "nullable": true, + "description": "Main tap", + "type": "integer", + "format": "int32" + }, + "post1": { + "nullable": true, + "description": "Post-cursor tap1", + "type": "integer", + "format": "int32" + }, + "post2": { + "nullable": true, + "description": "Post-cursor tap2", + "type": "integer", + "format": "int32" + }, + "pre1": { + "nullable": true, + "description": "Pre-cursor tap1", + "type": "integer", + "format": "int32" + }, + "pre2": { + "nullable": true, + "description": "Pre-cursor tap2", + "type": "integer", + "format": "int32" + } + } + }, "TypedUuidForRackInitKind": { "type": "string", "format": "uuid" @@ -6630,6 +6666,15 @@ "$ref": "#/components/schemas/RouteConfig" } }, + "tx_eq": { + "nullable": true, + "default": null, + "allOf": [ + { + "$ref": "#/components/schemas/TxEqConfig" + } + ] + }, "uplink_port_fec": { "$ref": "#/components/schemas/PortFec" }, diff --git a/package-manifest.toml b/package-manifest.toml index e35a0630e0..fffa89640d 100644 --- a/package-manifest.toml +++ b/package-manifest.toml @@ -719,8 +719,8 @@ only_for_targets.image = "standard" # the other `source.*` keys. source.type = "prebuilt" source.repo = "dendrite" -source.commit = "f3810e7bc1f0d746b5e95b3aaff32e52b02dfdfa" -source.sha256 = "c1506f6f818327523e6ff3102432a2038d319338b883235664b34f9132ff676a" +source.commit = "4067d742d832fa434217b95e4b149048d01ef54e" +source.sha256 = "5e9ccc42e5ac31f4be24025d2afd5978aef33d618f3cb7caa260eff73b7e6a79" output.type = "zone" output.intermediate_only = true @@ -746,8 +746,8 @@ only_for_targets.image = "standard" # the other `source.*` keys. source.type = "prebuilt" source.repo = "dendrite" -source.commit = "f3810e7bc1f0d746b5e95b3aaff32e52b02dfdfa" -source.sha256 = "061d40085e733e60d7c53ebfd2a4cf64f54a856e7eb5fd4b82ac65ec6a5b847b" +source.commit = "4067d742d832fa434217b95e4b149048d01ef54e" +source.sha256 = "9d3156b7895126b9df5460dd0c34668738a7f2d5894a4be0229644820e732895" output.type = "zone" output.intermediate_only = true @@ -766,8 +766,8 @@ only_for_targets.image = "standard" # the other `source.*` keys. source.type = "prebuilt" source.repo = "dendrite" -source.commit = "f3810e7bc1f0d746b5e95b3aaff32e52b02dfdfa" -source.sha256 = "c6cb4c077f0ddfc78ab06e07316d1312657f95526ced60c2b8e7baf1c73ae24a" +source.commit = "4067d742d832fa434217b95e4b149048d01ef54e" +source.sha256 = "4eff4f00201ab8373510644693d066dbec2497142d48964be9844f0b30c147e8" output.type = "zone" output.intermediate_only = true diff --git a/schema/crdb/add-tx-eq/up1.sql b/schema/crdb/add-tx-eq/up1.sql new file mode 100644 index 0000000000..07be4dd657 --- /dev/null +++ b/schema/crdb/add-tx-eq/up1.sql @@ -0,0 +1,4 @@ +/* + * Add a pointer to this link's transceiver equalization config settings. + */ +ALTER TABLE omicron.public.switch_port_settings_link_config ADD COLUMN IF NOT EXISTS tx_eq_config_id UUID; diff --git a/schema/crdb/add-tx-eq/up2.sql b/schema/crdb/add-tx-eq/up2.sql new file mode 100644 index 0000000000..76c8083eaa --- /dev/null +++ b/schema/crdb/add-tx-eq/up2.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS omicron.public.tx_eq_config ( + id UUID PRIMARY KEY, + pre1 INT4, + pre2 INT4, + main INT4, + post2 INT4, + post1 INT4 +); diff --git a/schema/crdb/dbinit.sql b/schema/crdb/dbinit.sql index a5a36156b4..b095c4c89a 100644 --- a/schema/crdb/dbinit.sql +++ b/schema/crdb/dbinit.sql @@ -2847,6 +2847,7 @@ CREATE TABLE IF NOT EXISTS omicron.public.switch_port_settings_link_config ( speed omicron.public.switch_link_speed, autoneg BOOL NOT NULL DEFAULT false, lldp_link_config_id UUID, + tx_eq_config_id UUID, PRIMARY KEY (port_settings_id, link_name) ); @@ -2865,6 +2866,15 @@ CREATE TABLE IF NOT EXISTS omicron.public.lldp_link_config ( time_deleted TIMESTAMPTZ ); +CREATE TABLE IF NOT EXISTS omicron.public.tx_eq_config ( + id UUID PRIMARY KEY, + pre1 INT4, + pre2 INT4, + main INT4, + post2 INT4, + post1 INT4 +); + CREATE TYPE IF NOT EXISTS omicron.public.switch_interface_kind AS ENUM ( 'primary', 'vlan', @@ -4591,7 +4601,7 @@ INSERT INTO omicron.public.db_metadata ( version, target_version ) VALUES - (TRUE, NOW(), NOW(), '112.0.0', NULL) + (TRUE, NOW(), NOW(), '113.0.0', NULL) ON CONFLICT DO NOTHING; COMMIT; diff --git a/schema/rss-sled-plan.json b/schema/rss-sled-plan.json index 7e8513e1e3..56fe35bcde 100644 --- a/schema/rss-sled-plan.json +++ b/schema/rss-sled-plan.json @@ -751,6 +751,17 @@ } ] }, + "tx_eq": { + "description": "TX-EQ configuration for this port", + "anyOf": [ + { + "$ref": "#/definitions/TxEqConfig" + }, + { + "type": "null" + } + ] + }, "uplink_port_fec": { "description": "Port forward error correction type.", "allOf": [ @@ -1091,6 +1102,52 @@ } ] }, + "TxEqConfig": { + "description": "Per-port tx-eq overrides. This can be used to fine-tune the transceiver equalization settings to improve signal integrity.", + "type": "object", + "properties": { + "main": { + "description": "Main tap", + "type": [ + "integer", + "null" + ], + "format": "int32" + }, + "post1": { + "description": "Post-cursor tap1", + "type": [ + "integer", + "null" + ], + "format": "int32" + }, + "post2": { + "description": "Post-cursor tap2", + "type": [ + "integer", + "null" + ], + "format": "int32" + }, + "pre1": { + "description": "Pre-cursor tap1", + "type": [ + "integer", + "null" + ], + "format": "int32" + }, + "pre2": { + "description": "Pre-cursor tap2", + "type": [ + "integer", + "null" + ], + "format": "int32" + } + } + }, "TypedUuidForSledKind": { "type": "string", "format": "uuid" diff --git a/sled-agent/src/bootstrap/early_networking.rs b/sled-agent/src/bootstrap/early_networking.rs index 9685780a0e..acfbafe61c 100644 --- a/sled-agent/src/bootstrap/early_networking.rs +++ b/sled-agent/src/bootstrap/early_networking.rs @@ -6,7 +6,7 @@ use anyhow::{anyhow, Context}; use dpd_client::types::{ - LinkCreate, LinkId, LinkSettings, PortId, PortSettings, + LinkCreate, LinkId, LinkSettings, PortId, PortSettings, TxEq, }; use dpd_client::Client as DpdClient; use futures::future; @@ -702,6 +702,13 @@ impl<'a> EarlyNetworkSetup<'a> { fec: convert_fec(&port_config.uplink_port_fec), speed: convert_speed(&port_config.uplink_port_speed), lane: Some(LinkId(0)), + tx_eq: port_config.tx_eq.map(|x| TxEq { + pre1: x.pre1, + pre2: x.pre2, + main: x.main, + post2: x.post2, + post1: x.post1, + }), }, addrs, }; diff --git a/sled-agent/src/rack_setup/service.rs b/sled-agent/src/rack_setup/service.rs index c1e29b0e0f..2795422ee2 100644 --- a/sled-agent/src/rack_setup/service.rs +++ b/sled-agent/src/rack_setup/service.rs @@ -844,6 +844,15 @@ impl ServiceInner { management_addrs: lp.management_addrs.clone(), } }), + tx_eq: config.tx_eq.as_ref().map(|tx_eq| { + NexusTypes::TxEqConfig { + pre1: tx_eq.pre1, + pre2: tx_eq.pre2, + main: tx_eq.main, + post2: tx_eq.post2, + post1: tx_eq.post1, + } + }), }) .collect(), bgp: config diff --git a/sled-agent/tests/integration_tests/early_network.rs b/sled-agent/tests/integration_tests/early_network.rs index c0ecc09f12..420ce493d0 100644 --- a/sled-agent/tests/integration_tests/early_network.rs +++ b/sled-agent/tests/integration_tests/early_network.rs @@ -154,6 +154,7 @@ fn current_config_example() -> (&'static str, EarlyNetworkConfig) { vlan_id: None, }], autoneg: true, + tx_eq: None, lldp: None, }], bgp: vec![BgpConfig { diff --git a/sled-agent/tests/output/new-rss-sled-plans/madrid-rss-sled-plan.json b/sled-agent/tests/output/new-rss-sled-plans/madrid-rss-sled-plan.json index 270d926ea8..25e470e389 100644 --- a/sled-agent/tests/output/new-rss-sled-plans/madrid-rss-sled-plan.json +++ b/sled-agent/tests/output/new-rss-sled-plans/madrid-rss-sled-plan.json @@ -144,7 +144,8 @@ "uplink_port_fec": "none", "bgp_peers": [], "autoneg": false, - "lldp": null + "lldp": null, + "tx_eq": null }, { "routes": [ @@ -167,7 +168,8 @@ "uplink_port_fec": "none", "bgp_peers": [], "autoneg": false, - "lldp": null + "lldp": null, + "tx_eq": null } ], "bgp": [], diff --git a/sled-agent/types/src/early_networking.rs b/sled-agent/types/src/early_networking.rs index 46ceb2dbbf..6faead6fe9 100644 --- a/sled-agent/types/src/early_networking.rs +++ b/sled-agent/types/src/early_networking.rs @@ -300,6 +300,7 @@ pub mod back_compat { bgp_peers: v1.bgp_peers.clone(), autoneg: v1.autoneg, lldp: None, + tx_eq: None, } } } @@ -347,6 +348,7 @@ pub mod back_compat { bgp_peers: vec![], autoneg: false, lldp: None, + tx_eq: None, } } } @@ -520,6 +522,7 @@ mod tests { autoneg: false, bgp_peers: vec![], lldp: None, + tx_eq: None, }], bgp: vec![], bfd: vec![], @@ -602,6 +605,7 @@ mod tests { autoneg: false, bgp_peers: vec![], lldp: None, + tx_eq: None, }], bgp: vec![], bfd: vec![], diff --git a/tools/dendrite_openapi_version b/tools/dendrite_openapi_version index 75c57e9d29..09378177d7 100755 --- a/tools/dendrite_openapi_version +++ b/tools/dendrite_openapi_version @@ -1,2 +1,2 @@ -COMMIT="f3810e7bc1f0d746b5e95b3aaff32e52b02dfdfa" -SHA2="3a54305ab4b1270c9a5fb0603f481fce199f3767c174a03559ff642f7f44687e" +COMMIT="4067d742d832fa434217b95e4b149048d01ef54e" +SHA2="ff41c2a30f67c4ce79fc1c8dd99bce8042e855c51fd15362be4ee833acec58cf" diff --git a/tools/dendrite_stub_checksums b/tools/dendrite_stub_checksums index fe420e299e..40cf08e5c0 100644 --- a/tools/dendrite_stub_checksums +++ b/tools/dendrite_stub_checksums @@ -1,3 +1,3 @@ -CIDL_SHA256_ILLUMOS="c1506f6f818327523e6ff3102432a2038d319338b883235664b34f9132ff676a" -CIDL_SHA256_LINUX_DPD="fc9ea4dc22e761dce3aa4d252983360f799426a0c23ea8f347653664d3e2b55a" -CIDL_SHA256_LINUX_SWADM="9da0dd6c972206338971a90144b1c35e101d69aaacf26240a45cef45d828b090" +CIDL_SHA256_ILLUMOS="5e9ccc42e5ac31f4be24025d2afd5978aef33d618f3cb7caa260eff73b7e6a79" +CIDL_SHA256_LINUX_DPD="5738cb74ea2657dd255f5ba094600265f5b10b99eeb70bd0b69cbb987b951b71" +CIDL_SHA256_LINUX_SWADM="30e085b64e33c374ec79d9e0e8cf015547e573b34525d2da228a8c99755557fb" diff --git a/tools/permslip_production b/tools/permslip_production index b985e1280c..a01da94672 100644 --- a/tools/permslip_production +++ b/tools/permslip_production @@ -1,2 +1,2 @@ -926c726edca29f7134df9489fdedbdc3553d7e9ecbaf97d3be6c55dc2634f828 manifest-oxide-rot-1-v1.0.28.toml +3cbe182c3e3425442eec2c2019045f8d5e613688597a01c6f05fa93e2ff3d165 manifest-oxide-rot-1-v1.0.27.toml 610ebce44b1fb622eb56591534fb2569340fdba9b5ba62ca1b02f0b2d2e973dc manifest-bootleby-v1.3.1.toml diff --git a/wicket-common/src/example.rs b/wicket-common/src/example.rs index 3951520f01..8572b791cd 100644 --- a/wicket-common/src/example.rs +++ b/wicket-common/src/example.rs @@ -13,7 +13,7 @@ use omicron_common::{ external::AllowedSourceIps, internal::shared::{ BgpConfig, BgpPeerConfig, LldpAdminStatus, LldpPortConfig, PortFec, - PortSpeed, RouteConfig, + PortSpeed, RouteConfig, TxEqConfig, }, }, }; @@ -177,6 +177,14 @@ impl ExampleRackSetupData { management_addrs: None, }); + let tx_eq = Some(TxEqConfig { + pre1: Some(0), + pre2: Some(0), + main: Some(26), + post2: Some(0), + post1: Some(0), + }); + let switch1_port0_lldp = Some(LldpPortConfig { status: LldpAdminStatus::Enabled, chassis_id: Some("chassid id override".to_string()), @@ -190,10 +198,11 @@ impl ExampleRackSetupData { let rack_network_config = UserSpecifiedRackNetworkConfig { infra_ip_first: "172.30.0.1".parse().unwrap(), infra_ip_last: "172.30.0.10".parse().unwrap(), + #[rustfmt::skip] switch0: btreemap! { - "port0".to_owned() => UserSpecifiedPortConfig { - addresses: vec!["172.30.0.1/24".parse().unwrap()], - routes: vec![RouteConfig { + "port0".to_owned() => UserSpecifiedPortConfig { + addresses: vec!["172.30.0.1/24".parse().unwrap()], + routes: vec![RouteConfig { destination: "0.0.0.0/0".parse().unwrap(), nexthop: "172.30.0.10".parse().unwrap(), vlan_id: Some(1), @@ -202,10 +211,12 @@ impl ExampleRackSetupData { bgp_peers: switch0_port0_bgp_peers, uplink_port_speed: PortSpeed::Speed400G, uplink_port_fec: PortFec::Firecode, - lldp: switch0_port0_lldp, - autoneg: true, - }, - }, + lldp: switch0_port0_lldp, + tx_eq, + autoneg: true, + }, + }, + #[rustfmt::skip] switch1: btreemap! { // Use the same port name as in switch0 to test that it doesn't // collide. @@ -221,6 +232,7 @@ impl ExampleRackSetupData { uplink_port_speed: PortSpeed::Speed400G, uplink_port_fec: PortFec::Firecode, lldp: switch1_port0_lldp, + tx_eq, autoneg: true, }, }, diff --git a/wicket-common/src/rack_setup.rs b/wicket-common/src/rack_setup.rs index cb6b13422b..7136bc8d96 100644 --- a/wicket-common/src/rack_setup.rs +++ b/wicket-common/src/rack_setup.rs @@ -15,6 +15,7 @@ use omicron_common::api::internal::shared::LldpPortConfig; use omicron_common::api::internal::shared::PortFec; use omicron_common::api::internal::shared::PortSpeed; use omicron_common::api::internal::shared::RouteConfig; +use omicron_common::api::internal::shared::TxEqConfig; use omicron_common::api::internal::shared::UplinkAddressConfig; use omicron_common::update::ArtifactHash; use owo_colors::OwoColorize; @@ -188,6 +189,8 @@ pub struct UserSpecifiedPortConfig { pub bgp_peers: Vec, #[serde(default)] pub lldp: Option, + #[serde(default)] + pub tx_eq: Option, } /// User-specified version of [`BgpPeerConfig`]. diff --git a/wicket/src/cli/rack_setup/config_toml.rs b/wicket/src/cli/rack_setup/config_toml.rs index 92496e94d5..727f1bbaa8 100644 --- a/wicket/src/cli/rack_setup/config_toml.rs +++ b/wicket/src/cli/rack_setup/config_toml.rs @@ -322,6 +322,7 @@ fn populate_uplink_table(cfg: &UserSpecifiedPortConfig) -> Table { autoneg, bgp_peers, lldp, + tx_eq, } = cfg; let mut uplink = Table::new(); @@ -533,6 +534,26 @@ fn populate_uplink_table(cfg: &UserSpecifiedPortConfig) -> Table { uplink.insert("lldp", Item::Table(lldp)); } + if let Some(t) = tx_eq { + let mut tx_eq = Table::new(); + if let Some(x) = t.pre1 { + tx_eq.insert("pre1", i32_item(x)); + } + if let Some(x) = t.pre2 { + tx_eq.insert("pre2", i32_item(x)); + } + if let Some(x) = t.main { + tx_eq.insert("main", i32_item(x)); + } + if let Some(x) = t.post1 { + tx_eq.insert("post1", i32_item(x)); + } + if let Some(x) = t.post2 { + tx_eq.insert("post2", i32_item(x)); + } + uplink.insert("tx_eq", Item::Table(tx_eq)); + } + uplink } @@ -556,6 +577,14 @@ fn string_item(s: impl ToString) -> Item { Item::Value(string_value(s)) } +fn i32_value(i: i32) -> Value { + Value::Integer(Formatted::new(i.into())) +} + +fn i32_item(i: i32) -> Item { + Item::Value(i32_value(i)) +} + fn i64_value(i: i64) -> Value { Value::Integer(Formatted::new(i)) } diff --git a/wicket/src/ui/panes/rack_setup.rs b/wicket/src/ui/panes/rack_setup.rs index be299ef022..1828049fc3 100644 --- a/wicket/src/ui/panes/rack_setup.rs +++ b/wicket/src/ui/panes/rack_setup.rs @@ -774,6 +774,7 @@ fn rss_config_text<'a>( autoneg, bgp_peers, lldp, + tx_eq, } = uplink; let mut items = vec![ @@ -1136,6 +1137,45 @@ fn rss_config_text<'a>( items.extend(lldp); } + if let Some(t) = tx_eq { + let mut tx_eq = vec![vec![Span::styled( + " • TxEq port settings: ", + label_style, + )]]; + + if let Some(x) = t.pre1 { + tx_eq.push(vec![ + Span::styled(" • Precursor 1: ", label_style), + Span::styled(x.to_string(), ok_style), + ]) + } + if let Some(x) = t.pre2 { + tx_eq.push(vec![ + Span::styled(" • Precursor 2: ", label_style), + Span::styled(x.to_string(), ok_style), + ]) + } + if let Some(x) = t.main { + tx_eq.push(vec![ + Span::styled(" • Main cursor: ", label_style), + Span::styled(x.to_string(), ok_style), + ]) + } + if let Some(x) = t.post2 { + tx_eq.push(vec![ + Span::styled(" • Postcursor 2: ", label_style), + Span::styled(x.to_string(), ok_style), + ]) + } + if let Some(x) = t.post1 { + tx_eq.push(vec![ + Span::styled(" • Postcursor 1: ", label_style), + Span::styled(x.to_string(), ok_style), + ]) + } + items.extend(tx_eq); + } + append_list( &mut spans, Cow::from(format!("Uplink {}: ", i + 1)), diff --git a/wicket/tests/output/example_non_empty.toml b/wicket/tests/output/example_non_empty.toml index fafb31048d..d6f5e7c820 100644 --- a/wicket/tests/output/example_non_empty.toml +++ b/wicket/tests/output/example_non_empty.toml @@ -119,6 +119,13 @@ system_name = "system name override" system_description = "system description override" port_description = "port description override" +[rack_network_config.switch0.port0.tx_eq] +pre1 = 0 +pre2 = 0 +main = 26 +post1 = 0 +post2 = 0 + [rack_network_config.switch1.port0] routes = [{ nexthop = "172.33.0.10", destination = "0.0.0.0/0", vlan_id = 1 }] addresses = [{ address = "172.32.0.1/24" }] @@ -148,6 +155,13 @@ system_description = "system description override" port_description = "port description override" management_addrs = ["172.32.0.4"] +[rack_network_config.switch1.port0.tx_eq] +pre1 = 0 +pre2 = 0 +main = 26 +post1 = 0 +post2 = 0 + [[rack_network_config.bgp]] asn = 47 originate = ["10.0.0.0/16"] diff --git a/wicketd/src/preflight_check/uplink.rs b/wicketd/src/preflight_check/uplink.rs index da30cd0199..e70d9db57b 100644 --- a/wicketd/src/preflight_check/uplink.rs +++ b/wicketd/src/preflight_check/uplink.rs @@ -778,6 +778,7 @@ fn build_port_settings( fec, speed, lane: Some(LinkId(0)), + tx_eq: None, }, }, ); diff --git a/wicketd/src/rss_config.rs b/wicketd/src/rss_config.rs index 2be098dbc2..8ee252ef77 100644 --- a/wicketd/src/rss_config.rs +++ b/wicketd/src/rss_config.rs @@ -702,6 +702,7 @@ fn build_port_config( use bootstrap_agent_client::types::PortSpeed as BaPortSpeed; use bootstrap_agent_client::types::RouteConfig as BaRouteConfig; use bootstrap_agent_client::types::SwitchLocation as BaSwitchLocation; + use bootstrap_agent_client::types::TxEqConfig as BaTxEqConfig; use bootstrap_agent_client::types::UplinkAddressConfig as BaUplinkAddressConfig; use omicron_common::api::internal::shared::LldpAdminStatus; use omicron_common::api::internal::shared::PortFec; @@ -807,6 +808,13 @@ fn build_port_config( port_description: c.port_description.clone(), management_addrs: c.management_addrs.clone(), }), + tx_eq: config.tx_eq.as_ref().map(|c| BaTxEqConfig { + pre1: c.pre1, + pre2: c.pre2, + main: c.main, + post2: c.post2, + post1: c.post1, + }), } }