Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
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
148 changes: 134 additions & 14 deletions awkernel_drivers/src/pcie/intel/igc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,15 @@ use awkernel_lib::{
dma_pool::DMAPool,
interrupt::IRQ,
net::{
ether::{ETHER_ADDR_LEN, ETHER_MAX_LEN, ETHER_TYPE_VLAN},
ether::{
extract_headers, EtherHeader, NetworkHdr, TransportHdr, ETHER_ADDR_LEN, ETHER_MAX_LEN,
ETHER_TYPE_VLAN,
},
multicast::MulticastAddrs,
net_device::{self, LinkStatus, NetCapabilities, NetDevice, NetFlags},
net_device::{self, LinkStatus, NetCapabilities, NetDevice, NetFlags, PacketHeaderFlags},
tcp::TCPHdr,
toeplitz::stoeplitz_to_key,
udp::UDPHdr,
},
paging::PAGESIZE,
sync::{mcs::MCSNode, mutex::Mutex, rwlock::RwLock},
Expand Down Expand Up @@ -114,11 +119,20 @@ struct Rx {
dropped_pkts: u64,
}

#[derive(Debug, PartialEq)]
enum ActiveChecksumContext {
None,
Ipv4,
TcpIpv4,
UdpIpv4,
}

struct Tx {
next_avail_desc: usize,
next_to_clean: usize,
tx_desc_ring: DMAPool<TxRing>,
write_buf: Option<DMAPool<TxBuffer>>,
active_checksum_context: ActiveChecksumContext,
}

struct Queue {
Expand Down Expand Up @@ -764,7 +778,10 @@ impl IgcInner {
multicast_addrs: MulticastAddrs::new(),
if_flags: NetFlags::BROADCAST | NetFlags::SIMPLEX | NetFlags::MULTICAST,
queue_info,
capabilities: NetCapabilities::VLAN_MTU,
capabilities: NetCapabilities::VLAN_MTU
| NetCapabilities::CSUM_IPv4
| NetCapabilities::CSUM_TCPv4
| NetCapabilities::CSUM_UDPv4,
}
}

Expand Down Expand Up @@ -954,6 +971,104 @@ impl IgcInner {
Ok(())
}

/// Setup an Advanced TX Context Descriptor for checksum offload.
///
/// Returns `(ctx_desc_count, data_olinfo_status)` where:
/// - `ctx_desc_count` is 0 (context reused or not needed) or 1 (new context written)
/// - `data_olinfo_status` is the complete olinfo_status value for the data descriptor
fn igc_tx_ctx_setup(
&self,
tx: &mut Tx,
ether_frame: &net_device::EtherFrameRef,
head: usize,
) -> Result<(usize, u32), IgcDriverErr> {
let base_olinfo = (ether_frame.data.len() as u32) << IGC_ADVTXD_PAYLEN_SHIFT;

let ext = match extract_headers(ether_frame.data) {
Ok(e) => e,
Err(_) => return Ok((0, base_olinfo)),
};

let mut olinfo_status = base_olinfo;
let mut type_tucmd_mlhl = 0u32;
let mut vlan_macip_lens = 0u32;
let mut offload = false;

vlan_macip_lens |= (core::mem::size_of::<EtherHeader>() as u32) << IGC_ADVTXD_MACLEN_SHIFT;

let iphlen = match &ext.network {
NetworkHdr::Ipv4(ip) => {
if ether_frame
.csum_flags
.contains(PacketHeaderFlags::IPV4_CSUM_OUT)
{
olinfo_status |= IGC_TXD_POPTS_IXSM << IGC_ADVTXD_POPTS_SHIFT;
offload = true;
}
type_tucmd_mlhl |= IGC_ADVTXD_TUCMD_IPV4;
ip.header_len() as u32
}
Comment on lines +1008 to +1010
_ => return Ok((0, olinfo_status)),
};

vlan_macip_lens |= iphlen;

let (l4len, new_ctx) = match &ext.transport {
TransportHdr::Tcp(_) => {
type_tucmd_mlhl |= IGC_ADVTXD_TUCMD_L4T_TCP;
if ether_frame
.csum_flags
.contains(PacketHeaderFlags::TCP_CSUM_OUT)
{
olinfo_status |= IGC_TXD_POPTS_TXSM << IGC_ADVTXD_POPTS_SHIFT;
offload = true;
}
(
core::mem::size_of::<TCPHdr>() as u32,
ActiveChecksumContext::TcpIpv4,
)
}
TransportHdr::Udp(_) => {
type_tucmd_mlhl |= IGC_ADVTXD_TUCMD_L4T_UDP;
if ether_frame
.csum_flags
.contains(PacketHeaderFlags::UDP_CSUM_OUT)
{
olinfo_status |= IGC_TXD_POPTS_TXSM << IGC_ADVTXD_POPTS_SHIFT;
offload = true;
}
(
core::mem::size_of::<UDPHdr>() as u32,
ActiveChecksumContext::UdpIpv4,
)
}
_ => (0, ActiveChecksumContext::Ipv4),
};

if !offload {
return Ok((0, olinfo_status));
}

// Reuse the active context descriptor when the checksum context is unchanged.
if tx.active_checksum_context == new_ctx {
return Ok((0, olinfo_status));
}

// Write a new context descriptor at head.
type_tucmd_mlhl |= IGC_ADVTXD_DTYP_CTXT | IGC_TXD_CMD_DEXT;
let mss_l4len_idx = l4len << IGC_ADVTXD_L4LEN_SHIFT;

let desc = &mut tx.tx_desc_ring.as_mut()[head];
desc.adv_ctx.vlan_macip_lens = u32::to_le(vlan_macip_lens);
desc.adv_ctx.ts.launch_time = u32::to_le(0);
desc.adv_ctx.type_tucmd_mlhl = u32::to_le(type_tucmd_mlhl);
Comment on lines +1061 to +1064
desc.adv_ctx.mss_l4len_idx = u32::to_le(mss_l4len_idx);

tx.active_checksum_context = new_ctx;

Ok((1, olinfo_status))
}

fn igc_send(
&self,
que_id: usize,
Expand All @@ -979,24 +1094,27 @@ impl IgcInner {
let mut tx = self.queue_info.que[que_id].tx.lock(&mut node);
self.igc_txeof(que_id, &mut tx)?;

if tx.igc_desc_unused() == 0 {
let head = tx.next_avail_desc;
let ring_len = tx.tx_desc_ring.as_ref().len();

let (ctx_count, data_olinfo_status) = self.igc_tx_ctx_setup(&mut tx, &ether_frame, head)?;

let needed = ctx_count + 1;
if tx.igc_desc_unused() < needed {
return Ok(());
}
Comment on lines +1097 to 1105

let idx = tx.next_avail_desc;
let next_idx = if idx + 1 == tx.tx_desc_ring.as_ref().len() {
0
} else {
idx + 1
};
let data_idx = (head + ctx_count) % ring_len;
let next_idx = (data_idx + 1) % ring_len;

let buffer_addr = {
let write_buf = tx.write_buf.as_mut().ok_or(IgcDriverErr::DmaPoolAlloc)?;
let dst = &mut write_buf.as_mut()[idx];
let dst = &mut write_buf.as_mut()[data_idx];
dst[..ether_frame.data.len()].copy_from_slice(ether_frame.data);
(write_buf.get_phy_addr().as_usize() + idx * TX_BUFFER_SIZE) as u64
(write_buf.get_phy_addr().as_usize() + data_idx * TX_BUFFER_SIZE) as u64
};

let desc = &mut tx.tx_desc_ring.as_mut()[idx];
let desc = &mut tx.tx_desc_ring.as_mut()[data_idx];
let read = unsafe { &mut desc.read };
read.buffer_addr = u64::to_le(buffer_addr);
read.cmd_type_len = u32::to_le(
Expand All @@ -1007,7 +1125,7 @@ impl IgcInner {
| IGC_TXD_CMD_IFCS
| IGC_TXD_CMD_RS,
);
read.olinfo_status = u32::to_le((ether_frame.data.len() as u32) << IGC_ADVTXD_PAYLEN_SHIFT);
read.olinfo_status = u32::to_le(data_olinfo_status);

membar_sync();
write_reg(&self.info, igc_regs::IGC_TDT(que_id), next_idx as u32)?;
Expand Down Expand Up @@ -1419,6 +1537,7 @@ fn igc_allocate_queues(
)
.ok_or(PCIeDeviceErr::InitFailure)?,
write_buf: None,
active_checksum_context: ActiveChecksumContext::None,
});

que.push(Queue { rx, tx, me: n });
Expand Down Expand Up @@ -1584,6 +1703,7 @@ impl Tx {
// Reset indices
self.next_avail_desc = 0;
self.next_to_clean = 0;
self.active_checksum_context = ActiveChecksumContext::None;
self.write_buf = Some(
DMAPool::new(
self.tx_desc_ring.get_numa_id(),
Expand Down
1 change: 1 addition & 0 deletions awkernel_drivers/src/pcie/intel/igc/igc_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub(super) const IGC_SRRCTL_DESCTYPE_ADV_ONEBUF: u32 = 0x02000000;
pub(super) union IgcAdvTxDesc {
pub(super) read: TxDescRead,
pub(super) wb: TxDescWb,
pub(super) adv_ctx: IgcAdvTxContextDesc,
}

#[derive(Debug, Clone, Copy)]
Expand Down
7 changes: 7 additions & 0 deletions awkernel_drivers/src/pcie/intel/igc/igc_defines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,13 @@ pub(super) const AUTONEG_ADVERTISE_SPEED_DEFAULT_2500: u16 = IGC_ALL_SPEED_DUPLE
pub(super) const IGC_TXD_DTYP_D: u32 = 0x00100000; // Data Descriptor
pub(super) const IGC_TXD_DTYP_C: u32 = 0x00000000; // Context Descriptor
pub(super) const IGC_ADVTXD_DTYP_DATA: u32 = 0x00300000; // Advanced Data Descriptor
pub(super) const IGC_ADVTXD_DTYP_CTXT: u32 = 0x00200000; // Advanced Context Descriptor
pub(super) const IGC_ADVTXD_MACLEN_SHIFT: u32 = 9; // MAC header length shift in vlan_macip_lens
pub(super) const IGC_ADVTXD_L4LEN_SHIFT: u32 = 8; // L4 header length shift in mss_l4len_idx
pub(super) const IGC_ADVTXD_TUCMD_IPV4: u32 = 0x00000400; // IP Packet Type: IPv4
pub(super) const IGC_ADVTXD_TUCMD_L4T_UDP: u32 = 0x00000000; // L4 Packet TYPE of UDP
pub(super) const IGC_ADVTXD_TUCMD_L4T_TCP: u32 = 0x00000800; // L4 Packet TYPE of TCP
pub(super) const IGC_ADVTXD_POPTS_SHIFT: u32 = 8; // POPTS field offset in olinfo_status
pub(super) const IGC_TXD_POPTS_IXSM: u32 = 0x01; // Insert IP checksum
pub(super) const IGC_TXD_POPTS_TXSM: u32 = 0x02; // Insert TCP/UDP checksum
pub(super) const IGC_TXD_CMD_EOP: u32 = 0x01000000; // End of Packet
Expand Down
18 changes: 7 additions & 11 deletions awkernel_lib/src/net/if_net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,21 +109,17 @@ impl Device for NetDriverRef<'_> {

let capabilities = self.inner.capabilities();

// Capability bits determine whether TX checksum work stays in software
// or is handed to the NIC. Checksum::Rx means smoltcp validates on RX
// while the NIC inserts checksums on TX.

if capabilities.contains(NetCapabilities::CSUM_IPv4) {
cap.checksum.ipv4 = Checksum::Rx;
}

// Note: Awkernel doen't yet support Ipv6.
// Additionally, tests for TCP functionality have not yet been conducted.
// Checksum offload currently only supports UDPv4.

// if capabilities.contains(NetCapabilities::CSUM_TCPv4 | NetCapabilities::CSUM_TCPv6) {
// cap.checksum.tcp = Checksum::Rx;
// }

// if capabilities.contains(NetCapabilities::CSUM_UDPv4 | NetCapabilities::CSUM_UDPv6) {
// cap.checksum.udp = Checksum::Rx;
// }
if capabilities.contains(NetCapabilities::CSUM_TCPv4) {
cap.checksum.tcp = Checksum::Rx;
}

if capabilities.contains(NetCapabilities::CSUM_UDPv4) {
cap.checksum.udp = Checksum::Rx;
Expand Down
Loading