mana_driver/
mana.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! The interface to the MANA device.
5
6pub 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/// A MANA device.
51#[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    /// Initializes the MANA driver on `device`.
74    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    /// Returns the number of vports the device supports.
147    pub fn num_vports(&self) -> u32 {
148        self.inner.dev_config.max_num_vports.into()
149    }
150
151    /// Returns the device configuration.
152    pub fn dev_config(&self) -> &ManaQueryDeviceCfgResp {
153        &self.inner.dev_config
154    }
155
156    /// Starts a hardware channel (HWC) task that listens to events on the HWC
157    /// and calls the appropriate provided callsbacks/closure.
158    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    /// Initializes and returns the vport number `index`.
199    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    /// Shuts the device down.
233    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    /// Queries the configuration of a specific vport.
244    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/// Tracks vport state and optionally notifies a listener of changes.
253#[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    /// Create a new VportState instance.
261    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    /// Remember current filter setting.
272    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    /// Get current filter setting if known.
280    pub fn get_direction_to_vtl0(&self) -> Option<bool> {
281        *self.direction_to_vtl0.lock()
282    }
283}
284
285/// A MANA vport.
286pub 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    /// Returns the maximum number of transmit queues.
295    pub fn max_tx_queues(&self) -> u32 {
296        self.config.max_num_sq
297    }
298
299    /// Returns the maximum number of receive queues.
300    pub fn max_rx_queues(&self) -> u32 {
301        self.config.max_num_rq
302    }
303
304    /// Returns the assigned MAC address.
305    pub fn mac_address(&self) -> MacAddress {
306        self.config.mac_addr.into()
307    }
308
309    /// Returns the memory key to refer to all of GPA space.
310    pub fn gpa_mkey(&self) -> u32 {
311        self.inner.dev_data.gpa_mkey
312    }
313
314    /// Returns this vport's id
315    pub fn id(&self) -> u32 {
316        self.id
317    }
318
319    /// Returns the number of indirection entries supported by the vport
320    pub fn num_indirection_ent(&self) -> u32 {
321        self.config.num_indirection_ent
322    }
323
324    /// Creates a new event queue.
325    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    /// Creates a new work queue (transmit or receive).
362    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    /// Get the transmit configuration.
423    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    /// Sets the receive configuration.
440    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    /// Move filter between VTL2 VF vport and VTL0 VF vport
450    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    /// Get current filter state.
472    pub async fn get_direction_to_vtl0(&self) -> Option<bool> {
473        self.vport_state.get_direction_to_vtl0()
474    }
475
476    /// Set the vport serial number
477    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    /// Gets stats. Note that these are adapter-wide and not really per-vport.
486    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    /// Retrieves vport mac filter state from socamana
494    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    /// Destroys resources in `arena`.
505    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    /// Changes the target CPU for the given eq to `cpu`.
511    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    /// Registers for link status notification updates.
521    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            // Send any pending notifications, whatever it is.
526            LinkStatus::Pending(connected) => (true, connected),
527            // Endpoint reestablishing connection. Only send, if the link is down.
528            LinkStatus::Active { connected, .. } => (!connected, connected),
529            // Don't send anything when transitioning from the default state.
530            _ => (false, true),
531        };
532        if send {
533            sender.send(connected);
534        }
535        vport_link_status[vport_index] = LinkStatus::Active { sender, connected };
536    }
537
538    /// Returns an object that can allocate dma memory to be shared with the device.
539    pub async fn dma_client(&self) -> Arc<dyn DmaClient> {
540        self.inner.gdma.lock().await.device().dma_client()
541    }
542}
543
544/// Transmit configuration.
545pub struct TxConfig {
546    /// The vport offset to include in tx packets.
547    pub tx_vport_offset: u16,
548}
549
550/// An event queue.
551pub struct BnicEq {
552    doorbell: DoorbellPage,
553    mem: MemoryBlock,
554    id: u32,
555    interrupt: DeviceInterrupt,
556}
557
558impl BnicEq {
559    /// The event queue ID.
560    pub fn id(&self) -> u32 {
561        self.id
562    }
563
564    /// The interrupt that will be signaled when the armed event queue is ready.
565    pub fn interrupt(&self) -> DeviceInterrupt {
566        self.interrupt.clone()
567    }
568
569    /// Gets an object to access the queue's entries.
570    pub fn queue(&self) -> queues::Eq {
571        queues::Eq::new_eq(self.mem.clone(), self.doorbell.clone(), self.id)
572    }
573}
574
575/// A work queue (transmit or receive).
576pub 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    /// Gets the work queue for sending requests.
588    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    /// Gets the completion queue for receiving results.
597    pub fn cq(&self) -> queues::Cq {
598        queues::Cq::new_cq(self.cq_mem.clone(), self.doorbell.clone(), self.cq_id)
599    }
600
601    /// Gets the work queue object ID.
602    pub fn wq_obj(&self) -> u64 {
603        self.wq_obj
604    }
605}