Skip to content

Commit 799daf5

Browse files
committed
address comments
1 parent 6b6ba56 commit 799daf5

File tree

4 files changed

+64
-42
lines changed

4 files changed

+64
-42
lines changed

Cargo.lock

Lines changed: 19 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ibverbs/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ license = "MIT OR Apache-2.0"
2020

2121
[dependencies]
2222
ffi = { path = "../ibverbs-sys", package = "ibverbs-sys", version = "0.3.0" }
23-
libc = "0.2"
23+
nix = { version = "0.29.0", default-features = false, features = ["fs", "poll"] }
2424

2525
[dependencies.serde]
2626
version = "1.0.100"

ibverbs/examples/loopback.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ fn main() {
2929
let mut received = false;
3030
let mut completions = [ibverbs::ibv_wc::default(); 16];
3131
while !sent || !received {
32-
let completed = cq.poll(&mut completions[..]).unwrap();
32+
let completed = cq.wait(&mut completions[..], None).unwrap();
3333
if completed.is_empty() {
3434
continue;
3535
}

ibverbs/src/lib.rs

Lines changed: 43 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ use std::ffi::CStr;
7070
use std::io;
7171
use std::marker::PhantomData;
7272
use std::mem;
73+
use std::os::fd::BorrowedFd;
7374
use std::os::raw::c_void;
7475
use std::ptr;
7576
use std::time::Duration;
@@ -431,15 +432,11 @@ impl Context {
431432
}
432433

433434
let cc_fd = unsafe { *cc }.fd;
434-
let flags = unsafe { libc::fcntl(cc_fd, libc::F_GETFL) };
435-
if flags < 0 {
436-
return Err(io::Error::last_os_error());
437-
}
438-
439-
let rc = unsafe { libc::fcntl(cc_fd, libc::F_SETFL, flags | libc::O_NONBLOCK) };
440-
if rc < 0 {
441-
return Err(io::Error::last_os_error());
442-
}
435+
let flags = nix::fcntl::fcntl(cc_fd, nix::fcntl::F_GETFL)?;
436+
let arg = nix::fcntl::FcntlArg::F_SETFL(
437+
nix::fcntl::OFlag::from_bits_retain(flags) | nix::fcntl::OFlag::O_NONBLOCK,
438+
);
439+
nix::fcntl::fcntl(cc_fd, arg)?;
443440

444441
let cq = unsafe {
445442
ffi::ibv_create_cq(
@@ -523,6 +520,10 @@ impl<'ctx> CompletionQueue<'ctx> {
523520
/// is not `IBV_WC_SUCCESS`, only the following attributes are valid: `wr_id`, `status`,
524521
/// `qp_num`, and `vendor_err`.
525522
///
523+
/// Callers must ensure the CQ does not overrun (exceed its capacity), as this triggers an
524+
/// `IBV_EVENT_CQ_ERR` async event, rendering the CQ unusable. You can do this by limiting
525+
/// the number of inflight Work Requests.
526+
///
526527
/// Note that `poll` does not block or cause a context switch. This is why RDMA technologies
527528
/// can achieve very low latency (below 1 µs).
528529
#[inline]
@@ -566,26 +567,22 @@ impl<'ctx> CompletionQueue<'ctx> {
566567
/// # Errors
567568
/// - `TimedOut`: If the timeout expires before any completions are available.
568569
/// - System errors: From underlying calls like `req_notify_cq`, `poll`, or `ibv_get_cq_event`.
569-
///
570-
/// # Notes
571-
/// - This method blocks, unlike `poll`, but avoids busy-waiting by leveraging CQ events and
572-
/// system polling.
573-
/// - Callers must ensure the CQ does not overrun (exceed its capacity), as this triggers an
574-
/// `IBV_EVENT_CQ_ERR` async event, rendering the CQ unusable.
575570
pub fn wait<'c>(
576571
&self,
577572
completions: &'c mut [ffi::ibv_wc],
578573
timeout: Option<Duration>,
579574
) -> io::Result<&'c mut [ffi::ibv_wc]> {
580575
let c = completions as *mut [ffi::ibv_wc];
581576

577+
//
582578
loop {
583-
let completions = self.poll(unsafe { &mut *c })?;
584-
if !completions.is_empty() {
585-
return Ok(completions);
579+
let polled_completions = self.poll(unsafe { &mut *c })?;
580+
if !polled_completions.is_empty() {
581+
return Ok(polled_completions);
586582
}
587583

588-
let ctx: *mut ffi::ibv_context = unsafe { *self.cq }.context;
584+
// SAFETY: dereferencing completion queue, which is guaranteed to not have been destroyed yet.
585+
let ctx = unsafe { *self.cq }.context;
589586
let errno = unsafe {
590587
let ops = &mut { &mut *ctx }.ops;
591588
ops.req_notify_cq.as_mut().unwrap()(self.cq, 0)
@@ -594,25 +591,26 @@ impl<'ctx> CompletionQueue<'ctx> {
594591
return Err(io::Error::from_raw_os_error(errno));
595592
}
596593

597-
let completions = self.poll(unsafe { &mut *c })?;
598-
if !completions.is_empty() {
599-
return Ok(completions);
594+
// We poll again to avoid a race when Work Completions arrive between the first `poll()` and `req_notify_cq()`.
595+
let polled_completions = self.poll(unsafe { &mut *c })?;
596+
if !polled_completions.is_empty() {
597+
return Ok(polled_completions);
600598
}
601599

602-
let mut pollfd = libc::pollfd {
603-
fd: unsafe { *self.cc }.fd,
604-
events: libc::POLLIN,
605-
revents: 0,
606-
};
607-
let rc = unsafe {
608-
libc::poll(
609-
&mut pollfd,
610-
1,
611-
timeout.map_or(-1, |x| x.as_millis() as libc::c_int),
612-
)
613-
};
614-
match rc {
615-
-1 => return Err(io::Error::last_os_error()),
600+
// ibv_get_cq_event supports blocking operations, but the fd of cq_context was put into non blocking mode to support timeouts.
601+
let pollfd = nix::poll::PollFd::new(
602+
// SAFETY: dereferencing completion queue context, which is guaranteed to not have been destroyed yet.
603+
unsafe { BorrowedFd::borrow_raw({ *self.cc }.fd) },
604+
nix::poll::PollFlags::POLLIN,
605+
);
606+
let ret = nix::poll::poll(
607+
&mut [pollfd],
608+
timeout
609+
.map(nix::poll::PollTimeout::try_from)
610+
.transpose()
611+
.map_err(|_| io::Error::other("failedd to convert timeout to PollTimeout"))?,
612+
)?;
613+
match ret {
616614
0 => {
617615
return Err(io::Error::new(
618616
io::ErrorKind::TimedOut,
@@ -624,7 +622,10 @@ impl<'ctx> CompletionQueue<'ctx> {
624622
}
625623

626624
let mut out_cq = std::ptr::null_mut();
625+
// The cq_context is an opaque identifier that
627626
let mut out_cq_context = std::ptr::null_mut();
627+
// The Completion Notification must be read using ibv_get_cq_event().
628+
// SAFETY: c ffi call
628629
let rc = unsafe { ffi::ibv_get_cq_event(self.cc, &mut out_cq, &mut out_cq_context) };
629630
if rc < 0 {
630631
let e = io::Error::last_os_error();
@@ -635,9 +636,12 @@ impl<'ctx> CompletionQueue<'ctx> {
635636
}
636637

637638
assert_eq!(self.cq, out_cq);
638-
unsafe {
639-
ffi::ibv_ack_cq_events(self.cq, 1);
640-
};
639+
// cq_context is the user defined value passed during ibv_create_cq().
640+
assert!(out_cq_context.is_null());
641+
642+
// All completion events returned by ibv_get_cq_event() must eventually be acknowledged with ibv_ack_cq_events().
643+
// SAFETY: c ffi call
644+
unsafe { ffi::ibv_ack_cq_events(self.cq, 1) };
641645
}
642646
}
643647
}

0 commit comments

Comments
 (0)