use std::collections::HashMap;
use crate::{
ErrorKind, EthtoolCoalesceConfig, EthtoolFeatureConfig, EthtoolFecConfig,
EthtoolFecMode, EthtoolPauseConfig, EthtoolRingConfig, Interface,
NmstateError,
nm::nm_dbus::{NmConnection, NmSettingEthtool},
};
const KERNEL_ETHTOOL_FEATURE_2_NM: [(&str, &str); 10] = [
("rx-checksum", "feature-rx"),
("tx-scatter-gather", "feature-sg"),
("tx-tcp-segmentation", "feature-tso"),
("rx-gro", "feature-gro"),
("tx-generic-segmentation", "feature-gso"),
("rx-hashing", "feature-rxhash"),
("rx-lro", "feature-lro"),
("rx-ntuple-filter", "feature-ntuple"),
("rx-vlan-hw-parse", "feature-rxvlan"),
("tx-vlan-hw-insert", "feature-txvlan"),
];
pub(crate) fn gen_ethtool_setting(
iface: &Interface,
nm_conn: &mut NmConnection,
) -> Result<(), NmstateError> {
if let Some(ethtool_iface) = iface.base_iface().ethtool.as_ref() {
let mut nm_ethtool_set =
nm_conn.ethtool.as_ref().cloned().unwrap_or_default();
if let Some(pause_conf) = ethtool_iface.pause.as_ref() {
apply_pause_options(&mut nm_ethtool_set, pause_conf);
}
if let Some(feature_conf) = ethtool_iface.feature.as_ref() {
apply_feature_options(&mut nm_ethtool_set, feature_conf)?;
}
if let Some(coalesce_conf) = ethtool_iface.coalesce.as_ref() {
apply_coalesce_options(&mut nm_ethtool_set, coalesce_conf);
}
if let Some(ring_conf) = ethtool_iface.ring.as_ref() {
apply_ring_options(&mut nm_ethtool_set, ring_conf);
}
if let Some(fec_conf) = ethtool_iface.fec.as_ref() {
apply_fec_options(&mut nm_ethtool_set, fec_conf);
}
nm_conn.ethtool = Some(nm_ethtool_set);
}
Ok(())
}
fn apply_pause_options(
nm_ethtool_set: &mut NmSettingEthtool,
pause_conf: &EthtoolPauseConfig,
) {
if pause_conf.autoneg == Some(false) {
nm_ethtool_set.pause_rx = pause_conf.rx;
nm_ethtool_set.pause_tx = pause_conf.tx;
}
nm_ethtool_set.pause_autoneg = pause_conf.autoneg;
}
fn apply_feature_options(
nm_ethtool_set: &mut NmSettingEthtool,
feature_conf: &EthtoolFeatureConfig,
) -> Result<(), NmstateError> {
let mut kernel_2_nm = HashMap::new();
for (k, v) in KERNEL_ETHTOOL_FEATURE_2_NM {
kernel_2_nm.insert(k, v);
}
let mut nm_features = HashMap::new();
for (k, v) in feature_conf {
if let Some(nm_feature_name) = kernel_2_nm.get(k.as_str()) {
nm_features.insert(nm_feature_name.to_string(), *v);
} else {
nm_features.insert(format!("feature-{k}"), *v);
}
}
nm_ethtool_set.features = Some(nm_features);
if let Err(e) = nm_ethtool_set.validate() {
return Err(NmstateError::new(
ErrorKind::InvalidArgument,
e.to_string(),
));
}
Ok(())
}
fn apply_coalesce_options(
nm_ethtool_set: &mut NmSettingEthtool,
coalesce_conf: &EthtoolCoalesceConfig,
) {
nm_ethtool_set.coalesce_adaptive_rx = coalesce_conf.adaptive_rx;
nm_ethtool_set.coalesce_adaptive_tx = coalesce_conf.adaptive_tx;
nm_ethtool_set.coalesce_pkt_rate_high = coalesce_conf.pkt_rate_high;
nm_ethtool_set.coalesce_pkt_rate_low = coalesce_conf.pkt_rate_low;
nm_ethtool_set.coalesce_rx_frames = coalesce_conf.rx_frames;
nm_ethtool_set.coalesce_rx_frames_high = coalesce_conf.rx_frames_high;
nm_ethtool_set.coalesce_rx_frames_low = coalesce_conf.rx_frames_low;
nm_ethtool_set.coalesce_rx_frames_irq = coalesce_conf.rx_frames_irq;
nm_ethtool_set.coalesce_tx_frames = coalesce_conf.tx_frames;
nm_ethtool_set.coalesce_tx_frames_high = coalesce_conf.tx_frames_high;
nm_ethtool_set.coalesce_tx_frames_low = coalesce_conf.tx_frames_low;
nm_ethtool_set.coalesce_tx_frames_irq = coalesce_conf.tx_frames_irq;
nm_ethtool_set.coalesce_rx_usecs = coalesce_conf.rx_usecs;
nm_ethtool_set.coalesce_rx_usecs_high = coalesce_conf.rx_usecs_high;
nm_ethtool_set.coalesce_rx_usecs_low = coalesce_conf.rx_usecs_low;
nm_ethtool_set.coalesce_rx_usecs_irq = coalesce_conf.rx_usecs_irq;
nm_ethtool_set.coalesce_sample_interval = coalesce_conf.sample_interval;
nm_ethtool_set.coalesce_stats_block_usecs = coalesce_conf.stats_block_usecs;
nm_ethtool_set.coalesce_tx_usecs = coalesce_conf.tx_usecs;
}
fn apply_ring_options(
nm_ethtool_set: &mut NmSettingEthtool,
ring_conf: &EthtoolRingConfig,
) {
nm_ethtool_set.ring_rx = ring_conf.rx;
nm_ethtool_set.ring_rx_jumbo = ring_conf.rx_jumbo;
nm_ethtool_set.ring_rx_mini = ring_conf.rx_mini;
nm_ethtool_set.ring_tx = ring_conf.tx;
}
const NM_SETTING_ETHTOOL_FEC_MODE_AUTO: u32 = 1 << 1;
const NM_SETTING_ETHTOOL_FEC_MODE_OFF: u32 = 1 << 2;
const NM_SETTING_ETHTOOL_FEC_MODE_RS: u32 = 1 << 3;
const NM_SETTING_ETHTOOL_FEC_MODE_BASER: u32 = 1 << 4;
const NM_SETTING_ETHTOOL_FEC_MODE_LLRS: u32 = 1 << 5;
fn apply_fec_options(
nm_ethtool_set: &mut NmSettingEthtool,
fec_conf: &EthtoolFecConfig,
) {
nm_ethtool_set.fec_mode = match (fec_conf.auto, fec_conf.mode) {
(None, None) => None,
(Some(true), _) => Some(NM_SETTING_ETHTOOL_FEC_MODE_AUTO),
(Some(false), None) => Some(NM_SETTING_ETHTOOL_FEC_MODE_OFF),
(Some(false), Some(mode)) | (None, Some(mode)) => match mode {
EthtoolFecMode::Off => Some(NM_SETTING_ETHTOOL_FEC_MODE_OFF),
EthtoolFecMode::Rs => Some(NM_SETTING_ETHTOOL_FEC_MODE_RS),
EthtoolFecMode::Baser => Some(NM_SETTING_ETHTOOL_FEC_MODE_BASER),
EthtoolFecMode::Llrs => Some(NM_SETTING_ETHTOOL_FEC_MODE_LLRS),
},
}
}