Skip to content
This repository has been archived by the owner on Oct 26, 2022. It is now read-only.

Commit

Permalink
ethtool: Add ring support
Browse files Browse the repository at this point in the history
Equivalent to `ethtool -g <nic_name>` command.

Example code included.

Signed-off-by: Gris Ge <cnfourt@gmail.com>
  • Loading branch information
cathay4t committed Sep 27, 2021
1 parent bc43fd6 commit 2ec5f17
Show file tree
Hide file tree
Showing 8 changed files with 254 additions and 0 deletions.
29 changes: 29 additions & 0 deletions ethtool/examples/dump_rings.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use ethtool;
use futures::stream::TryStreamExt;
use tokio;

// Once we find a way to load netsimdev kernel module in CI, we can convert this
// to a test
fn main() {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_io()
.build()
.unwrap();
rt.block_on(get_ring(None));
}

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

let mut ring_handle = handle.ring().get(iface_name).execute().await;

let mut msgs = Vec::new();
while let Some(msg) = ring_handle.try_next().await.unwrap() {
msgs.push(msg);
}
assert!(msgs.len() > 0);
for msg in msgs {
println!("{:?}", msg);
}
}
5 changes: 5 additions & 0 deletions ethtool/src/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::{
EthtoolLinkModeHandle,
EthtoolMessage,
EthtoolPauseHandle,
EthtoolRingHandle,
};

#[derive(Clone, Debug)]
Expand All @@ -35,6 +36,10 @@ impl EthtoolHandle {
EthtoolLinkModeHandle::new(self.clone())
}

pub fn ring(&mut self) -> EthtoolRingHandle {
EthtoolRingHandle::new(self.clone())
}

pub async fn request(
&mut self,
message: NetlinkMessage<GenlMessage<EthtoolMessage>>,
Expand Down
2 changes: 2 additions & 0 deletions ethtool/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod link_mode;
mod macros;
mod message;
mod pause;
mod ring;

pub use connection::new_connection;
pub use error::EthtoolError;
Expand All @@ -31,5 +32,6 @@ pub use pause::{
EthtoolPauseHandle,
EthtoolPauseStatAttr,
};
pub use ring::{EthtoolRingAttr, EthtoolRingGetRequest, EthtoolRingHandle};

pub(crate) use handle::ethtool_execute;
28 changes: 28 additions & 0 deletions ethtool/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::{
feature::{parse_feature_nlas, EthtoolFeatureAttr},
link_mode::{parse_link_mode_nlas, EthtoolLinkModeAttr},
pause::{parse_pause_nlas, EthtoolPauseAttr},
ring::{parse_ring_nlas, EthtoolRingAttr},
EthtoolHeader,
};

Expand All @@ -15,6 +16,8 @@ const ETHTOOL_MSG_FEATURES_GET: u8 = 11;
const ETHTOOL_MSG_FEATURES_GET_REPLY: u8 = 11;
const ETHTOOL_MSG_LINKMODES_GET: u8 = 4;
const ETHTOOL_MSG_LINKMODES_GET_REPLY: u8 = 4;
const ETHTOOL_MSG_RINGS_GET: u8 = 15;
const ETHTOOL_MSG_RINGS_GET_REPLY: u8 = 16;

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum EthtoolCmd {
Expand All @@ -24,6 +27,8 @@ pub enum EthtoolCmd {
FeatureGetReply,
LinkModeGet,
LinkModeGetReply,
RingGet,
RingGetReply,
}

impl From<EthtoolCmd> for u8 {
Expand All @@ -35,6 +40,8 @@ impl From<EthtoolCmd> for u8 {
EthtoolCmd::FeatureGetReply => ETHTOOL_MSG_FEATURES_GET_REPLY,
EthtoolCmd::LinkModeGet => ETHTOOL_MSG_LINKMODES_GET,
EthtoolCmd::LinkModeGetReply => ETHTOOL_MSG_LINKMODES_GET_REPLY,
EthtoolCmd::RingGet => ETHTOOL_MSG_RINGS_GET,
EthtoolCmd::RingGetReply => ETHTOOL_MSG_RINGS_GET_REPLY,
}
}
}
Expand All @@ -44,6 +51,7 @@ pub enum EthtoolAttr {
Pause(EthtoolPauseAttr),
Feature(EthtoolFeatureAttr),
LinkMode(EthtoolLinkModeAttr),
Ring(EthtoolRingAttr),
}

impl Nla for EthtoolAttr {
Expand All @@ -52,6 +60,7 @@ impl Nla for EthtoolAttr {
Self::Pause(attr) => attr.value_len(),
Self::Feature(attr) => attr.value_len(),
Self::LinkMode(attr) => attr.value_len(),
Self::Ring(attr) => attr.value_len(),
}
}

Expand All @@ -60,6 +69,7 @@ impl Nla for EthtoolAttr {
Self::Pause(attr) => attr.kind(),
Self::Feature(attr) => attr.kind(),
Self::LinkMode(attr) => attr.kind(),
Self::Ring(attr) => attr.kind(),
}
}

Expand All @@ -68,6 +78,7 @@ impl Nla for EthtoolAttr {
Self::Pause(attr) => attr.emit_value(buffer),
Self::Feature(attr) => attr.emit_value(buffer),
Self::LinkMode(attr) => attr.emit_value(buffer),
Self::Ring(attr) => attr.emit_value(buffer),
}
}
}
Expand Down Expand Up @@ -131,6 +142,19 @@ impl EthtoolMessage {
nlas,
}
}

pub fn new_ring_get(iface_name: Option<&str>) -> Self {
let nlas = match iface_name {
Some(s) => vec![EthtoolAttr::Ring(EthtoolRingAttr::Header(vec![
EthtoolHeader::DevName(s.to_string()),
]))],
None => vec![EthtoolAttr::Ring(EthtoolRingAttr::Header(vec![]))],
};
EthtoolMessage {
cmd: EthtoolCmd::RingGet,
nlas,
}
}
}

impl Emitable for EthtoolMessage {
Expand Down Expand Up @@ -158,6 +182,10 @@ impl ParseableParametrized<[u8], GenlHeader> for EthtoolMessage {
cmd: EthtoolCmd::LinkModeGetReply,
nlas: parse_link_mode_nlas(buffer)?,
},
ETHTOOL_MSG_RINGS_GET_REPLY => Self {
cmd: EthtoolCmd::RingGetReply,
nlas: parse_ring_nlas(buffer)?,
},
cmd => {
return Err(DecodeError::from(format!(
"Unsupported ethtool reply command: {}",
Expand Down
137 changes: 137 additions & 0 deletions ethtool/src/ring/attr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
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_RINGS_HEADER: u16 = 1;
const ETHTOOL_A_RINGS_RX_MAX: u16 = 2;
const ETHTOOL_A_RINGS_RX_MINI_MAX: u16 = 3;
const ETHTOOL_A_RINGS_RX_JUMBO_MAX: u16 = 4;
const ETHTOOL_A_RINGS_TX_MAX: u16 = 5;
const ETHTOOL_A_RINGS_RX: u16 = 6;
const ETHTOOL_A_RINGS_RX_MINI: u16 = 7;
const ETHTOOL_A_RINGS_RX_JUMBO: u16 = 8;
const ETHTOOL_A_RINGS_TX: u16 = 9;

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum EthtoolRingAttr {
Header(Vec<EthtoolHeader>),
RxMax(u32),
RxMiniMax(u32),
RxJumboMax(u32),
TxMax(u32),
Rx(u32),
RxMini(u32),
RxJumbo(u32),
Tx(u32),
Other(DefaultNla),
}

impl Nla for EthtoolRingAttr {
fn value_len(&self) -> usize {
match self {
Self::Header(hdrs) => hdrs.as_slice().buffer_len(),
Self::RxMax(_)
| Self::RxMiniMax(_)
| Self::RxJumboMax(_)
| Self::TxMax(_)
| Self::Rx(_)
| Self::RxMini(_)
| Self::RxJumbo(_)
| Self::Tx(_) => 4,
Self::Other(attr) => attr.value_len(),
}
}

fn kind(&self) -> u16 {
match self {
Self::Header(_) => ETHTOOL_A_RINGS_HEADER | NLA_F_NESTED,
Self::RxMax(_) => ETHTOOL_A_RINGS_RX_MAX,
Self::RxMiniMax(_) => ETHTOOL_A_RINGS_RX_MINI_MAX,
Self::RxJumboMax(_) => ETHTOOL_A_RINGS_RX_JUMBO_MAX,
Self::TxMax(_) => ETHTOOL_A_RINGS_TX_MAX,
Self::Rx(_) => ETHTOOL_A_RINGS_RX,
Self::RxMini(_) => ETHTOOL_A_RINGS_RX_MINI,
Self::RxJumbo(_) => ETHTOOL_A_RINGS_RX_JUMBO,
Self::Tx(_) => ETHTOOL_A_RINGS_TX,
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::RxMiniMax(d)
| Self::RxJumboMax(d)
| Self::TxMax(d)
| Self::Rx(d)
| Self::RxMini(d)
| Self::RxJumbo(d)
| Self::Tx(d) => NativeEndian::write_u32(buffer, *d),
Self::Other(ref attr) => attr.emit(buffer),
}
}
}

impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for EthtoolRingAttr {
fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
let payload = buf.value();
Ok(match buf.kind() {
ETHTOOL_A_RINGS_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_RINGS_RX_MAX => {
Self::RxMax(parse_u32(payload).context("Invalid ETHTOOL_A_RINGS_RX_MAX value")?)
}

ETHTOOL_A_RINGS_RX_MINI_MAX => Self::RxMiniMax(
parse_u32(payload).context("Invalid ETHTOOL_A_RINGS_RX_MINI_MAX value")?,
),
ETHTOOL_A_RINGS_RX_JUMBO_MAX => Self::RxJumboMax(
parse_u32(payload).context("Invalid ETHTOOL_A_RINGS_RX_JUMBO_MAX value")?,
),
ETHTOOL_A_RINGS_TX_MAX => {
Self::TxMax(parse_u32(payload).context("Invalid ETHTOOL_A_RINGS_TX_MAX value")?)
}
ETHTOOL_A_RINGS_RX => {
Self::Rx(parse_u32(payload).context("Invalid ETHTOOL_A_RINGS_RX value")?)
}
ETHTOOL_A_RINGS_RX_MINI => {
Self::RxMini(parse_u32(payload).context("Invalid ETHTOOL_A_RINGS_RX_MINI value")?)
}
ETHTOOL_A_RINGS_RX_JUMBO => {
Self::RxJumbo(parse_u32(payload).context("Invalid ETHTOOL_A_RINGS_RX_JUMBO value")?)
}
ETHTOOL_A_RINGS_TX => {
Self::Tx(parse_u32(payload).context("Invalid ETHTOOL_A_RINGS_TX value")?)
}
_ => Self::Other(DefaultNla::parse(buf).context("invalid NLA (unknown kind)")?),
})
}
}

pub(crate) fn parse_ring_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 = EthtoolRingAttr::parse(nla).context(error_msg)?;
nlas.push(EthtoolAttr::Ring(parsed));
}
Ok(nlas)
}
30 changes: 30 additions & 0 deletions ethtool/src/ring/get.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use futures::TryStream;
use netlink_packet_generic::GenlMessage;

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

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

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

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

let ethtool_msg = EthtoolMessage::new_ring_get(iface_name.as_deref());
ethtool_execute(&mut handle, iface_name.is_none(), ethtool_msg).await
}
}
14 changes: 14 additions & 0 deletions ethtool/src/ring/handle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use crate::{EthtoolHandle, EthtoolRingGetRequest};

pub struct EthtoolRingHandle(EthtoolHandle);

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

/// Retrieve the ethtool rings of a interface (equivalent to `ethtool -g eth1`)
pub fn get(&mut self, iface_name: Option<&str>) -> EthtoolRingGetRequest {
EthtoolRingGetRequest::new(self.0.clone(), iface_name)
}
}
9 changes: 9 additions & 0 deletions ethtool/src/ring/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
mod attr;
mod get;
mod handle;

pub(crate) use attr::parse_ring_nlas;

pub use attr::EthtoolRingAttr;
pub use get::EthtoolRingGetRequest;
pub use handle::EthtoolRingHandle;

0 comments on commit 2ec5f17

Please sign in to comment.