Skip to content

ethtool: Add support of channel #8

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

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
29 changes: 29 additions & 0 deletions examples/dump_channel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: MIT

use futures::stream::TryStreamExt;

// Once we find a way to load netsimdev kernel module in CI, we can convert this

Choose a reason for hiding this comment

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

s/netsimdev/netdevsim/

Copy link
Author

Choose a reason for hiding this comment

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

This comment is copied from other examples provided in the project.
I'll submit a dedicated PR to fix this in all the code examples.

// to a test
fn main() {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_io()
.build()
.unwrap();
rt.block_on(get_channel(None));
}

async fn get_channel(iface_name: Option<&str>) {
let (connection, mut handle, _) = ethtool::new_connection().unwrap();
tokio::spawn(connection);

let mut channel_handle = handle.channel().get(iface_name).execute().await;

let mut msgs = Vec::new();
while let Some(msg) = channel_handle.try_next().await.unwrap() {
msgs.push(msg);
}
assert!(!msgs.is_empty());
for msg in msgs {
println!("{msg:?}");
}
}
162 changes: 162 additions & 0 deletions src/channel/attr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// SPDX-License-Identifier: MIT

use anyhow::Context;
use byteorder::{ByteOrder, NativeEndian};
use netlink_packet_utils::{
nla::{DefaultNla, Nla, NlaBuffer, NlasIterator, NLA_F_NESTED},
parsers::parse_u32,
DecodeError, Emitable, Parseable,
};

use crate::{EthtoolAttr, EthtoolHeader};

const ETHTOOL_A_CHANNELS_HEADER: u16 = 1;
const ETHTOOL_A_CHANNELS_RX_MAX: u16 = 2;
const ETHTOOL_A_CHANNELS_TX_MAX: u16 = 3;
const ETHTOOL_A_CHANNELS_OTHER_MAX: u16 = 4;
const ETHTOOL_A_CHANNELS_COMBINED_MAX: u16 = 5;
const ETHTOOL_A_CHANNELS_RX_COUNT: u16 = 6;
const ETHTOOL_A_CHANNELS_TX_COUNT: u16 = 7;
const ETHTOOL_A_CHANNELS_OTHER_COUNT: u16 = 8;
const ETHTOOL_A_CHANNELS_COMBINED_COUNT: u16 = 9;

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum EthtoolChannelAttr {
Header(Vec<EthtoolHeader>),
RxMax(u32),
TxMax(u32),
OtherMax(u32),
CombinedMax(u32),
RxCount(u32),
TxCount(u32),
OtherCount(u32),
CombinedCount(u32),
Other(DefaultNla),
}

impl Nla for EthtoolChannelAttr {
fn value_len(&self) -> usize {
match self {
Self::Header(hdrs) => hdrs.as_slice().buffer_len(),
Self::RxMax(_)
| Self::TxMax(_)
| Self::OtherMax(_)
| Self::CombinedMax(_)
| Self::RxCount(_)
| Self::TxCount(_)
| Self::OtherCount(_)
| Self::CombinedCount(_) => 4,
Self::Other(attr) => attr.value_len(),
}
}

fn kind(&self) -> u16 {
match self {
Self::Header(_) => ETHTOOL_A_CHANNELS_HEADER | NLA_F_NESTED,
Self::RxMax(_) => ETHTOOL_A_CHANNELS_RX_MAX,
Self::TxMax(_) => ETHTOOL_A_CHANNELS_TX_MAX,
Self::OtherMax(_) => ETHTOOL_A_CHANNELS_OTHER_MAX,
Self::CombinedMax(_) => ETHTOOL_A_CHANNELS_COMBINED_MAX,
Self::RxCount(_) => ETHTOOL_A_CHANNELS_RX_COUNT,
Self::TxCount(_) => ETHTOOL_A_CHANNELS_TX_COUNT,
Self::OtherCount(_) => ETHTOOL_A_CHANNELS_OTHER_COUNT,
Self::CombinedCount(_) => ETHTOOL_A_CHANNELS_COMBINED_COUNT,
Self::Other(attr) => attr.kind(),
}
}

fn emit_value(&self, buffer: &mut [u8]) {
match self {
Self::Header(ref nlas) => nlas.as_slice().emit(buffer),
Self::RxMax(d)
| Self::TxMax(d)
| Self::OtherMax(d)
| Self::CombinedMax(d)
| Self::RxCount(d)
| Self::TxCount(d)
| Self::OtherCount(d)
| Self::CombinedCount(d) => NativeEndian::write_u32(buffer, *d),
Self::Other(ref attr) => attr.emit(buffer),
}
}
}

impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>>
for EthtoolChannelAttr
{
fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
let payload = buf.value();
Ok(match buf.kind() {
ETHTOOL_A_CHANNELS_HEADER => {
let mut nlas = Vec::new();
let error_msg = "failed to parse ring header attributes";
for nla in NlasIterator::new(payload) {
let nla = &nla.context(error_msg)?;
let parsed =
EthtoolHeader::parse(nla).context(error_msg)?;
nlas.push(parsed);
}
Self::Header(nlas)
}
ETHTOOL_A_CHANNELS_RX_MAX => Self::RxMax(
parse_u32(payload)
.context("Invalid ETHTOOL_A_CHANNELS_RX_MAX value")?,
),

ETHTOOL_A_CHANNELS_TX_MAX => Self::TxMax(
parse_u32(payload)
.context("Invalid ETHTOOL_A_CHANNELS_TX_MAX value")?,
),

ETHTOOL_A_CHANNELS_OTHER_MAX => Self::OtherMax(
parse_u32(payload)
.context("Invalid ETHTOOL_A_CHANNELS_OTHER_MAX value")?,
),

ETHTOOL_A_CHANNELS_COMBINED_MAX => Self::CombinedMax(
parse_u32(payload)
.context("Invalid ETHTOOL_A_CHANNELS_COMBINED_MAX value")?,
),

ETHTOOL_A_CHANNELS_RX_COUNT => Self::RxCount(
parse_u32(payload)
.context("Invalid ETHTOOL_A_CHANNELS_RX_COUNT value")?,
),

ETHTOOL_A_CHANNELS_TX_COUNT => Self::TxCount(
parse_u32(payload)
.context("Invalid ETHTOOL_A_CHANNELS_TX_COUNT value")?,
),

ETHTOOL_A_CHANNELS_OTHER_COUNT => Self::OtherCount(
parse_u32(payload)
.context("Invalid ETHTOOL_A_CHANNELS_OTHER_COUNT value")?,
),

ETHTOOL_A_CHANNELS_COMBINED_COUNT => {
Self::CombinedCount(parse_u32(payload).context(
"Invalid ETHTOOL_A_CHANNELS_COMBINED_COUNT value",
)?)
}

kind => Self::Other(
DefaultNla::parse(buf)
.context(format!("invalid ethtool ring NLA kind {kind}"))?,
),
})
}
}

pub(crate) fn parse_channel_nlas(
buffer: &[u8],
) -> Result<Vec<EthtoolAttr>, DecodeError> {
let mut nlas = Vec::new();
for nla in NlasIterator::new(buffer) {
let error_msg =
format!("Failed to parse ethtool ring message attribute {nla:?}");
let nla = &nla.context(error_msg.clone())?;
let parsed = EthtoolChannelAttr::parse(nla).context(error_msg)?;
nlas.push(EthtoolAttr::Channel(parsed));
}
Ok(nlas)
}
34 changes: 34 additions & 0 deletions src/channel/get.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: MIT

use futures::TryStream;
use netlink_packet_generic::GenlMessage;

use crate::{ethtool_execute, EthtoolError, EthtoolHandle, EthtoolMessage};

pub struct EthtoolChannelGetRequest {
handle: EthtoolHandle,
iface_name: Option<String>,
}

impl EthtoolChannelGetRequest {
pub(crate) fn new(handle: EthtoolHandle, iface_name: Option<&str>) -> Self {
EthtoolChannelGetRequest {
handle,
iface_name: iface_name.map(|i| i.to_string()),
}
}

pub async fn execute(
self,
) -> impl TryStream<Ok = GenlMessage<EthtoolMessage>, Error = EthtoolError>
{
let EthtoolChannelGetRequest {
mut handle,
iface_name,
} = self;

let ethtool_msg =
EthtoolMessage::new_channel_get(iface_name.as_deref());
ethtool_execute(&mut handle, iface_name.is_none(), ethtool_msg).await
}
}
20 changes: 20 additions & 0 deletions src/channel/handle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: MIT

use crate::{EthtoolChannelGetRequest, EthtoolHandle};

pub struct EthtoolChannelHandle(EthtoolHandle);

impl EthtoolChannelHandle {
pub fn new(handle: EthtoolHandle) -> Self {
EthtoolChannelHandle(handle)
}

/// Retrieve the ethtool channels of a interface (equivalent to `ethtool -l
/// eth1`)
pub fn get(
&mut self,
iface_name: Option<&str>,
) -> EthtoolChannelGetRequest {
EthtoolChannelGetRequest::new(self.0.clone(), iface_name)
}
}
11 changes: 11 additions & 0 deletions src/channel/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: MIT

mod attr;
mod get;
mod handle;

pub(crate) use attr::parse_channel_nlas;

pub use attr::EthtoolChannelAttr;
pub use get::EthtoolChannelGetRequest;
pub use handle::EthtoolChannelHandle;
10 changes: 7 additions & 3 deletions src/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ use netlink_packet_generic::GenlMessage;
use netlink_packet_utils::DecodeError;

use crate::{
try_ethtool, EthtoolCoalesceHandle, EthtoolError, EthtoolFeatureHandle,
EthtoolLinkModeHandle, EthtoolMessage, EthtoolPauseHandle,
EthtoolRingHandle, EthtoolTsInfoHandle,
try_ethtool, EthtoolChannelHandle, EthtoolCoalesceHandle, EthtoolError,
EthtoolFeatureHandle, EthtoolLinkModeHandle, EthtoolMessage,
EthtoolPauseHandle, EthtoolRingHandle, EthtoolTsInfoHandle,
};

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -40,6 +40,10 @@ impl EthtoolHandle {
EthtoolRingHandle::new(self.clone())
}

pub fn channel(&mut self) -> EthtoolChannelHandle {
EthtoolChannelHandle::new(self.clone())
}

pub fn coalesce(&mut self) -> EthtoolCoalesceHandle {
EthtoolCoalesceHandle::new(self.clone())
}
Expand Down
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: MIT

mod bitset_util;
mod channel;
mod coalesce;
mod connection;
mod error;
Expand All @@ -14,6 +15,9 @@ mod pause;
mod ring;
mod tsinfo;

pub use channel::{
EthtoolChannelAttr, EthtoolChannelGetRequest, EthtoolChannelHandle,
};
pub use coalesce::{
EthtoolCoalesceAttr, EthtoolCoalesceGetRequest, EthtoolCoalesceHandle,
};
Expand Down
Loading