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