Skip to main content

ibverbs_rs/ibverbs/queue_pair/
ops.rs

1//! Work request posting operations for a connected queue pair.
2//!
3//! This module adds the four core RDMA operations to [`QueuePair`] as `unsafe` methods.
4//! All four follow the same pattern: build a typed work request, post it with an
5//! application-chosen `wr_id`, and poll the corresponding completion queue for the result.
6//!
7//! | Method | Operation | Queue |
8//! |--------|-----------|-------|
9//! | [`post_send`](QueuePair::post_send) | Two-sided Send | Send CQ |
10//! | [`post_receive`](QueuePair::post_receive) | Two-sided Receive | Recv CQ |
11//! | [`post_write`](QueuePair::post_write) | One-sided RDMA Write | Send CQ |
12//! | [`post_read`](QueuePair::post_read) | One-sided RDMA Read | Send CQ |
13
14use crate::ibverbs::error::{IbvError, IbvResult};
15use crate::ibverbs::queue_pair::QueuePair;
16use crate::ibverbs::work::{
17    ReadWorkRequest, ReceiveWorkRequest, SendWorkRequest, WriteWorkRequest,
18};
19use ibverbs_sys::*;
20use std::ptr;
21
22impl QueuePair {
23    /// Posts a **Send** request to the Send Queue.
24    ///
25    /// # Safety
26    ///
27    /// The buffers referenced by the `gather_elements` must remain **valid** and **immutable**
28    /// until the work is finished by the hardware (signaled via the Send CQ).
29    ///
30    /// You must ensure that the lifetime of the data, which was tied to the
31    /// `GatherElement`, is manually extended until completion.
32    pub unsafe fn post_send(&mut self, wr: SendWorkRequest, wr_id: u64) -> IbvResult<()> {
33        let (opcode, __bindgen_anon_1) = match wr.imm_data {
34            None => (ibv_wr_opcode::IBV_WR_SEND, Default::default()),
35            Some(imm_data) => (
36                ibv_wr_opcode::IBV_WR_SEND_WITH_IMM,
37                ibv_send_wr__bindgen_ty_1 {
38                    imm_data: imm_data.to_be(),
39                },
40            ),
41        };
42
43        #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
44        // length validated at WorkRequest construction
45        let mut wr = ibv_send_wr {
46            wr_id,
47            next: ptr::null::<ibv_send_wr>() as *mut _,
48            sg_list: wr.gather_elements.as_ref().as_ptr() as *mut ibv_sge,
49            num_sge: wr.gather_elements.as_ref().len() as i32,
50            opcode,
51            send_flags: ibv_send_flags::IBV_SEND_SIGNALED.0,
52            wr: Default::default(),
53            qp_type: Default::default(),
54            __bindgen_anon_1,
55            __bindgen_anon_2: Default::default(),
56        };
57
58        unsafe {
59            self.post_send_wr(&mut wr).map_err(|errno| {
60                IbvError::from_errno_with_msg(errno, "Failed to post send work request")
61            })
62        }
63    }
64
65    /// Posts a **Receive** request to the Receive Queue.
66    ///
67    /// # Safety
68    ///
69    /// The buffers referenced by the `scatter_elements` must remain **valid** and **exclusive**
70    /// (mutable) until the work is finished by the hardware (signaled via the Recv CQ).
71    ///
72    /// Accessing these buffers before completion results in a data race (Undefined Behavior).
73    pub unsafe fn post_receive(&mut self, wr: ReceiveWorkRequest, wr_id: u64) -> IbvResult<()> {
74        #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
75        // length validated at WorkRequest construction
76        let mut wr = ibv_recv_wr {
77            wr_id,
78            next: ptr::null::<ibv_send_wr>() as *mut _,
79            sg_list: wr.scatter_elements.as_mut().as_mut_ptr() as *mut ibv_sge,
80            num_sge: wr.scatter_elements.as_mut().len() as i32,
81        };
82
83        unsafe {
84            self.post_receive_wr(&mut wr).map_err(|errno| {
85                IbvError::from_errno_with_msg(errno, "Failed to post receive work request")
86            })
87        }
88    }
89
90    /// Posts an **RDMA Write** request.
91    ///
92    /// # Safety
93    ///
94    /// The buffers referenced by the `gather_elements` must remain **valid** and **immutable**
95    /// until the work is finished by the hardware.
96    ///
97    /// Additionally, the remote address range must be valid on the remote peer; otherwise,
98    /// a Remote Access Error will occur.
99    pub unsafe fn post_write(&mut self, wr: WriteWorkRequest, wr_id: u64) -> IbvResult<()> {
100        let (opcode, __bindgen_anon_1) = match wr.imm_data {
101            None => (ibv_wr_opcode::IBV_WR_RDMA_WRITE, Default::default()),
102            Some(imm_data) => (
103                ibv_wr_opcode::IBV_WR_RDMA_WRITE_WITH_IMM,
104                ibv_send_wr__bindgen_ty_1 {
105                    imm_data: imm_data.to_be(),
106                },
107            ),
108        };
109
110        #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
111        // length validated at WorkRequest construction
112        let mut wr = ibv_send_wr {
113            wr_id,
114            next: ptr::null::<ibv_send_wr>() as *mut _,
115            sg_list: wr.gather_elements.as_ptr() as *mut ibv_sge,
116            num_sge: wr.gather_elements.len() as i32,
117            opcode,
118            send_flags: ibv_send_flags::IBV_SEND_SIGNALED.0,
119            wr: ibv_send_wr__bindgen_ty_2 {
120                rdma: ibv_send_wr__bindgen_ty_2__bindgen_ty_1 {
121                    remote_addr: wr.remote_mr.address(),
122                    rkey: wr.remote_mr.rkey(),
123                },
124            },
125            qp_type: Default::default(),
126            __bindgen_anon_1,
127            __bindgen_anon_2: Default::default(),
128        };
129
130        unsafe {
131            self.post_send_wr(&mut wr).map_err(|errno| {
132                IbvError::from_errno_with_msg(errno, "Failed to post write work request")
133            })
134        }
135    }
136
137    /// Posts an **RDMA Read** request.
138    ///
139    /// # Safety
140    ///
141    /// The buffers referenced by the `scatter_elements` must remain **valid** and **exclusive**
142    /// (mutable) until the work is finished by the hardware.
143    pub unsafe fn post_read(&mut self, wr: ReadWorkRequest, wr_id: u64) -> IbvResult<()> {
144        // length validated at WorkRequest construction
145        #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
146        let mut wr = ibv_send_wr {
147            wr_id,
148            next: ptr::null::<ibv_send_wr>() as *mut _,
149            sg_list: wr.scatter_elements.as_ptr() as *mut ibv_sge,
150            num_sge: wr.scatter_elements.len() as i32,
151            opcode: ibv_wr_opcode::IBV_WR_RDMA_READ,
152            send_flags: ibv_send_flags::IBV_SEND_SIGNALED.0,
153            wr: ibv_send_wr__bindgen_ty_2 {
154                rdma: ibv_send_wr__bindgen_ty_2__bindgen_ty_1 {
155                    remote_addr: wr.remote_mr.address(),
156                    rkey: wr.remote_mr.rkey(),
157                },
158            },
159            qp_type: Default::default(),
160            __bindgen_anon_1: Default::default(),
161            __bindgen_anon_2: Default::default(),
162        };
163
164        unsafe { self.post_send_wr(&mut wr) }.map_err(|errno| {
165            IbvError::from_errno_with_msg(errno, "Failed to post read work request")
166        })
167    }
168
169    #[inline(always)]
170    unsafe fn post_send_wr(&mut self, wr: &mut ibv_send_wr) -> Result<(), i32> {
171        let mut bad_wr: *mut ibv_send_wr = ptr::null::<ibv_send_wr>() as *mut _;
172        let ctx = unsafe { (*self.qp).context };
173        let post_send = unsafe {
174            (*ctx)
175                .ops
176                .post_send
177                .expect("post_send function pointer should be set by driver")
178        };
179        let errno = unsafe { post_send(self.qp, wr as *mut _, &mut bad_wr as *mut _) };
180        if errno != 0 { Err(errno) } else { Ok(()) }
181    }
182
183    #[inline(always)]
184    unsafe fn post_receive_wr(&mut self, wr: &mut ibv_recv_wr) -> Result<(), i32> {
185        let mut bad_wr: *mut ibv_recv_wr = ptr::null::<ibv_recv_wr>() as *mut _;
186        let ctx = unsafe { (*self.qp).context };
187        let post_recv = unsafe {
188            (*ctx)
189                .ops
190                .post_recv
191                .expect("post_recv function pointer should be set by driver")
192        };
193        let errno = unsafe { post_recv(self.qp, wr as *mut _, &mut bad_wr as *mut _) };
194        if errno != 0 { Err(errno) } else { Ok(()) }
195    }
196}