1pub use crate::bnic_driver::RxConfig;
7pub use crate::resources::ResourceArena;
8
9use crate::bnic_driver::BnicDriver;
10use crate::bnic_driver::WqConfig;
11use crate::gdma_driver::GdmaDriver;
12use crate::queues;
13use crate::queues::Doorbell;
14use crate::queues::DoorbellPage;
15use anyhow::Context;
16use futures::StreamExt;
17use futures::lock::Mutex;
18use gdma_defs::GdmaDevId;
19use gdma_defs::GdmaDevType;
20use gdma_defs::GdmaQueueType;
21use gdma_defs::GdmaRegisterDeviceResp;
22use gdma_defs::bnic::ManaQueryDeviceCfgResp;
23use gdma_defs::bnic::ManaQueryFilterStateResponse;
24use gdma_defs::bnic::ManaQueryStatisticsResponse;
25use gdma_defs::bnic::ManaQueryVportCfgResp;
26use gdma_defs::bnic::STATISTICS_FLAGS_ALL;
27use inspect::Inspect;
28use net_backend_resources::mac_address::MacAddress;
29use pal_async::driver::SpawnDriver;
30use pal_async::task::Spawn;
31use pal_async::task::Task;
32use std::sync::Arc;
33use tracing::Instrument;
34use user_driver::DeviceBacking;
35use user_driver::DmaClient;
36use user_driver::interrupt::DeviceInterrupt;
37use user_driver::memory::MemoryBlock;
38use user_driver::memory::PAGE_SIZE;
39use vmcore::vm_task::VmTaskDriverSource;
40
41enum LinkStatus {
42 Default,
43 Pending(bool),
44 Active {
45 sender: mesh::Sender<bool>,
46 connected: bool,
47 },
48}
49
50#[derive(Inspect)]
52pub struct ManaDevice<T: DeviceBacking> {
53 #[inspect(skip)]
54 inner: Arc<Inner<T>>,
55 #[inspect(skip)]
56 inspect_task: Task<()>,
57 #[inspect(skip)]
58 hwc_task: Option<Task<()>>,
59 #[inspect(flatten, send = "|x| x")]
60 inspect_send: mesh::Sender<inspect::Deferred>,
61}
62
63struct Inner<T: DeviceBacking> {
64 gdma: Mutex<GdmaDriver<T>>,
65 dev_id: GdmaDevId,
66 dev_data: GdmaRegisterDeviceResp,
67 dev_config: ManaQueryDeviceCfgResp,
68 doorbell: Arc<dyn Doorbell>,
69 vport_link_status: Arc<Mutex<Vec<LinkStatus>>>,
70}
71
72impl<T: DeviceBacking> ManaDevice<T> {
73 pub async fn new(
75 driver: &impl SpawnDriver,
76 device: T,
77 num_vps: u32,
78 max_queues_per_vport: u16,
79 ) -> anyhow::Result<Self> {
80 let mut gdma = GdmaDriver::new(driver, device, num_vps, None)
81 .instrument(tracing::info_span!("new_gdma_driver"))
82 .await?;
83 gdma.test_eq().await?;
84
85 gdma.verify_vf_driver_version().await?;
86
87 let dev_id = gdma
88 .list_devices()
89 .await?
90 .iter()
91 .copied()
92 .find(|dev_id| dev_id.ty == GdmaDevType::GDMA_DEVICE_MANA)
93 .context("no mana device found")?;
94
95 let dev_data = gdma.register_device(dev_id).await?;
96
97 let mut bnic = BnicDriver::new(&mut gdma, dev_id);
98 let dev_config = bnic.query_dev_config().await?;
99 tracing::info!(mana_dev_config = ?dev_config);
100 let num_queues_needed = dev_config.max_num_vports as u32 * max_queues_per_vport as u32;
101 gdma.check_vf_resources(num_vps, num_queues_needed);
102
103 let doorbell = gdma.doorbell();
104 let vport_link_status = (0..dev_config.max_num_vports)
105 .map(|_| LinkStatus::Default)
106 .collect();
107 let inner = Arc::new(Inner {
108 gdma: Mutex::new(gdma),
109 dev_id,
110 dev_data,
111 dev_config,
112 doorbell,
113 vport_link_status: Arc::new(Mutex::new(vport_link_status)),
114 });
115
116 let (inspect_send, mut inspect_recv) = mesh::channel::<inspect::Deferred>();
117 let inspect_task = driver.spawn("mana-inspect", {
118 let inner = inner.clone();
119 async move {
120 while let Some(deferred) = inspect_recv.next().await {
121 let Inner {
122 gdma,
123 dev_id: _,
124 dev_data: _,
125 dev_config: _,
126 doorbell: _,
127 vport_link_status: _,
128 } = inner.as_ref();
129 let gdma = gdma.lock().await;
130 deferred.respond(|resp| {
131 resp.merge(&*gdma);
132 })
133 }
134 }
135 });
136
137 let device = Self {
138 inner,
139 inspect_send,
140 inspect_task,
141 hwc_task: None,
142 };
143 Ok(device)
144 }
145
146 pub fn num_vports(&self) -> u32 {
148 self.inner.dev_config.max_num_vports.into()
149 }
150
151 pub fn dev_config(&self) -> &ManaQueryDeviceCfgResp {
153 &self.inner.dev_config
154 }
155
156 pub async fn start_notification_task(&mut self, driver_source: &VmTaskDriverSource) {
159 if self.hwc_task.is_some() {
160 return;
161 }
162
163 let inner = self.inner.clone();
164 let hwc_task = driver_source.simple().spawn("mana-hwc", {
165 let mut gdma = self.inner.gdma.lock().await;
166 let mut hwc_event = gdma.hwc_subscribe();
167 async move {
168 loop {
169 hwc_event.wait().await;
170 let mut gdma = inner.gdma.lock().await;
171 if gdma.process_all_eqs() {
172 let mut vport_link_status = inner.vport_link_status.lock().await;
173 for (vport_index, current) in gdma.get_link_toggle_list() {
174 let vport_index = vport_index as usize;
175 if vport_index >= vport_link_status.len() {
176 tracing::error!(vport_index, "Invalid vport index");
177 continue;
178 }
179 if let LinkStatus::Active { sender, connected } =
180 &mut vport_link_status[vport_index]
181 {
182 *connected = current;
183 sender.send(*connected);
184 } else {
185 let _ = std::mem::replace(
186 &mut vport_link_status[vport_index],
187 LinkStatus::Pending(current),
188 );
189 }
190 }
191 }
192 }
193 }
194 });
195 self.hwc_task = Some(hwc_task);
196 }
197
198 pub async fn new_vport(
200 &self,
201 index: u32,
202 vport_state: Option<VportState>,
203 dev_config: &ManaQueryDeviceCfgResp,
204 ) -> anyhow::Result<Vport<T>> {
205 let vport_config = self.query_vport_config(index).await?;
206
207 let vport_state = vport_state.unwrap_or(VportState::new(None, None));
208
209 let vport = Vport {
210 inner: self.inner.clone(),
211 config: vport_config,
212 vport_state,
213 id: index,
214 };
215
216 if dev_config.cap_filter_state_query() {
217 if let Ok(resp) = vport.query_filter_state(vport.id.into()).await {
218 tracing::debug!(
219 mac_address = %vport.mac_address(),
220 direction_to_vtl0 = resp.direction_to_vtl0,
221 "query_filter_state"
222 );
223 vport
224 .vport_state
225 .set_direction_to_vtl0(resp.direction_to_vtl0 == 1);
226 }
227 }
228
229 Ok(vport)
230 }
231
232 pub async fn shutdown(self) -> (anyhow::Result<()>, T) {
234 self.inspect_task.cancel().await;
235 if let Some(hwc_task) = self.hwc_task {
236 hwc_task.cancel().await;
237 }
238 let inner = Arc::into_inner(self.inner).unwrap();
239 let mut driver = inner.gdma.into_inner();
240 let result = driver.deregister_device(inner.dev_id).await;
241 (result, driver.into_device())
242 }
243 pub async fn query_vport_config(&self, vport: u32) -> anyhow::Result<ManaQueryVportCfgResp> {
245 let mut gdma = self.inner.gdma.lock().await;
246 BnicDriver::new(&mut *gdma, self.inner.dev_id)
247 .query_vport_config(vport)
248 .await
249 }
250}
251
252#[derive(Clone)]
254pub struct VportState {
255 direction_to_vtl0: Arc<parking_lot::Mutex<Option<bool>>>,
256 state_change_callback: Arc<Option<Box<dyn Fn(bool) + Send + Sync>>>,
257}
258
259impl VportState {
260 pub fn new(
262 direction_to_vtl0: Option<bool>,
263 state_change_callback: Option<Box<dyn Fn(bool) + Send + Sync>>,
264 ) -> Self {
265 Self {
266 direction_to_vtl0: Arc::new(parking_lot::Mutex::new(direction_to_vtl0)),
267 state_change_callback: Arc::new(state_change_callback),
268 }
269 }
270
271 pub fn set_direction_to_vtl0(&self, direction_to_vtl0: bool) {
273 *self.direction_to_vtl0.lock() = Some(direction_to_vtl0);
274 if let Some(callback) = self.state_change_callback.as_ref() {
275 (callback)(direction_to_vtl0);
276 }
277 }
278
279 pub fn get_direction_to_vtl0(&self) -> Option<bool> {
281 *self.direction_to_vtl0.lock()
282 }
283}
284
285pub struct Vport<T: DeviceBacking> {
287 inner: Arc<Inner<T>>,
288 config: ManaQueryVportCfgResp,
289 vport_state: VportState,
290 id: u32,
291}
292
293impl<T: DeviceBacking> Vport<T> {
294 pub fn max_tx_queues(&self) -> u32 {
296 self.config.max_num_sq
297 }
298
299 pub fn max_rx_queues(&self) -> u32 {
301 self.config.max_num_rq
302 }
303
304 pub fn mac_address(&self) -> MacAddress {
306 self.config.mac_addr.into()
307 }
308
309 pub fn gpa_mkey(&self) -> u32 {
311 self.inner.dev_data.gpa_mkey
312 }
313
314 pub fn id(&self) -> u32 {
316 self.id
317 }
318
319 pub fn num_indirection_ent(&self) -> u32 {
321 self.config.num_indirection_ent
322 }
323
324 pub async fn new_eq(
326 &self,
327 arena: &mut ResourceArena,
328 size: u32,
329 cpu: u32,
330 ) -> anyhow::Result<BnicEq> {
331 let mut gdma = self.inner.gdma.lock().await;
332 let dma_client = gdma.device().dma_client();
333 let mem = dma_client
334 .allocate_dma_buffer(size as usize)
335 .context("Failed to allocate DMA buffer")?;
336
337 let gdma_region = gdma
338 .create_dma_region(arena, self.inner.dev_id, mem.clone())
339 .await
340 .context("failed to create eq dma region")?;
341 let (id, interrupt) = gdma
342 .create_eq(
343 arena,
344 self.inner.dev_id,
345 gdma_region,
346 size,
347 self.inner.dev_data.pdid,
348 self.inner.dev_data.db_id,
349 cpu,
350 )
351 .await
352 .context("failed to create eq")?;
353 Ok(BnicEq {
354 doorbell: DoorbellPage::new(self.inner.doorbell.clone(), self.inner.dev_data.db_id)?,
355 mem,
356 id,
357 interrupt,
358 })
359 }
360
361 pub async fn new_wq(
363 &self,
364 arena: &mut ResourceArena,
365 is_send: bool,
366 wq_size: u32,
367 cq_size: u32,
368 eq_id: u32,
369 ) -> anyhow::Result<BnicWq> {
370 assert!(wq_size >= PAGE_SIZE as u32 && wq_size.is_power_of_two());
371 assert!(cq_size >= PAGE_SIZE as u32 && cq_size.is_power_of_two());
372 let mut gdma = self.inner.gdma.lock().await;
373
374 let dma_client = gdma.device().dma_client();
375
376 let mem = dma_client
377 .allocate_dma_buffer((wq_size + cq_size) as usize)
378 .context("failed to allocate DMA buffer")?;
379
380 let wq_mem = mem.subblock(0, wq_size as usize);
381 let cq_mem = mem.subblock(wq_size as usize, cq_size as usize);
382
383 let wq_gdma_region = gdma
384 .create_dma_region(arena, self.inner.dev_id, wq_mem.clone())
385 .await?;
386 let cq_gdma_region = gdma
387 .create_dma_region(arena, self.inner.dev_id, cq_mem.clone())
388 .await?;
389 let wq_type = if is_send {
390 GdmaQueueType::GDMA_SQ
391 } else {
392 GdmaQueueType::GDMA_RQ
393 };
394 let doorbell = DoorbellPage::new(self.inner.doorbell.clone(), self.inner.dev_data.db_id)?;
395 let resp = BnicDriver::new(&mut *gdma, self.inner.dev_id)
396 .create_wq_obj(
397 arena,
398 self.config.vport,
399 wq_type,
400 &WqConfig {
401 wq_gdma_region,
402 cq_gdma_region,
403 wq_size,
404 cq_size,
405 cq_moderation_ctx_id: 0,
406 eq_id,
407 },
408 )
409 .await?;
410
411 Ok(BnicWq {
412 doorbell,
413 wq_mem,
414 cq_mem,
415 wq_id: resp.wq_id,
416 cq_id: resp.cq_id,
417 is_send,
418 wq_obj: resp.wq_obj,
419 })
420 }
421
422 pub async fn config_tx(&self) -> anyhow::Result<TxConfig> {
424 let mut gdma = self.inner.gdma.lock().await;
425 let resp = BnicDriver::new(&mut *gdma, self.inner.dev_id)
426 .config_vport_tx(
427 self.config.vport,
428 self.inner.dev_data.pdid,
429 self.inner.dev_data.db_id,
430 )
431 .await?;
432
433 let config = TxConfig {
434 tx_vport_offset: resp.tx_vport_offset,
435 };
436 Ok(config)
437 }
438
439 pub async fn config_rx(&self, config: &RxConfig<'_>) -> anyhow::Result<()> {
441 let mut gdma = self.inner.gdma.lock().await;
442 BnicDriver::new(&mut *gdma, self.inner.dev_id)
443 .config_vport_rx(self.config.vport, config)
444 .await?;
445
446 Ok(())
447 }
448
449 pub async fn move_filter(&self, direction_to_vtl0: u8) -> anyhow::Result<()> {
451 if let Some(to_vtl0) = self.vport_state.get_direction_to_vtl0() {
452 if to_vtl0 == (direction_to_vtl0 == 1) {
453 return Ok(());
454 }
455 }
456 let mut gdma = self.inner.gdma.lock().await;
457 let hwc_activity_id = BnicDriver::new(&mut *gdma, self.inner.dev_id)
458 .move_vport_filter(self.config.vport, direction_to_vtl0)
459 .await?;
460 self.vport_state
461 .set_direction_to_vtl0(direction_to_vtl0 == 1);
462 tracing::info!(
463 mac_address = %self.mac_address(),
464 direction_to_vtl0,
465 hwc_activity_id,
466 "switch data path for mac",
467 );
468 Ok(())
469 }
470
471 pub async fn get_direction_to_vtl0(&self) -> Option<bool> {
473 self.vport_state.get_direction_to_vtl0()
474 }
475
476 pub async fn set_serial_no(&self, serial_no: u32) -> anyhow::Result<()> {
478 let mut gdma = self.inner.gdma.lock().await;
479 BnicDriver::new(&mut *gdma, self.inner.dev_id)
480 .set_vport_serial_no(self.config.vport, serial_no)
481 .await?;
482 Ok(())
483 }
484
485 pub async fn query_stats(&self) -> anyhow::Result<ManaQueryStatisticsResponse> {
487 let mut gdma = self.inner.gdma.lock().await;
488 BnicDriver::new(&mut *gdma, self.inner.dev_id)
489 .query_stats(STATISTICS_FLAGS_ALL)
490 .await
491 }
492
493 pub async fn query_filter_state(
495 &self,
496 vport: u64,
497 ) -> anyhow::Result<ManaQueryFilterStateResponse> {
498 let mut gdma = self.inner.gdma.lock().await;
499 BnicDriver::new(&mut *gdma, self.inner.dev_id)
500 .query_filter_state(vport)
501 .await
502 }
503
504 pub async fn destroy(&self, arena: ResourceArena) {
506 let mut gdma = self.inner.gdma.lock().await;
507 arena.destroy(&mut *gdma).await;
508 }
509
510 pub async fn retarget_interrupt(
512 &self,
513 eq_id: u32,
514 cpu: u32,
515 ) -> anyhow::Result<Option<DeviceInterrupt>> {
516 let mut gdma = self.inner.gdma.lock().await;
517 gdma.retarget_eq(self.inner.dev_id, eq_id, cpu).await
518 }
519
520 pub async fn register_link_status_notifier(&self, sender: mesh::Sender<bool>) {
522 let mut vport_link_status = self.inner.vport_link_status.lock().await;
523 let vport_index = self.id as usize;
524 let (send, connected) = match vport_link_status[vport_index] {
525 LinkStatus::Pending(connected) => (true, connected),
527 LinkStatus::Active { connected, .. } => (!connected, connected),
529 _ => (false, true),
531 };
532 if send {
533 sender.send(connected);
534 }
535 vport_link_status[vport_index] = LinkStatus::Active { sender, connected };
536 }
537
538 pub async fn dma_client(&self) -> Arc<dyn DmaClient> {
540 self.inner.gdma.lock().await.device().dma_client()
541 }
542}
543
544pub struct TxConfig {
546 pub tx_vport_offset: u16,
548}
549
550pub struct BnicEq {
552 doorbell: DoorbellPage,
553 mem: MemoryBlock,
554 id: u32,
555 interrupt: DeviceInterrupt,
556}
557
558impl BnicEq {
559 pub fn id(&self) -> u32 {
561 self.id
562 }
563
564 pub fn interrupt(&self) -> DeviceInterrupt {
566 self.interrupt.clone()
567 }
568
569 pub fn queue(&self) -> queues::Eq {
571 queues::Eq::new_eq(self.mem.clone(), self.doorbell.clone(), self.id)
572 }
573}
574
575pub struct BnicWq {
577 doorbell: DoorbellPage,
578 wq_mem: MemoryBlock,
579 cq_mem: MemoryBlock,
580 wq_id: u32,
581 cq_id: u32,
582 is_send: bool,
583 wq_obj: u64,
584}
585
586impl BnicWq {
587 pub fn wq(&self) -> queues::Wq {
589 if self.is_send {
590 queues::Wq::new_sq(self.wq_mem.clone(), self.doorbell.clone(), self.wq_id)
591 } else {
592 queues::Wq::new_rq(self.wq_mem.clone(), self.doorbell.clone(), self.wq_id)
593 }
594 }
595
596 pub fn cq(&self) -> queues::Cq {
598 queues::Cq::new_cq(self.cq_mem.clone(), self.doorbell.clone(), self.cq_id)
599 }
600
601 pub fn wq_obj(&self) -> u64 {
603 self.wq_obj
604 }
605}