pub use crate::bnic_driver::RxConfig;
pub use crate::resources::ResourceArena;
use crate::bnic_driver::BnicDriver;
use crate::bnic_driver::WqConfig;
use crate::gdma_driver::GdmaDriver;
use crate::queues;
use crate::queues::Doorbell;
use crate::queues::DoorbellPage;
use anyhow::Context;
use futures::StreamExt;
use futures::lock::Mutex;
use gdma_defs::GdmaDevId;
use gdma_defs::GdmaDevType;
use gdma_defs::GdmaQueueType;
use gdma_defs::GdmaRegisterDeviceResp;
use gdma_defs::bnic::ManaQueryDeviceCfgResp;
use gdma_defs::bnic::ManaQueryFilterStateResponse;
use gdma_defs::bnic::ManaQueryStatisticsResponse;
use gdma_defs::bnic::ManaQueryVportCfgResp;
use gdma_defs::bnic::STATISTICS_FLAGS_ALL;
use inspect::Inspect;
use net_backend_resources::mac_address::MacAddress;
use pal_async::driver::SpawnDriver;
use pal_async::task::Spawn;
use pal_async::task::Task;
use std::sync::Arc;
use user_driver::DeviceBacking;
use user_driver::DmaClient;
use user_driver::interrupt::DeviceInterrupt;
use user_driver::memory::MemoryBlock;
use user_driver::memory::PAGE_SIZE;
use vmcore::vm_task::VmTaskDriverSource;
enum LinkStatus {
Default,
Pending(bool),
Active {
sender: mesh::Sender<bool>,
connected: bool,
},
}
pub struct ManaDevice<T: DeviceBacking> {
inner: Arc<Inner<T>>,
inspect_task: Task<()>,
hwc_task: Option<Task<()>>,
inspect_send: mesh::Sender<inspect::Deferred>,
}
impl<T: DeviceBacking> Inspect for ManaDevice<T> {
fn inspect(&self, req: inspect::Request<'_>) {
self.inspect_send.send(req.defer());
}
}
struct Inner<T: DeviceBacking> {
gdma: Mutex<GdmaDriver<T>>,
dev_id: GdmaDevId,
dev_data: GdmaRegisterDeviceResp,
dev_config: ManaQueryDeviceCfgResp,
doorbell: Arc<dyn Doorbell>,
vport_link_status: Arc<Mutex<Vec<LinkStatus>>>,
}
impl<T: DeviceBacking> ManaDevice<T> {
pub async fn new(
driver: &impl SpawnDriver,
device: T,
num_vps: u32,
max_queues_per_vport: u16,
) -> anyhow::Result<Self> {
let mut gdma = GdmaDriver::new(driver, device, num_vps).await?;
gdma.test_eq().await?;
gdma.verify_vf_driver_version().await?;
let dev_id = gdma
.list_devices()
.await?
.iter()
.copied()
.find(|dev_id| dev_id.ty == GdmaDevType::GDMA_DEVICE_MANA)
.context("no mana device found")?;
let dev_data = gdma.register_device(dev_id).await?;
let mut bnic = BnicDriver::new(&mut gdma, dev_id);
let dev_config = bnic.query_dev_config().await?;
tracing::info!(mana_dev_config = ?dev_config);
let num_queues_needed = dev_config.max_num_vports as u32 * max_queues_per_vport as u32;
gdma.check_vf_resources(num_vps, num_queues_needed);
let doorbell = gdma.doorbell();
let vport_link_status = (0..dev_config.max_num_vports)
.map(|_| LinkStatus::Default)
.collect();
let inner = Arc::new(Inner {
gdma: Mutex::new(gdma),
dev_id,
dev_data,
dev_config,
doorbell,
vport_link_status: Arc::new(Mutex::new(vport_link_status)),
});
let (inspect_send, mut inspect_recv) = mesh::channel::<inspect::Deferred>();
let inspect_task = driver.spawn("mana-inspect", {
let inner = inner.clone();
async move {
while let Some(deferred) = inspect_recv.next().await {
let Inner {
gdma,
dev_id: _,
dev_data: _,
dev_config: _,
doorbell: _,
vport_link_status: _,
} = inner.as_ref();
let gdma = gdma.lock().await;
deferred.respond(|resp| {
resp.merge(&*gdma);
})
}
}
});
let device = Self {
inner,
inspect_send,
inspect_task,
hwc_task: None,
};
Ok(device)
}
pub fn num_vports(&self) -> u32 {
self.inner.dev_config.max_num_vports.into()
}
pub fn dev_config(&self) -> &ManaQueryDeviceCfgResp {
&self.inner.dev_config
}
pub async fn start_notification_task(&mut self, driver_source: &VmTaskDriverSource) {
if self.hwc_task.is_some() {
return;
}
let inner = self.inner.clone();
let hwc_task = driver_source.simple().spawn("mana-hwc", {
let mut gdma = self.inner.gdma.lock().await;
let mut hwc_event = gdma.hwc_subscribe();
async move {
loop {
hwc_event.wait().await;
let mut gdma = inner.gdma.lock().await;
if gdma.process_all_eqs() {
let mut vport_link_status = inner.vport_link_status.lock().await;
for (vport_index, current) in gdma.get_link_toggle_list() {
let vport_index = vport_index as usize;
if vport_index >= vport_link_status.len() {
tracing::error!(vport_index, "Invalid vport index");
continue;
}
if let LinkStatus::Active { sender, connected } =
&mut vport_link_status[vport_index]
{
*connected = current;
sender.send(*connected);
} else {
let _ = std::mem::replace(
&mut vport_link_status[vport_index],
LinkStatus::Pending(current),
);
}
}
}
}
}
});
self.hwc_task = Some(hwc_task);
}
pub async fn new_vport(
&self,
index: u32,
vport_state: Option<VportState>,
dev_config: &ManaQueryDeviceCfgResp,
) -> anyhow::Result<Vport<T>> {
let vport_config = self.query_vport_config(index).await?;
let vport_state = vport_state.unwrap_or(VportState::new(None, None));
let vport = Vport {
inner: self.inner.clone(),
config: vport_config,
vport_state,
id: index,
};
if dev_config.cap_filter_state_query() {
if let Ok(resp) = vport.query_filter_state(vport.id.into()).await {
tracing::debug!(
mac_address = %vport.mac_address(),
direction_to_vtl0 = resp.direction_to_vtl0,
"query_filter_state"
);
vport
.vport_state
.set_direction_to_vtl0(resp.direction_to_vtl0 == 1);
}
}
Ok(vport)
}
pub async fn shutdown(self) -> (anyhow::Result<()>, T) {
self.inspect_task.cancel().await;
if let Some(hwc_task) = self.hwc_task {
hwc_task.cancel().await;
}
let inner = Arc::into_inner(self.inner).unwrap();
let mut driver = inner.gdma.into_inner();
let result = driver.deregister_device(inner.dev_id).await;
(result, driver.into_device())
}
pub async fn query_vport_config(&self, vport: u32) -> anyhow::Result<ManaQueryVportCfgResp> {
let mut gdma = self.inner.gdma.lock().await;
BnicDriver::new(&mut *gdma, self.inner.dev_id)
.query_vport_config(vport)
.await
}
}
#[derive(Clone)]
pub struct VportState {
direction_to_vtl0: Arc<parking_lot::Mutex<Option<bool>>>,
state_change_callback: Arc<Option<Box<dyn Fn(bool) + Send + Sync>>>,
}
impl VportState {
pub fn new(
direction_to_vtl0: Option<bool>,
state_change_callback: Option<Box<dyn Fn(bool) + Send + Sync>>,
) -> Self {
Self {
direction_to_vtl0: Arc::new(parking_lot::Mutex::new(direction_to_vtl0)),
state_change_callback: Arc::new(state_change_callback),
}
}
pub fn set_direction_to_vtl0(&self, direction_to_vtl0: bool) {
*self.direction_to_vtl0.lock() = Some(direction_to_vtl0);
if let Some(callback) = self.state_change_callback.as_ref() {
(callback)(direction_to_vtl0);
}
}
pub fn get_direction_to_vtl0(&self) -> Option<bool> {
let direction_to_vtl0 = *self.direction_to_vtl0.lock();
direction_to_vtl0
}
}
pub struct Vport<T: DeviceBacking> {
inner: Arc<Inner<T>>,
config: ManaQueryVportCfgResp,
vport_state: VportState,
id: u32,
}
impl<T: DeviceBacking> Vport<T> {
pub fn max_tx_queues(&self) -> u32 {
self.config.max_num_sq
}
pub fn max_rx_queues(&self) -> u32 {
self.config.max_num_rq
}
pub fn mac_address(&self) -> MacAddress {
self.config.mac_addr.into()
}
pub fn gpa_mkey(&self) -> u32 {
self.inner.dev_data.gpa_mkey
}
pub fn id(&self) -> u32 {
self.id
}
pub fn num_indirection_ent(&self) -> u32 {
self.config.num_indirection_ent
}
pub async fn new_eq(
&self,
arena: &mut ResourceArena,
size: u32,
cpu: u32,
) -> anyhow::Result<BnicEq> {
let mut gdma = self.inner.gdma.lock().await;
let dma_client = gdma.device().dma_client();
let mem = dma_client
.allocate_dma_buffer(size as usize)
.context("Failed to allocate DMA buffer")?;
let gdma_region = gdma
.create_dma_region(arena, self.inner.dev_id, mem.clone())
.await
.context("failed to create eq dma region")?;
let (id, interrupt) = gdma
.create_eq(
arena,
self.inner.dev_id,
gdma_region,
size,
self.inner.dev_data.pdid,
self.inner.dev_data.db_id,
cpu,
)
.await
.context("failed to create eq")?;
Ok(BnicEq {
doorbell: DoorbellPage::new(self.inner.doorbell.clone(), self.inner.dev_data.db_id)?,
mem,
id,
interrupt,
})
}
pub async fn new_wq(
&self,
arena: &mut ResourceArena,
is_send: bool,
wq_size: u32,
cq_size: u32,
eq_id: u32,
) -> anyhow::Result<BnicWq> {
assert!(wq_size >= PAGE_SIZE as u32 && wq_size.is_power_of_two());
assert!(cq_size >= PAGE_SIZE as u32 && cq_size.is_power_of_two());
let mut gdma = self.inner.gdma.lock().await;
let dma_client = gdma.device().dma_client();
let mem = dma_client
.allocate_dma_buffer((wq_size + cq_size) as usize)
.context("failed to allocate DMA buffer")?;
let wq_mem = mem.subblock(0, wq_size as usize);
let cq_mem = mem.subblock(wq_size as usize, cq_size as usize);
let wq_gdma_region = gdma
.create_dma_region(arena, self.inner.dev_id, wq_mem.clone())
.await?;
let cq_gdma_region = gdma
.create_dma_region(arena, self.inner.dev_id, cq_mem.clone())
.await?;
let wq_type = if is_send {
GdmaQueueType::GDMA_SQ
} else {
GdmaQueueType::GDMA_RQ
};
let doorbell = DoorbellPage::new(self.inner.doorbell.clone(), self.inner.dev_data.db_id)?;
let resp = BnicDriver::new(&mut *gdma, self.inner.dev_id)
.create_wq_obj(
arena,
self.config.vport,
wq_type,
&WqConfig {
wq_gdma_region,
cq_gdma_region,
wq_size,
cq_size,
cq_moderation_ctx_id: 0,
eq_id,
},
)
.await?;
Ok(BnicWq {
doorbell,
wq_mem,
cq_mem,
wq_id: resp.wq_id,
cq_id: resp.cq_id,
is_send,
wq_obj: resp.wq_obj,
})
}
pub async fn config_tx(&self) -> anyhow::Result<TxConfig> {
let mut gdma = self.inner.gdma.lock().await;
let resp = BnicDriver::new(&mut *gdma, self.inner.dev_id)
.config_vport_tx(
self.config.vport,
self.inner.dev_data.pdid,
self.inner.dev_data.db_id,
)
.await?;
let config = TxConfig {
tx_vport_offset: resp.tx_vport_offset,
};
Ok(config)
}
pub async fn config_rx(&self, config: &RxConfig<'_>) -> anyhow::Result<()> {
let mut gdma = self.inner.gdma.lock().await;
BnicDriver::new(&mut *gdma, self.inner.dev_id)
.config_vport_rx(self.config.vport, config)
.await?;
Ok(())
}
pub async fn move_filter(&self, direction_to_vtl0: u8) -> anyhow::Result<()> {
if let Some(to_vtl0) = self.vport_state.get_direction_to_vtl0() {
if to_vtl0 == (direction_to_vtl0 == 1) {
return Ok(());
}
}
let mut gdma = self.inner.gdma.lock().await;
let hwc_activity_id = BnicDriver::new(&mut *gdma, self.inner.dev_id)
.move_vport_filter(self.config.vport, direction_to_vtl0)
.await?;
self.vport_state
.set_direction_to_vtl0(direction_to_vtl0 == 1);
tracing::info!(
mac_address = %self.mac_address(),
direction_to_vtl0,
hwc_activity_id,
"switch data path for mac",
);
Ok(())
}
pub async fn get_direction_to_vtl0(&self) -> Option<bool> {
self.vport_state.get_direction_to_vtl0()
}
pub async fn set_serial_no(&self, serial_no: u32) -> anyhow::Result<()> {
let mut gdma = self.inner.gdma.lock().await;
BnicDriver::new(&mut *gdma, self.inner.dev_id)
.set_vport_serial_no(self.config.vport, serial_no)
.await?;
Ok(())
}
pub async fn query_stats(&self) -> anyhow::Result<ManaQueryStatisticsResponse> {
let mut gdma = self.inner.gdma.lock().await;
BnicDriver::new(&mut *gdma, self.inner.dev_id)
.query_stats(STATISTICS_FLAGS_ALL)
.await
}
pub async fn query_filter_state(
&self,
vport: u64,
) -> anyhow::Result<ManaQueryFilterStateResponse> {
let mut gdma = self.inner.gdma.lock().await;
BnicDriver::new(&mut *gdma, self.inner.dev_id)
.query_filter_state(vport)
.await
}
pub async fn destroy(&self, arena: ResourceArena) {
let mut gdma = self.inner.gdma.lock().await;
arena.destroy(&mut *gdma).await;
}
pub async fn retarget_interrupt(
&self,
eq_id: u32,
cpu: u32,
) -> anyhow::Result<Option<DeviceInterrupt>> {
let mut gdma = self.inner.gdma.lock().await;
gdma.retarget_eq(self.inner.dev_id, eq_id, cpu).await
}
pub async fn register_link_status_notifier(&self, sender: mesh::Sender<bool>) {
let mut vport_link_status = self.inner.vport_link_status.lock().await;
let vport_index = self.id as usize;
let (send, connected) = match vport_link_status[vport_index] {
LinkStatus::Pending(connected) => (true, connected),
LinkStatus::Active { connected, .. } => (!connected, connected),
_ => (false, true),
};
if send {
sender.send(connected);
}
vport_link_status[vport_index] = LinkStatus::Active { sender, connected };
}
pub async fn dma_client(&self) -> Arc<dyn DmaClient> {
self.inner.gdma.lock().await.device().dma_client()
}
}
pub struct TxConfig {
pub tx_vport_offset: u16,
}
pub struct BnicEq {
doorbell: DoorbellPage,
mem: MemoryBlock,
id: u32,
interrupt: DeviceInterrupt,
}
impl BnicEq {
pub fn id(&self) -> u32 {
self.id
}
pub fn interrupt(&self) -> DeviceInterrupt {
self.interrupt.clone()
}
pub fn queue(&self) -> queues::Eq {
queues::Eq::new_eq(self.mem.clone(), self.doorbell.clone(), self.id)
}
}
pub struct BnicWq {
doorbell: DoorbellPage,
wq_mem: MemoryBlock,
cq_mem: MemoryBlock,
wq_id: u32,
cq_id: u32,
is_send: bool,
wq_obj: u64,
}
impl BnicWq {
pub fn wq(&self) -> queues::Wq {
if self.is_send {
queues::Wq::new_sq(self.wq_mem.clone(), self.doorbell.clone(), self.wq_id)
} else {
queues::Wq::new_rq(self.wq_mem.clone(), self.doorbell.clone(), self.wq_id)
}
}
pub fn cq(&self) -> queues::Cq {
queues::Cq::new_cq(self.cq_mem.clone(), self.doorbell.clone(), self.cq_id)
}
pub fn wq_obj(&self) -> u64 {
self.wq_obj
}
}