1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
//! Point-to-point RDMA channel — builder API with lifetime-safe operation posting and scope-based completion polling.
//!
//! A [`Channel`] wraps an InfiniBand [`QueuePair`] and uses Rust's borrow checker
//! to statically prevent data races between the CPU and the NIC: memory passed to a
//! work request is borrowed for the duration of the operation, so the compiler rejects
//! any attempt to read or drop it while the hardware may still be performing DMA.
//!
//! # Connection lifecycle
//!
//! A channel is established in two steps so that the endpoints can exchange connection
//! information out-of-band (typically over TCP) before the RDMA link is brought up.
//!
//! 1. **Build** — call [`Channel::builder`] (or [`ProtectionDomain::create_channel`])
//! and configure the queue pair parameters. [`build`](ChannelBuilder::build) returns
//! a [`PreparedChannel`] whose [`endpoint`](PreparedChannel::endpoint) contains the
//! local connection information.
//! 2. **Handshake** — exchange [`QueuePairEndpoint`]s with the remote peer, then call
//! [`PreparedChannel::handshake`] to bring the queue pair to the Ready-To-Send state
//! and obtain the connected [`Channel`].
//!
//! # Posting operations
//!
//! Once connected, operations can be posted at three levels of control:
//!
//! * **Blocking** — [`Channel::send`], [`Channel::receive`], [`Channel::write`],
//! [`Channel::read`] each post a single operation and spin-poll until it completes.
//! Best for simple, sequential use cases.
//! * **Scoped** — [`Channel::scope`] and [`Channel::manual_scope`] open a
//! [`PollingScope`] through which multiple operations can be posted and polled
//! independently as [`ScopedPendingWork`] handles. The scope guarantees all
//! outstanding work is polled to completion before it returns, even if the closure
//! panics. This mirrors the design of [`std::thread::scope`].
//! * **Unpolled** — [`Channel::send_unpolled`], [`Channel::receive_unpolled`],
//! [`Channel::write_unpolled`], [`Channel::read_unpolled`] are `unsafe` and return
//! raw [`PendingWork`] handles. These are the primitives that the two higher levels
//! are built on; prefer those unless you need direct control.
//!
//! # Memory safety
//!
//! Work requests borrow their data buffers for the lifetime of the operation. That
//! borrow is released only once the operation is polled to completion — or, if the
//! handle is dropped without being polled, by blocking until the hardware finishes.
//! It is therefore impossible in safe code to free or reuse a buffer that the NIC is
//! still reading from or writing to.
//!
//! # Choosing `scope` vs `manual_scope`
//!
//! * Use [`scope`](Channel::scope) when you want automatic cleanup: any work not
//! manually polled is polled to completion when the scope exits, even on panic.
//! Errors are wrapped in [`ScopeError`] to distinguish closure errors from
//! auto-poll errors.
//! * Use [`manual_scope`](Channel::manual_scope) when you want to poll everything
//! yourself and get `Result<T, E>` directly. It panics if you leave work unpolled
//! on the success path, acting as a safety net against forgotten completions.
//!
//! # Error handling
//!
//! Transport-layer errors are reported as [`TransportError`], which covers both
//! low-level ibverbs call failures and work completion errors.
//! [`Channel::scope`] wraps errors further in [`ScopeError`] to distinguish between
//! closure errors and errors discovered during automatic polling at scope exit.
//!
//! # Examples
//!
//! ## Blocking send and receive
//!
//! ```no_run
//! use ibverbs_rs::ibverbs;
//! use ibverbs_rs::channel::Channel;
//! use ibverbs_rs::ibverbs::work::{SendWorkRequest, ReceiveWorkRequest};
//!
//! let ctx = ibverbs::open_device("mlx5_0")?;
//! let pd = ctx.allocate_pd()?;
//! let prepared = Channel::builder().pd(&pd).build()?;
//!
//! // Exchange endpoints out-of-band (loopback for illustration)
//! let endpoint = prepared.endpoint();
//! let mut channel = prepared.handshake(endpoint)?;
//!
//! let mut buf = [0u8; 64];
//! let mr = pd.register_local_mr_slice(&buf)?;
//!
//! // Blocking receive (posts one WR and spins until complete)
//! channel.receive(ReceiveWorkRequest::new(&mut [mr.scatter_element(&mut buf)]))?;
//!
//! // Blocking send
//! channel.send(SendWorkRequest::new(&[mr.gather_element(&buf)]))?;
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```
//!
//! ## Scoped operations
//!
//! ```no_run
//! use ibverbs_rs::ibverbs;
//! use ibverbs_rs::channel::{Channel, ScopeError, TransportError};
//! use ibverbs_rs::ibverbs::work::{SendWorkRequest, ReceiveWorkRequest};
//!
//! let ctx = ibverbs::open_device("mlx5_0")?;
//! let pd = ctx.allocate_pd()?;
//! let prepared = Channel::builder().pd(&pd).build()?;
//! let endpoint = prepared.endpoint();
//! let mut channel = prepared.handshake(endpoint)?;
//!
//! let mut buf = [0u8; 64];
//! let mr = pd.register_local_mr_slice(&buf)?;
//!
//! channel.scope(|s| {
//! let (tx, rx) = buf.split_at_mut(32);
//!
//! // Post both operations — they execute concurrently on the NIC
//! let send = s.post_send(SendWorkRequest::new(&[mr.gather_element(tx)]))?;
//! let recv = s.post_receive(ReceiveWorkRequest::new(&mut [mr.scatter_element(rx)]))?;
//!
//! // Optionally poll individual handles for fine-grained control
//! while send.poll().is_none() {} // spin until complete
//! while recv.poll().is_none() {} // spin until complete
//!
//! Ok::<(), ScopeError<TransportError>>(())
//! })?;
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```
//!
//! See also the [`examples/channel.rs`](https://github.com/Tikitikitikidesuka/ibverbs-rs/blob/main/examples/channel.rs) file
//! for a complete runnable example.
//!
//! [`QueuePair`]: crate::ibverbs::queue_pair::QueuePair
//! [`QueuePairEndpoint`]: crate::ibverbs::queue_pair::builder::QueuePairEndpoint
use crateCachedCompletionQueue;
use crateIbvError;
use crateProtectionDomain;
use crateQueuePair;
use crateWorkError;
use RefCell;
use Rc;
use Error;
pub use ;
pub use ;
pub use PendingWork;
pub use ;
/// A safe RDMA communication endpoint built on top of a [`QueuePair`].
///
/// `Channel` wraps a queue pair with lifetime-safe operation posting through
/// [`scope`](Self::scope) and [`manual_scope`](Self::manual_scope).
///
/// A channel belongs to a [`ProtectionDomain`] and can share memory regions with
/// other channels under the same domain.
/// Use [`ProtectionDomain::create_channel`] or [`Channel::builder`] to construct one.
/// An error from an RDMA transport operation.
///
/// Wraps both low-level ibverbs errors and work completion errors into a single type.
/// Convenience alias for a [`Result`] with [`TransportError`].
pub type TransportResult<T> = ;