Skip to main content

monarch_rdma/backend/ibverbs/
mlx_queue_pair.rs

1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 * All rights reserved.
4 *
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8
9//! mlx5 queue pair built on the mlx5dv extended verbs.
10
11use std::io::Error;
12use std::result::Result;
13use std::sync::Arc;
14
15use super::domain::IbvDomain;
16use super::domain::IbvDomainImpl;
17use super::memory_region::IbvMemoryRegionView;
18use super::memory_region::IbvRemoteMemoryRegionView;
19use super::primitives::GidScope;
20use super::primitives::GidType;
21use super::primitives::IbvConfig;
22use super::primitives::IbvCq;
23use super::primitives::IbvQp;
24use super::primitives::IbvQpInfo;
25use super::queue_pair::IbvQueuePair;
26use super::queue_pair::RCQueuePair;
27
28/// An mlx5 RC queue pair created through `mlx5dv_create_qp`, so it carries the
29/// mlx5dv send-ops flags that arm a direct-WQE/doorbell data path.
30///
31/// Operations delegate to an inner [`RCQueuePair`], driving the QP
32/// through the standard `ibv_post_send`/`ibv_poll_cq` verbs; the mlx5dv
33/// send-ops flags are not exercised by that path.
34#[derive(Debug)]
35pub struct MlxQueuePair(RCQueuePair);
36
37impl MlxQueuePair {
38    /// Creates the `mlx5dv` RC QP against `domain`'s context and PD, reporting
39    /// its completions on `send_cq`/`recv_cq`, returned as an [`IbvQp`] holding
40    /// a clone of each of them and of the PD. The QP carries the mlx5dv send-ops
41    /// flags that arm its extended work-request builder. A null context or PD on
42    /// `domain` yields `Err`.
43    ///
44    /// # Safety
45    ///
46    /// `domain`'s context and PD, if non-null, must be live. `send_cq` and
47    /// `recv_cq` must each hold a live `ibv_cq` created on that same context:
48    /// `mlx5dv_create_qp` binds the QP to both, and the device reports its
49    /// completions through them for as long as the QP lives.
50    pub(super) unsafe fn create_ibv_qp<I: IbvDomainImpl>(
51        domain: &IbvDomain<I>,
52        config: &IbvConfig,
53        send_cq: Arc<IbvCq>,
54        recv_cq: Arc<IbvCq>,
55    ) -> Result<IbvQp, anyhow::Error> {
56        let context = domain.context().as_ptr();
57        let pd = domain.as_ptr();
58        if pd.is_null() {
59            anyhow::bail!("cannot create an MlxQueuePair on a null protection domain");
60        }
61
62        // An mlx5dv extended RC QP with the caps from `config`. The
63        // `SEND_OPS_FLAGS` enable the mlx5dv extended work-request builder; the
64        // standard verbs this QP runs on ignore them.
65        let mut init_attr = rdmaxcel_sys::ibv_qp_init_attr_ex {
66            send_cq: send_cq.as_ptr(),
67            recv_cq: recv_cq.as_ptr(),
68            cap: rdmaxcel_sys::ibv_qp_cap {
69                max_send_wr: config.max_send_wr,
70                max_recv_wr: config.max_recv_wr,
71                max_send_sge: config.max_send_sge,
72                max_recv_sge: config.max_recv_sge,
73                max_inline_data: 0,
74            },
75            qp_type: rdmaxcel_sys::ibv_qp_type::IBV_QPT_RC,
76            sq_sig_all: 0,
77            pd,
78            comp_mask: rdmaxcel_sys::IBV_QP_INIT_ATTR_PD
79                | rdmaxcel_sys::IBV_QP_INIT_ATTR_SEND_OPS_FLAGS,
80            send_ops_flags: (rdmaxcel_sys::IBV_QP_EX_WITH_RDMA_WRITE
81                | rdmaxcel_sys::IBV_QP_EX_WITH_RDMA_READ
82                | rdmaxcel_sys::IBV_QP_EX_WITH_SEND) as u64,
83            ..Default::default()
84        };
85        let mut mlx5dv_attr = rdmaxcel_sys::mlx5dv_qp_init_attr {
86            comp_mask: rdmaxcel_sys::MLX5DV_QP_INIT_ATTR_MASK_SEND_OPS_FLAGS as u64,
87            send_ops_flags: (rdmaxcel_sys::MLX5DV_QP_EX_WITH_MKEY_CONFIGURE
88                | rdmaxcel_sys::MLX5DV_QP_EX_WITH_MR_LIST) as u64,
89            ..Default::default()
90        };
91        // SAFETY: `context` and `pd` are non-null (checked above) and live (an
92        // `IbvDomain` holds null-or-live pointers); both attr structs are fully
93        // initialized and outlive the call, and their CQ pointers came from the
94        // freshly created `send_cq`/`recv_cq`. `mlx5dv_create_qp` returns null on
95        // failure.
96        let qp =
97            unsafe { rdmaxcel_sys::mlx5dv_create_qp(context, &mut init_attr, &mut mlx5dv_attr) };
98        if qp.is_null() {
99            anyhow::bail!(
100                "failed to create mlx5dv queue pair (QP): {}",
101                Error::last_os_error()
102            );
103        }
104        // SAFETY: `qp` is a live RC QP just created against `pd` with
105        // `send_cq`/`recv_cq`; `IbvQp` holds a clone of each, keeping them alive
106        // for at least as long as the QP it destroys on drop.
107        Ok(unsafe { IbvQp::from_raw(qp, send_cq, recv_cq, domain.pd().clone()) })
108    }
109}
110
111impl IbvQueuePair for MlxQueuePair {
112    unsafe fn new<I: IbvDomainImpl<QueuePair = Self>>(
113        domain: &IbvDomain<I>,
114        config: IbvConfig,
115        send_cq: Arc<IbvCq>,
116        recv_cq: Arc<IbvCq>,
117    ) -> Result<Self, anyhow::Error> {
118        tracing::debug!("creating an MlxQueuePair from config {}", config);
119        let gid = domain.device_info().select_gid(
120            config.port_num,
121            Some(GidScope::Global),
122            Some(GidType::RoCEv2),
123        )?;
124        // SAFETY: an `IbvDomain` holds a null-or-live context and PD, which is
125        // `create_ibv_qp`'s contract.
126        let qp = unsafe { Self::create_ibv_qp(domain, &config, send_cq, recv_cq) }?;
127        let access_flags = domain.access_flags();
128        // SAFETY: `create_ibv_qp` returns a live, fully non-null `IbvQp` (it
129        // bails on any null handle).
130        Ok(MlxQueuePair(unsafe {
131            RCQueuePair::from_qp(qp, config, gid, access_flags)
132        }))
133    }
134
135    fn connect(&mut self, info: &IbvQpInfo) -> Result<(), anyhow::Error> {
136        self.0.connect(info)
137    }
138
139    fn get_qp_info(&mut self) -> Result<IbvQpInfo, anyhow::Error> {
140        self.0.get_qp_info()
141    }
142
143    fn state(&mut self) -> Result<u32, anyhow::Error> {
144        self.0.state()
145    }
146
147    fn put(
148        &mut self,
149        remote_dst: IbvRemoteMemoryRegionView,
150        local_src: IbvMemoryRegionView,
151    ) -> Result<Vec<u64>, anyhow::Error> {
152        self.0.put(remote_dst, local_src)
153    }
154
155    fn get(
156        &mut self,
157        local_dst: IbvMemoryRegionView,
158        remote_src: IbvRemoteMemoryRegionView,
159    ) -> Result<Vec<u64>, anyhow::Error> {
160        self.0.get(local_dst, remote_src)
161    }
162}