1use std::ffi::CStr;
4use std::sync::Arc;
5
6use rdma_io_sys::ibverbs::*;
7use rdma_io_sys::wrapper::rdma_wrap____ibv_query_port;
8
9use crate::error::{from_ptr, from_ret};
10use crate::{Error, Result};
11
12#[derive(Debug)]
17pub struct Device {
18 list: Arc<DeviceList>,
19 index: usize,
20}
21
22#[derive(Debug)]
24struct DeviceList {
25 list: *mut *mut ibv_device,
26 #[allow(dead_code)]
27 count: usize,
28}
29
30unsafe impl Send for DeviceList {}
32unsafe impl Sync for DeviceList {}
33
34impl Drop for DeviceList {
35 fn drop(&mut self) {
36 unsafe { ibv_free_device_list(self.list) };
37 }
38}
39
40pub fn devices() -> Result<Vec<Device>> {
45 let mut num_devices: i32 = 0;
46 let list = unsafe { ibv_get_device_list(&mut num_devices) };
47 if list.is_null() {
48 return Err(Error::Verbs(std::io::Error::last_os_error()));
49 }
50 let count = num_devices as usize;
51 let shared = Arc::new(DeviceList { list, count });
52 let devs = (0..count)
53 .map(|i| Device {
54 list: Arc::clone(&shared),
55 index: i,
56 })
57 .collect();
58 Ok(devs)
59}
60
61impl Device {
62 pub fn name(&self) -> &str {
64 let dev = self.as_ptr();
65 let name = unsafe { ibv_get_device_name(dev) };
66 if name.is_null() {
67 "<unknown>"
68 } else {
69 unsafe { CStr::from_ptr(name) }
70 .to_str()
71 .unwrap_or("<invalid utf8>")
72 }
73 }
74
75 pub fn guid(&self) -> u64 {
77 unsafe { ibv_get_device_guid(self.as_ptr()) }
78 }
79
80 pub fn transport_type(&self) -> ibv_transport_type {
85 unsafe { (*self.as_ptr()).transport_type }
86 }
87
88 pub fn is_iwarp(&self) -> bool {
90 self.transport_type() == IBV_TRANSPORT_IWARP
91 }
92
93 pub fn open(&self) -> Result<Context> {
95 let ctx = from_ptr(unsafe { ibv_open_device(self.as_ptr()) })?;
96 Ok(Context {
97 inner: ctx,
98 owned: true,
99 })
100 }
101
102 fn as_ptr(&self) -> *mut ibv_device {
103 unsafe { *self.list.list.add(self.index) }
104 }
105}
106
107pub struct Context {
113 pub(crate) inner: *mut ibv_context,
114 owned: bool,
116}
117
118unsafe impl Send for Context {}
120unsafe impl Sync for Context {}
121
122impl Drop for Context {
123 fn drop(&mut self) {
124 if self.owned {
125 let ret = unsafe { ibv_close_device(self.inner) };
126 if ret != 0 {
127 tracing::error!(
128 "ibv_close_device failed: {}",
129 std::io::Error::from_raw_os_error(-ret)
130 );
131 }
132 }
133 }
134}
135
136impl Context {
137 pub unsafe fn from_raw(ctx: *mut ibv_context, owned: bool) -> Self {
143 Self { inner: ctx, owned }
144 }
145
146 pub fn query_device(&self) -> Result<ibv_device_attr> {
148 let mut attr = ibv_device_attr::default();
149 from_ret(unsafe { ibv_query_device(self.inner, &mut attr) })?;
150 Ok(attr)
151 }
152
153 pub fn query_port(&self, port_num: u8) -> Result<ibv_port_attr> {
155 let mut attr = ibv_port_attr::default();
156 from_ret(unsafe { rdma_wrap____ibv_query_port(self.inner, port_num, &mut attr) })?;
157 Ok(attr)
158 }
159
160 pub fn query_gid(&self, port_num: u8, index: i32) -> Result<ibv_gid> {
162 let mut gid = ibv_gid::default();
163 from_ret(unsafe { ibv_query_gid(self.inner, port_num, index, &mut gid) })?;
164 Ok(gid)
165 }
166
167 pub fn as_raw(&self) -> *mut ibv_context {
169 self.inner
170 }
171}
172
173pub fn open_first_device() -> Result<Context> {
177 let devs = devices()?;
178 if devs.is_empty() {
179 return Err(Error::NoDevices);
180 }
181 devs[0].open()
182}
183
184pub fn any_device_is_iwarp() -> bool {
193 devices()
194 .ok()
195 .map(|d| d.iter().any(|dev| dev.is_iwarp()))
196 .unwrap_or(false)
197}
198
199pub fn supports_mw_type2(pd: &Arc<crate::pd::ProtectionDomain>) -> bool {
212 let attr = match pd.context().query_device() {
213 Ok(attr) => attr,
214 Err(_) => return false,
215 };
216 let flags = attr.device_cap_flags;
217 flags
218 & (rdma_io_sys::ibverbs::IBV_DEVICE_MEM_WINDOW_TYPE_2A
219 | rdma_io_sys::ibverbs::IBV_DEVICE_MEM_WINDOW_TYPE_2B)
220 != 0
221}
222
223pub fn open_device_by_name(name: &str) -> Result<Context> {
225 let devs = devices()?;
226 for d in &devs {
227 if d.name() == name {
228 return d.open();
229 }
230 }
231 Err(Error::DeviceNotFound(name.to_string()))
232}