Skip to content
Merged
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
121 changes: 89 additions & 32 deletions awkernel_drivers/src/pcie/intel/igc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,20 @@ pub struct Igc {
}

impl Igc {
fn service_queue(inner: &IgcInner, que_id: usize) -> Result<(), IgcDriverErr> {
{
let mut node = MCSNode::new();
let mut rx = inner.queue_info.que[que_id].rx.lock(&mut node);
inner.igc_rx_recv(que_id, &mut rx)?;
}

let mut node = MCSNode::new();
let mut tx = inner.queue_info.que[que_id].tx.lock(&mut node);
inner.igc_txeof(que_id, &mut tx)?;

Ok(())
}

fn new(mut info: PCIeInfo) -> Result<Self, PCIeDeviceErr> {
use PCIeDeviceErr::InitFailure;

Expand Down Expand Up @@ -265,47 +279,58 @@ impl Igc {
let igc_icr = read_reg(&inner.info, igc_regs::IGC_ICR)?;
let irq_queue = irq.and_then(|irq| inner.queue_info.irqs_to_queues.get(&irq).copied());

let que_result = if let Some(que_id) = irq_queue {
let rx_result = {
let mut node = MCSNode::new();
let mut rx = inner.queue_info.que[que_id].rx.lock(&mut node);
inner.igc_rx_recv(que_id, &mut rx)
};
let tx_result = {
let mut node = MCSNode::new();
let mut tx = inner.queue_info.que[que_id].tx.lock(&mut node);
inner.igc_txeof(&mut tx)
};
rx_result.and(tx_result)
} else {
Ok(())
};
// Accumulate errors rather than short-circuiting with `?`: the EIMS/IMS re-arm
// writes below must execute regardless of queue-service failures, otherwise the
// interrupt line stays masked permanently.
let mut result: Result<(), IgcDriverErr> = Ok(());

if let Some(que_id) = irq_queue {
if let Err(e) = Self::service_queue(&inner, que_id) {
result = Err(e);
}
}

Comment thread
atsushi421 marked this conversation as resolved.
let should_poll_link = irq.is_none() && (igc_icr & igc_defines::IGC_ICR_LSC) == 0;

if (igc_icr & igc_defines::IGC_ICR_LSC) != 0 {
// Link status change interrupt.
drop(inner);
{
let mut inner = self.inner.write();
inner.igc_intr_link()?;
if let Err(e) = inner.igc_intr_link() {
result = Err(e);
}
}
inner = self.inner.read();
} else if irq.is_none() {
} else if should_poll_link {
drop(inner);
{
let mut inner = self.inner.write();
inner.igc_poll_link()?;
if let Err(e) = inner.igc_poll_link() {
result = Err(e);
}
}
inner = self.inner.read();
}

if irq.is_none() {
for que_id in 0..inner.queue_info.que.len() {
if let Err(e) = Self::service_queue(&inner, que_id) {
result = Err(e);
}
}
}

let msix_linkmask = 1 << inner.queue_info.que.len();
let msix_queuesmask = (1 << inner.queue_info.que.len()) - 1;
write_reg(&inner.info, igc_regs::IGC_IMS, igc_defines::IGC_IMS_LSC)?;
write_reg(
&inner.info,
igc_regs::IGC_EIMS,
1 << inner.queue_info.que.len(),
msix_queuesmask | msix_linkmask,
)?;

que_result
result
}
}

Expand Down Expand Up @@ -365,9 +390,15 @@ impl NetDevice for Igc {
return false;
}

let mut node = MCSNode::new();
let mut tx = inner.queue_info.que[0].tx.lock(&mut node);
inner.igc_txeof(&mut tx).is_ok() && tx.igc_desc_unused() > 0
for que_id in 0..inner.queue_info.que.len() {
let mut node = MCSNode::new();
let tx = inner.queue_info.que[que_id].tx.lock(&mut node);
if tx.igc_desc_unused() > 0 {
return true;
}
}

false
}

fn capabilities(&self) -> net_device::NetCapabilities {
Expand Down Expand Up @@ -896,7 +927,10 @@ impl IgcInner {
Ok(())
}

fn igc_txeof(&self, tx: &mut Tx) -> Result<(), IgcDriverErr> {
// _que_id is unused: descriptor reclaim is driven entirely by the DD writeback bit in
// descriptor memory; the hardware head register is not required. The parameter is
// retained so all queue-scoped helpers share a uniform signature.
fn igc_txeof(&self, _que_id: usize, tx: &mut Tx) -> Result<(), IgcDriverErr> {
Comment thread
atsushi421 marked this conversation as resolved.
membar_sync();

loop {
Expand Down Expand Up @@ -925,7 +959,7 @@ impl IgcInner {
que_id: usize,
Comment thread
atsushi421 marked this conversation as resolved.
ether_frame: net_device::EtherFrameRef,
) -> Result<(), IgcDriverErr> {
if que_id != 0 {
if que_id >= self.queue_info.que.len() {
return Err(IgcDriverErr::Param);
}

Expand All @@ -943,7 +977,7 @@ impl IgcInner {

let mut node = MCSNode::new();
let mut tx = self.queue_info.que[que_id].tx.lock(&mut node);
self.igc_txeof(&mut tx)?;
self.igc_txeof(que_id, &mut tx)?;

if tx.igc_desc_unused() == 0 {
return Ok(());
Expand Down Expand Up @@ -984,7 +1018,9 @@ impl IgcInner {
}

fn igc_rx_recv(&self, que_id: usize, rx: &mut Rx) -> Result<(), IgcDriverErr> {
debug_assert_eq!(que_id, 0, "multi-queue not yet supported (planned for PR5)");
if que_id >= self.queue_info.que.len() {
return Err(IgcDriverErr::Param);
}

if rx.read_buf.is_none() {
return Ok(());
Expand Down Expand Up @@ -1258,6 +1294,29 @@ fn igc_is_valid_ether_addr(addr: &[u8; 6]) -> bool {
!(addr[0] & 1 != 0 || addr.iter().all(|&x| x == 0))
}

/// Select the number of Rx/Tx queue pairs to enable.
///
/// The result is constrained by three factors:
/// 1. **MSI-X vectors**: one vector per queue plus one for link events.
/// 2. **CPU count**: no benefit in having more queues than CPUs.
/// 3. **Hardware cap**: IGC/I225 supports at most 4 RSS queues.
///
/// The result is rounded down to the nearest power of two (1, 2, or 4) so that
/// the 128-entry RSS redirection table divides evenly among queues, giving each
/// queue an equal share of hashed flows.
fn igc_select_num_queues(available_vectors: usize) -> usize {
Comment thread
atsushi421 marked this conversation as resolved.
let cpu_count = core::cmp::max(1, awkernel_lib::cpu::num_cpu());
let available = core::cmp::min(core::cmp::min(available_vectors, cpu_count), 4);

if available >= 4 {
Comment thread
atsushi421 marked this conversation as resolved.
4
} else if available >= 2 {
2
} else {
1
}
}

/// Allocate PCI resources for the IGC device.
/// This function initialize IRQs for the IGC device,
/// and returns IRQs for the Rx/Tx queues and an IRQ for events.
Expand All @@ -1275,12 +1334,10 @@ fn igc_allocate_pci_resources(info: &mut PCIeInfo) -> Result<(Vec<IRQ>, IRQ), PC
}

let nmsix = nmsix - 1; // Give one vector to events.

// Limit the driver to a single Rx/Tx queue for now.
let nqueues = core::cmp::min(nmsix, 1);
let nqueues = igc_select_num_queues(nmsix as usize);

// Initialize the IRQs for the Rx/Tx queues.
let mut irqs_queues = Vec::with_capacity(nqueues as usize);
let mut irqs_queues = Vec::with_capacity(nqueues);

for q in 0..nqueues {
let irq_name_rxtx = format!("{DEVICE_SHORT_NAME}-{bdf}-RxTx{q}");
Expand All @@ -1292,7 +1349,7 @@ fn igc_allocate_pci_resources(info: &mut PCIeInfo) -> Result<(Vec<IRQ>, IRQ), PC
}),
segment_number,
awkernel_lib::cpu::raw_cpu_id() as u32,
q as usize,
q,
)
.or(Err(PCIeDeviceErr::InitFailure))?;
irq_rxtx.enable();
Expand Down
Loading