1use super::*;
10use crate::backend::ibverbs::cq_pool::cq_entries_for;
11use crate::backend::ibverbs::primitives::IbvPd;
12use crate::backend::ibverbs::primitives::IbvWc;
13
14#[derive(Debug)]
35pub struct IbvQueuePair {
36 pub send_cq: usize, pub recv_cq: usize, qp: usize, pub dv_qp: usize, pub dv_send_cq: usize, pub dv_recv_cq: usize, context: usize, config: IbvConfig,
44 gid: Gid,
48 is_efa: bool,
49 _pd: Arc<IbvPd>,
53}
54
55impl Drop for IbvQueuePair {
56 fn drop(&mut self) {
57 if self.qp == 0 {
58 return;
59 }
60 unsafe {
67 rdmaxcel_sys::rdmaxcel_qp_destroy(self.qp as *mut rdmaxcel_sys::rdmaxcel_qp);
68 }
69 }
70}
71
72impl IbvQueuePair {
73 pub unsafe fn as_ptr(&self) -> *mut rdmaxcel_sys::rdmaxcel_qp {
84 self.qp as *mut rdmaxcel_sys::rdmaxcel_qp
85 }
86
87 fn is_efa(&self) -> bool {
88 self.is_efa
89 }
90
91 fn apply_first_op_delay(&self, wr_id: u64) {
93 unsafe {
94 let qp = self.qp as *mut rdmaxcel_sys::rdmaxcel_qp;
95 if wr_id == 0 {
96 let rts_timestamp = rdmaxcel_sys::rdmaxcel_qp_load_rts_timestamp(qp);
97 assert!(
98 rts_timestamp != u64::MAX,
99 "First operation attempted before queue pair reached RTS state! Call connect() first."
100 );
101 let current_nanos = std::time::SystemTime::now()
102 .duration_since(std::time::UNIX_EPOCH)
103 .unwrap()
104 .as_nanos() as u64;
105 let elapsed_nanos = current_nanos - rts_timestamp;
106 let elapsed = Duration::from_nanos(elapsed_nanos);
107 let init_delay = Duration::from_millis(self.config.hw_init_delay_ms);
108 if elapsed < init_delay {
109 let remaining_delay = init_delay - elapsed;
110 std::thread::sleep(remaining_delay);
114 }
115 }
116 }
117 }
118
119 pub fn new<I: IbvDomainImpl>(
129 domain: &IbvDomain<I>,
130 config: IbvConfig,
131 ) -> Result<Self, anyhow::Error> {
132 tracing::debug!("creating an IbvQueuePair from config {}", config);
133 let context = domain.context().as_ptr();
134 let pd = domain.as_ptr();
135 let resolved_qp_type = resolve_qp_type(config.qp_type);
137 let is_efa = resolved_qp_type == rdmaxcel_sys::RDMA_QP_TYPE_EFA;
138 let gid = if is_efa {
141 domain.device_info().gid_at(config.port_num, 0)?
142 } else {
143 domain.device_info().select_gid(
144 config.port_num,
145 Some(GidScope::Global),
146 Some(GidType::RoCEv2),
147 )?
148 };
149 let cq_entries = cq_entries_for(1, config.max_send_wr, domain.device_info().max_cqe())?;
153 unsafe {
154 let qp = rdmaxcel_sys::rdmaxcel_qp_create(
155 context,
156 pd,
157 cq_entries,
158 config.max_send_wr.try_into().unwrap(),
159 config.max_recv_wr.try_into().unwrap(),
160 config.max_send_sge.try_into().unwrap(),
161 config.max_recv_sge.try_into().unwrap(),
162 resolved_qp_type,
163 );
164
165 if qp.is_null() {
166 let os_error = Error::last_os_error();
167 return Err(anyhow::anyhow!(
168 "failed to create queue pair (QP): {}",
169 os_error
170 ));
171 }
172
173 let send_cq = (*(*qp).ibv_qp).send_cq;
174 let recv_cq = (*(*qp).ibv_qp).recv_cq;
175
176 if is_efa {
178 return Ok(IbvQueuePair {
179 send_cq: send_cq as usize,
180 recv_cq: recv_cq as usize,
181 qp: qp as usize,
182 dv_qp: 0,
183 dv_send_cq: 0,
184 dv_recv_cq: 0,
185 context: context as usize,
186 config,
187 gid,
188 is_efa: true,
189 _pd: domain.pd().clone(),
190 });
191 }
192
193 let dv_qp = rdmaxcel_sys::create_mlx5dv_qp((*qp).ibv_qp);
194 let dv_send_cq = rdmaxcel_sys::create_mlx5dv_send_cq((*qp).ibv_qp);
195 let dv_recv_cq = rdmaxcel_sys::create_mlx5dv_recv_cq((*qp).ibv_qp);
196
197 if dv_qp.is_null() || dv_send_cq.is_null() || dv_recv_cq.is_null() {
198 rdmaxcel_sys::ibv_destroy_cq((*(*qp).ibv_qp).recv_cq);
199 rdmaxcel_sys::ibv_destroy_cq((*(*qp).ibv_qp).send_cq);
200 rdmaxcel_sys::ibv_destroy_qp((*qp).ibv_qp);
201 return Err(anyhow::anyhow!(
202 "failed to init mlx5dv_qp or completion queues"
203 ));
204 }
205
206 if config.use_gpu_direct {
207 let ret = rdmaxcel_sys::register_cuda_memory(dv_qp, dv_recv_cq, dv_send_cq);
208 if ret != 0 {
209 rdmaxcel_sys::ibv_destroy_cq((*(*qp).ibv_qp).recv_cq);
210 rdmaxcel_sys::ibv_destroy_cq((*(*qp).ibv_qp).send_cq);
211 rdmaxcel_sys::ibv_destroy_qp((*qp).ibv_qp);
212 return Err(anyhow::anyhow!(
213 "failed to register GPU Direct RDMA memory: {:?}",
214 ret
215 ));
216 }
217 }
218 Ok(IbvQueuePair {
219 send_cq: send_cq as usize,
220 recv_cq: recv_cq as usize,
221 qp: qp as usize,
222 dv_qp: dv_qp as usize,
223 dv_send_cq: dv_send_cq as usize,
224 dv_recv_cq: dv_recv_cq as usize,
225 context: context as usize,
226 config,
227 gid,
228 is_efa: false,
229 _pd: domain.pd().clone(),
230 })
231 }
232 }
233
234 pub fn get_qp_info(&mut self) -> Result<IbvQpInfo, anyhow::Error> {
236 let context = self.context as *mut rdmaxcel_sys::ibv_context;
237 let qp = self.qp as *mut rdmaxcel_sys::rdmaxcel_qp;
238 unsafe { super::get_qp_info((*qp).ibv_qp, context, &self.config, self.gid) }
241 }
242
243 pub fn state(&mut self) -> Result<u32, anyhow::Error> {
245 let qp = self.qp as *mut rdmaxcel_sys::rdmaxcel_qp;
246 unsafe { super::state((*qp).ibv_qp) }
248 }
249
250 pub fn connect(&mut self, connection_info: &IbvQpInfo) -> Result<(), anyhow::Error> {
256 if self.is_efa() {
258 return self.efa_connect(connection_info);
259 }
260
261 let access_flags = (rdmaxcel_sys::ibv_access_flags::IBV_ACCESS_LOCAL_WRITE
262 | rdmaxcel_sys::ibv_access_flags::IBV_ACCESS_REMOTE_WRITE
263 | rdmaxcel_sys::ibv_access_flags::IBV_ACCESS_REMOTE_READ
264 | rdmaxcel_sys::ibv_access_flags::IBV_ACCESS_REMOTE_ATOMIC)
265 .0 as i32;
266 let qp = self.qp as *mut rdmaxcel_sys::rdmaxcel_qp;
267 unsafe {
269 super::connect(
270 (*qp).ibv_qp,
271 &self.config,
272 access_flags,
273 connection_info,
274 self.gid.index(),
275 )
276 }?;
277
278 tracing::debug!(
279 "connection sequence has successfully completed (qp: {:?})",
280 qp
281 );
282
283 let rts_timestamp_nanos = std::time::SystemTime::now()
286 .duration_since(std::time::UNIX_EPOCH)
287 .unwrap()
288 .as_nanos() as u64;
289 unsafe { rdmaxcel_sys::rdmaxcel_qp_store_rts_timestamp(qp, rts_timestamp_nanos) };
291 Ok(())
292 }
293
294 fn efa_connect(&mut self, connection_info: &IbvQpInfo) -> Result<(), anyhow::Error> {
296 let qp = self.qp as *mut rdmaxcel_sys::rdmaxcel_qp;
297
298 let gid_ptr = connection_info.gid.as_ref().map_or(std::ptr::null(), |g| {
299 let ibv_gid: &rdmaxcel_sys::ibv_gid = g.as_ref();
300 unsafe { ibv_gid.raw.as_ptr() }
301 });
302
303 unsafe {
304 let ret = rdmaxcel_sys::rdmaxcel_efa_connect(
305 qp,
306 self.config.port_num,
307 self.config.pkey_index,
308 0x4242, self.config.psn,
310 self.gid.index(),
311 gid_ptr,
312 connection_info.qp_num,
313 );
314 if ret != 0 {
315 let msg = std::ffi::CStr::from_ptr(rdmaxcel_sys::rdmaxcel_error_string(ret))
316 .to_str()
317 .unwrap_or("unknown");
318 return Err(anyhow::anyhow!("EFA connect failed: {}", msg));
319 }
320 }
321
322 let rts_timestamp_nanos = std::time::SystemTime::now()
324 .duration_since(std::time::UNIX_EPOCH)
325 .unwrap()
326 .as_nanos() as u64;
327 unsafe {
328 rdmaxcel_sys::rdmaxcel_qp_store_rts_timestamp(qp, rts_timestamp_nanos);
329 }
330
331 Ok(())
332 }
333
334 pub fn recv(
335 &mut self,
336 lhandle: IbvMemoryRegionView,
337 rhandle: IbvRemoteMemoryRegionView,
338 ) -> Result<u64, anyhow::Error> {
339 unsafe {
340 let qp = self.qp as *mut rdmaxcel_sys::rdmaxcel_qp;
341 let idx = rdmaxcel_sys::rdmaxcel_qp_fetch_add_recv_wqe_idx(qp);
342 self.post_op(
343 0,
344 lhandle.lkey,
345 0,
346 idx,
347 true,
348 IbvOperation::Recv,
349 0,
350 rhandle.rkey,
351 )
352 .unwrap();
353 rdmaxcel_sys::rdmaxcel_qp_fetch_add_recv_db_idx(qp);
354 Ok(idx)
355 }
356 }
357
358 pub fn put_with_recv(
359 &mut self,
360 lhandle: IbvMemoryRegionView,
361 rhandle: IbvRemoteMemoryRegionView,
362 ) -> Result<Vec<u64>, anyhow::Error> {
363 unsafe {
364 let qp = self.qp as *mut rdmaxcel_sys::rdmaxcel_qp;
365 let idx = rdmaxcel_sys::rdmaxcel_qp_fetch_add_send_wqe_idx(qp);
366 self.post_op(
367 lhandle.rdma_addr,
368 lhandle.lkey,
369 lhandle.size,
370 idx,
371 true,
372 IbvOperation::WriteWithImm,
373 rhandle.addr,
374 rhandle.rkey,
375 )
376 .unwrap();
377 rdmaxcel_sys::rdmaxcel_qp_fetch_add_send_db_idx(qp);
378 Ok(vec![idx])
379 }
380 }
381
382 pub fn put(
383 &mut self,
384 lhandle: IbvMemoryRegionView,
385 rhandle: IbvRemoteMemoryRegionView,
386 ) -> Result<Vec<u64>, anyhow::Error> {
387 let total_size = lhandle.size;
388 if rhandle.size < total_size {
389 return Err(anyhow::anyhow!(
390 "remote buffer size ({}) is smaller than local buffer size ({})",
391 rhandle.size,
392 total_size
393 ));
394 }
395
396 let mut remaining = total_size;
397 let mut offset = 0;
398 let mut wr_ids = Vec::new();
399 while remaining > 0 {
400 let chunk_size = std::cmp::min(remaining, MAX_RDMA_MSG_SIZE);
401 let idx = unsafe {
402 rdmaxcel_sys::rdmaxcel_qp_fetch_add_send_wqe_idx(
403 self.qp as *mut rdmaxcel_sys::rdmaxcel_qp,
404 )
405 };
406 wr_ids.push(idx);
407 self.post_op(
408 lhandle.rdma_addr + offset,
409 lhandle.lkey,
410 chunk_size,
411 idx,
412 true,
413 IbvOperation::Write,
414 rhandle.addr + offset,
415 rhandle.rkey,
416 )?;
417 unsafe {
418 rdmaxcel_sys::rdmaxcel_qp_fetch_add_send_db_idx(
419 self.qp as *mut rdmaxcel_sys::rdmaxcel_qp,
420 );
421 }
422
423 remaining -= chunk_size;
424 offset += chunk_size;
425 }
426
427 Ok(wr_ids)
428 }
429
430 pub fn ring_doorbell(&mut self) -> Result<(), anyhow::Error> {
432 if self.is_efa() {
434 return Ok(());
435 }
436
437 unsafe {
438 let qp = self.qp as *mut rdmaxcel_sys::rdmaxcel_qp;
439 let dv_qp = self.dv_qp as *mut rdmaxcel_sys::mlx5dv_qp;
440 let base_ptr = (*dv_qp).sq.buf as *mut u8;
441 let wqe_cnt = (*dv_qp).sq.wqe_cnt;
442 let stride = (*dv_qp).sq.stride;
443 let send_wqe_idx = rdmaxcel_sys::rdmaxcel_qp_load_send_wqe_idx(qp);
444 let mut send_db_idx = rdmaxcel_sys::rdmaxcel_qp_load_send_db_idx(qp);
445 if (wqe_cnt as u64) < (send_wqe_idx - send_db_idx) {
446 return Err(anyhow::anyhow!("Overflow of WQE, possible data loss"));
447 }
448 self.apply_first_op_delay(send_db_idx);
449 while send_db_idx < send_wqe_idx {
450 let offset = (send_db_idx % wqe_cnt as u64) * stride as u64;
451 let src_ptr = base_ptr.wrapping_add(offset as usize);
452 rdmaxcel_sys::db_ring((*dv_qp).bf.reg, src_ptr as *mut std::ffi::c_void);
453 send_db_idx += 1;
454 rdmaxcel_sys::rdmaxcel_qp_store_send_db_idx(qp, send_db_idx);
455 }
456 Ok(())
457 }
458 }
459
460 pub fn enqueue_put(
462 &mut self,
463 lhandle: IbvMemoryRegionView,
464 rhandle: IbvRemoteMemoryRegionView,
465 ) -> Result<Vec<u64>, anyhow::Error> {
466 let idx = unsafe {
467 rdmaxcel_sys::rdmaxcel_qp_fetch_add_send_wqe_idx(
468 self.qp as *mut rdmaxcel_sys::rdmaxcel_qp,
469 )
470 };
471
472 self.send_wqe(
473 lhandle.rdma_addr,
474 lhandle.lkey,
475 lhandle.size,
476 idx,
477 true,
478 IbvOperation::Write,
479 rhandle.addr,
480 rhandle.rkey,
481 )?;
482 Ok(vec![idx])
483 }
484
485 pub fn enqueue_put_with_recv(
487 &mut self,
488 lhandle: IbvMemoryRegionView,
489 rhandle: IbvRemoteMemoryRegionView,
490 ) -> Result<Vec<u64>, anyhow::Error> {
491 let idx = unsafe {
492 rdmaxcel_sys::rdmaxcel_qp_fetch_add_send_wqe_idx(
493 self.qp as *mut rdmaxcel_sys::rdmaxcel_qp,
494 )
495 };
496
497 self.send_wqe(
498 lhandle.rdma_addr,
499 lhandle.lkey,
500 lhandle.size,
501 idx,
502 true,
503 IbvOperation::WriteWithImm,
504 rhandle.addr,
505 rhandle.rkey,
506 )?;
507 Ok(vec![idx])
508 }
509
510 pub fn enqueue_get(
512 &mut self,
513 lhandle: IbvMemoryRegionView,
514 rhandle: IbvRemoteMemoryRegionView,
515 ) -> Result<Vec<u64>, anyhow::Error> {
516 let idx = unsafe {
517 rdmaxcel_sys::rdmaxcel_qp_fetch_add_send_wqe_idx(
518 self.qp as *mut rdmaxcel_sys::rdmaxcel_qp,
519 )
520 };
521
522 self.send_wqe(
523 lhandle.rdma_addr,
524 lhandle.lkey,
525 lhandle.size,
526 idx,
527 true,
528 IbvOperation::Read,
529 rhandle.addr,
530 rhandle.rkey,
531 )?;
532 Ok(vec![idx])
533 }
534
535 pub fn get(
536 &mut self,
537 lhandle: IbvMemoryRegionView,
538 rhandle: IbvRemoteMemoryRegionView,
539 ) -> Result<Vec<u64>, anyhow::Error> {
540 let total_size = rhandle.size;
541 if rhandle.size > lhandle.size {
542 return Err(anyhow::anyhow!(
543 "remote buffer size ({}) is larger than local buffer size ({})",
544 rhandle.size,
545 lhandle.size
546 ));
547 }
548
549 let mut remaining = total_size;
550 let mut offset = 0;
551 let mut wr_ids = Vec::new();
552
553 while remaining > 0 {
554 let chunk_size = std::cmp::min(remaining, MAX_RDMA_MSG_SIZE);
555 let idx = unsafe {
556 rdmaxcel_sys::rdmaxcel_qp_fetch_add_send_wqe_idx(
557 self.qp as *mut rdmaxcel_sys::rdmaxcel_qp,
558 )
559 };
560 wr_ids.push(idx);
561
562 self.post_op(
563 lhandle.rdma_addr + offset,
564 lhandle.lkey,
565 chunk_size,
566 idx,
567 true,
568 IbvOperation::Read,
569 rhandle.addr + offset,
570 rhandle.rkey,
571 )?;
572 unsafe {
573 rdmaxcel_sys::rdmaxcel_qp_fetch_add_send_db_idx(
574 self.qp as *mut rdmaxcel_sys::rdmaxcel_qp,
575 );
576 }
577
578 remaining -= chunk_size;
579 offset += chunk_size;
580 }
581
582 Ok(wr_ids)
583 }
584
585 fn post_op(
587 &mut self,
588 laddr: usize,
589 lkey: u32,
590 length: usize,
591 wr_id: u64,
592 signaled: bool,
593 op_type: IbvOperation,
594 raddr: usize,
595 rkey: u32,
596 ) -> Result<(), anyhow::Error> {
597 if self.is_efa() {
599 return self.post_op_efa(laddr, lkey, length, wr_id, signaled, op_type, raddr, rkey);
600 }
601
602 unsafe {
603 let qp = self.qp as *mut rdmaxcel_sys::rdmaxcel_qp;
604 let context = self.context as *mut rdmaxcel_sys::ibv_context;
605 let ops = &mut (*context).ops;
606 let errno;
607 if op_type == IbvOperation::Recv {
608 let mut sge = rdmaxcel_sys::ibv_sge {
609 addr: laddr as u64,
610 length: length as u32,
611 lkey,
612 };
613 let mut wr = rdmaxcel_sys::ibv_recv_wr {
614 wr_id,
615 sg_list: &mut sge as *mut _,
616 num_sge: 1,
617 ..Default::default()
618 };
619 let mut bad_wr: *mut rdmaxcel_sys::ibv_recv_wr = std::ptr::null_mut();
620 errno =
621 ops.post_recv.as_mut().unwrap()((*qp).ibv_qp, &mut wr as *mut _, &mut bad_wr);
622 } else if op_type == IbvOperation::Write
623 || op_type == IbvOperation::Read
624 || op_type == IbvOperation::WriteWithImm
625 {
626 self.apply_first_op_delay(wr_id);
627 let send_flags = if signaled {
628 rdmaxcel_sys::ibv_send_flags::IBV_SEND_SIGNALED.0
629 } else {
630 0
631 };
632 let mut sge = rdmaxcel_sys::ibv_sge {
633 addr: laddr as u64,
634 length: length as u32,
635 lkey,
636 };
637 let mut wr = rdmaxcel_sys::ibv_send_wr {
638 wr_id,
639 next: std::ptr::null_mut(),
640 sg_list: &mut sge as *mut _,
641 num_sge: 1,
642 opcode: op_type.into(),
643 send_flags,
644 wr: Default::default(),
645 qp_type: Default::default(),
646 __bindgen_anon_1: Default::default(),
647 __bindgen_anon_2: Default::default(),
648 };
649
650 wr.wr.rdma.remote_addr = raddr as u64;
651 wr.wr.rdma.rkey = rkey;
652 let mut bad_wr: *mut rdmaxcel_sys::ibv_send_wr = std::ptr::null_mut();
653
654 errno =
655 ops.post_send.as_mut().unwrap()((*qp).ibv_qp, &mut wr as *mut _, &mut bad_wr);
656 } else {
657 panic!("Not Implemented");
658 }
659
660 if errno != 0 {
661 let os_error = Error::last_os_error();
662 return Err(anyhow::anyhow!("Failed to post send request: {}", os_error));
663 }
664 tracing::debug!(
665 "completed sending {:?} request (lkey: {}, addr: 0x{:x}, length {}) to (raddr 0x{:x}, rkey {})",
666 op_type,
667 lkey,
668 laddr,
669 length,
670 raddr,
671 rkey,
672 );
673
674 Ok(())
675 }
676 }
677
678 fn post_op_efa(
680 &mut self,
681 laddr: usize,
682 lkey: u32,
683 length: usize,
684 wr_id: u64,
685 signaled: bool,
686 op_type: IbvOperation,
687 raddr: usize,
688 rkey: u32,
689 ) -> Result<(), anyhow::Error> {
690 let c_op = match op_type {
691 IbvOperation::Write => 0,
692 IbvOperation::Read => 1,
693 IbvOperation::Recv => 2,
694 IbvOperation::WriteWithImm => 3,
695 };
696
697 unsafe {
698 let qp = self.qp as *mut rdmaxcel_sys::rdmaxcel_qp;
699 let ret = rdmaxcel_sys::rdmaxcel_qp_post_op(
700 qp,
701 laddr as *mut std::ffi::c_void,
702 lkey,
703 length,
704 raddr as *mut std::ffi::c_void,
705 rkey,
706 wr_id,
707 signaled as i32,
708 c_op,
709 );
710 if ret != 0 {
711 let msg = std::ffi::CStr::from_ptr(rdmaxcel_sys::rdmaxcel_error_string(ret))
712 .to_str()
713 .unwrap_or("unknown");
714 return Err(anyhow::anyhow!("EFA post_op failed: {}", msg));
715 }
716 }
717 Ok(())
718 }
719
720 fn send_wqe(
721 &mut self,
722 laddr: usize,
723 lkey: u32,
724 length: usize,
725 wr_id: u64,
726 signaled: bool,
727 op_type: IbvOperation,
728 raddr: usize,
729 rkey: u32,
730 ) -> Result<DoorBell, anyhow::Error> {
731 if self.is_efa() {
733 self.post_op(laddr, lkey, length, wr_id, signaled, op_type, raddr, rkey)?;
734 return Ok(DoorBell {
735 dst_ptr: 0,
736 src_ptr: 0,
737 size: 0,
738 });
739 }
740
741 unsafe {
742 let op_type_val = match op_type {
743 IbvOperation::Write => rdmaxcel_sys::MLX5_OPCODE_RDMA_WRITE,
744 IbvOperation::WriteWithImm => rdmaxcel_sys::MLX5_OPCODE_RDMA_WRITE_IMM,
745 IbvOperation::Read => rdmaxcel_sys::MLX5_OPCODE_RDMA_READ,
746 IbvOperation::Recv => 0,
747 };
748
749 let qp = self.qp as *mut rdmaxcel_sys::rdmaxcel_qp;
750 let dv_qp = self.dv_qp as *mut rdmaxcel_sys::mlx5dv_qp;
751 let _dv_cq = if op_type == IbvOperation::Recv {
752 self.dv_recv_cq as *mut rdmaxcel_sys::mlx5dv_cq
753 } else {
754 self.dv_send_cq as *mut rdmaxcel_sys::mlx5dv_cq
755 };
756
757 let buf = if op_type == IbvOperation::Recv {
758 (*dv_qp).rq.buf as *mut u8
759 } else {
760 (*dv_qp).sq.buf as *mut u8
761 };
762
763 let params = rdmaxcel_sys::wqe_params_t {
764 laddr,
765 lkey,
766 length,
767 wr_id,
768 signaled,
769 op_type: op_type_val,
770 raddr,
771 rkey,
772 qp_num: (*(*qp).ibv_qp).qp_num,
773 buf,
774 dbrec: (*dv_qp).dbrec,
775 wqe_cnt: (*dv_qp).sq.wqe_cnt,
776 };
777
778 if op_type == IbvOperation::Recv {
779 rdmaxcel_sys::recv_wqe(params);
780 std::ptr::write_volatile((*dv_qp).dbrec, 1_u32.to_be());
781 } else {
782 rdmaxcel_sys::send_wqe(params);
783 };
784
785 Ok(DoorBell {
786 dst_ptr: (*dv_qp).bf.reg as usize,
787 src_ptr: (*dv_qp).sq.buf as usize,
788 size: 8,
789 })
790 }
791 }
792
793 pub fn poll_completion(
809 &mut self,
810 target: PollTarget,
811 ) -> Result<Option<Result<IbvWc, WorkRequestError>>, PollCompletionError> {
812 unsafe {
816 let (cq, cq_type) = match target {
817 PollTarget::Send => (self.send_cq as *mut rdmaxcel_sys::ibv_cq, "send"),
818 PollTarget::Recv => (self.recv_cq as *mut rdmaxcel_sys::ibv_cq, "recv"),
819 };
820 let context = self.context as *mut rdmaxcel_sys::ibv_context;
821 let poll_cq = (*context)
822 .ops
823 .poll_cq
824 .expect("poll_cq verb missing from ibv_context ops");
825
826 let mut wc = std::mem::MaybeUninit::<rdmaxcel_sys::ibv_wc>::zeroed().assume_init();
827 let ret = poll_cq(cq, 1, &mut wc);
828
829 if ret < 0 {
830 return Err(PollCompletionError {
831 message: format!("{} CQ poll failed (ibv_poll_cq returned {})", cq_type, ret),
832 });
833 }
834 if ret == 0 {
835 return Ok(None);
836 }
837
838 if let Some((status, vendor_err)) = wc.error() {
842 return Ok(Some(Err(WorkRequestError {
843 wr_id: wc.wr_id(),
844 status,
845 vendor_err,
846 message: format!(
847 "{} completion failed for wr_id={}: status={:?}, vendor_err={}",
848 cq_type,
849 wc.wr_id(),
850 status,
851 vendor_err,
852 ),
853 })));
854 }
855 Ok(Some(Ok(IbvWc::from(wc))))
856 }
857 }
858}