Skip to content

Commit 160a434

Browse files
w1ldptrroiedanino
andauthored
RUST: Implement queryMem binding (#620)
* RUST: Implement queryMem binding Signed-off-by: Vlad Buslov <[email protected]> * Update src/bindings/rust/src/descriptors/query.rs Co-authored-by: Roie Danino <[email protected]> Signed-off-by: Vlad Buslov <[email protected]> * Address code review comments Signed-off-by: Vlad Buslov <[email protected]> * Add missing stub Signed-off-by: Vlad Buslov <[email protected]> * Update src/bindings/rust/src/descriptors/reg.rs Co-authored-by: Roie Danino <[email protected]> Signed-off-by: Vlad Buslov <[email protected]> * Fix compilation Signed-off-by: Vlad Buslov <[email protected]> * Address code review comments Signed-off-by: Vlad Buslov <[email protected]> * Remove code duplication in add_desc Signed-off-by: Vlad Buslov <[email protected]> * Address more code review comments Signed-off-by: Vlad Buslov <[email protected]> --------- Signed-off-by: Vlad Buslov <[email protected]> Signed-off-by: Vlad Buslov <[email protected]> Co-authored-by: Roie Danino <[email protected]>
1 parent e6ab575 commit 160a434

File tree

11 files changed

+670
-22
lines changed

11 files changed

+670
-22
lines changed

src/bindings/rust/Cargo.lock

Lines changed: 49 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/bindings/rust/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,6 @@ bindgen = "0.71"
4141
cc = { version = "1.2.23", features = ["parallel"] }
4242
pkg-config = "0.3"
4343
os_info = "3.11"
44+
45+
[dev-dependencies]
46+
tempfile = "3.20.0"

src/bindings/rust/src/agent.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
// limitations under the License.
1515

1616
use super::*;
17+
use crate::descriptors::{QueryResponseList, RegDescList};
1718

1819
/// A NIXL agent that can create backends and manage memory
1920
#[derive(Debug, Clone)]
@@ -243,6 +244,41 @@ impl Agent {
243244
})
244245
}
245246

247+
/// Query information about memory/storage
248+
///
249+
/// # Arguments
250+
/// * `descs` - Registration descriptor list to query
251+
/// * `opt_args` - Optional arguments specifying backends
252+
///
253+
/// # Returns
254+
/// A list of query responses, where each response may contain parameters
255+
/// describing the memory/storage characteristics.
256+
pub fn query_mem(
257+
&self,
258+
descs: &RegDescList,
259+
opt_args: Option<&OptArgs>,
260+
) -> Result<QueryResponseList, NixlError> {
261+
let resp = QueryResponseList::new()?;
262+
263+
let status = {
264+
let inner_guard = self.inner.write().unwrap();
265+
unsafe {
266+
nixl_capi_query_mem(
267+
inner_guard.handle.as_ptr(),
268+
descs.handle(),
269+
resp.handle(),
270+
opt_args.map_or(std::ptr::null_mut(), |args| args.inner.as_ptr()),
271+
)
272+
}
273+
};
274+
275+
match status {
276+
NIXL_CAPI_SUCCESS => Ok(resp),
277+
NIXL_CAPI_ERROR_INVALID_PARAM => Err(NixlError::InvalidParam),
278+
_ => Err(NixlError::BackendError),
279+
}
280+
}
281+
246282
/// Gets the local metadata for this agent as a byte array
247283
pub fn get_local_md(&self) -> Result<Vec<u8>, NixlError> {
248284
tracing::trace!("Getting local metadata");

src/bindings/rust/src/descriptors.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515

1616
use super::*;
1717

18+
mod query;
1819
mod reg;
1920
mod xfer;
2021

22+
pub use query::{QueryResponse, QueryResponseIterator, QueryResponseList};
2123
pub use reg::RegDescList;
2224
pub use xfer::XferDescList;
2325

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
use super::*;
17+
use crate::Params;
18+
19+
/// A safe wrapper around a NIXL query response list
20+
pub struct QueryResponseList {
21+
inner: NonNull<bindings::nixl_capi_query_resp_list_s>,
22+
}
23+
24+
/// Represents a single query response which may or may not contain parameters
25+
pub struct QueryResponse<'a> {
26+
list: &'a QueryResponseList,
27+
index: usize,
28+
}
29+
30+
impl QueryResponseList {
31+
/// Creates a new empty query response list
32+
pub fn new() -> Result<Self, NixlError> {
33+
let mut list = ptr::null_mut();
34+
let status = unsafe { nixl_capi_create_query_resp_list(&mut list) };
35+
36+
match status {
37+
NIXL_CAPI_SUCCESS => {
38+
// SAFETY: If status is NIXL_CAPI_SUCCESS, list is non-null
39+
let inner = unsafe { NonNull::new_unchecked(list) };
40+
Ok(Self { inner })
41+
}
42+
NIXL_CAPI_ERROR_INVALID_PARAM => Err(NixlError::InvalidParam),
43+
_ => Err(NixlError::BackendError),
44+
}
45+
}
46+
47+
/// Returns the number of responses in the list
48+
pub fn len(&self) -> Result<usize, NixlError> {
49+
let mut size = 0;
50+
let status = unsafe { nixl_capi_query_resp_list_size(self.inner.as_ptr(), &mut size) };
51+
52+
match status {
53+
NIXL_CAPI_SUCCESS => Ok(size),
54+
NIXL_CAPI_ERROR_INVALID_PARAM => Err(NixlError::InvalidParam),
55+
_ => Err(NixlError::BackendError),
56+
}
57+
}
58+
59+
/// Returns true if the list is empty
60+
pub fn is_empty(&self) -> Result<bool, NixlError> {
61+
Ok(self.len()? == 0)
62+
}
63+
64+
/// Gets a query response at the given index
65+
pub fn get(&self, index: usize) -> Result<QueryResponse<'_>, NixlError> {
66+
let size = self.len()?;
67+
if index >= size {
68+
return Err(NixlError::InvalidParam);
69+
}
70+
71+
Ok(QueryResponse { list: self, index })
72+
}
73+
74+
/// Returns an iterator
75+
pub fn iter(&self) -> Result<QueryResponseIterator<'_>, NixlError> {
76+
Ok(QueryResponseIterator {
77+
list: self,
78+
index: 0,
79+
len: self.len()?,
80+
})
81+
}
82+
83+
pub(crate) fn handle(&self) -> *mut bindings::nixl_capi_query_resp_list_s {
84+
self.inner.as_ptr()
85+
}
86+
}
87+
88+
impl<'a> QueryResponse<'a> {
89+
/// Returns true if this response contains parameters
90+
pub fn has_value(&self) -> Result<bool, NixlError> {
91+
let mut has_value = false;
92+
let status = unsafe {
93+
nixl_capi_query_resp_list_has_value(
94+
self.list.inner.as_ptr(),
95+
self.index,
96+
&mut has_value,
97+
)
98+
};
99+
100+
match status {
101+
NIXL_CAPI_SUCCESS => Ok(has_value),
102+
NIXL_CAPI_ERROR_INVALID_PARAM => Err(NixlError::InvalidParam),
103+
_ => Err(NixlError::BackendError),
104+
}
105+
}
106+
107+
/// Gets the parameters if this response has a value
108+
pub fn get_params(&self) -> Result<Option<Params>, NixlError> {
109+
if !self.has_value()? {
110+
return Ok(None);
111+
}
112+
113+
let mut params = ptr::null_mut();
114+
let status = unsafe {
115+
nixl_capi_query_resp_list_get_params(self.list.inner.as_ptr(), self.index, &mut params)
116+
};
117+
118+
match status {
119+
NIXL_CAPI_SUCCESS => {
120+
// SAFETY: If status is NIXL_CAPI_SUCCESS, params is non-null
121+
let inner = unsafe { NonNull::new_unchecked(params) };
122+
Ok(Some(Params::new(inner)))
123+
}
124+
NIXL_CAPI_ERROR_INVALID_PARAM => Err(NixlError::InvalidParam),
125+
_ => Err(NixlError::BackendError),
126+
}
127+
}
128+
}
129+
130+
/// An iterator over query responses
131+
pub struct QueryResponseIterator<'a> {
132+
list: &'a QueryResponseList,
133+
index: usize,
134+
len: usize,
135+
}
136+
137+
impl<'a> Iterator for QueryResponseIterator<'a> {
138+
type Item = QueryResponse<'a>;
139+
140+
fn next(&mut self) -> Option<Self::Item> {
141+
if self.index >= self.len {
142+
None
143+
} else {
144+
let response = QueryResponse {
145+
list: self.list,
146+
index: self.index,
147+
};
148+
self.index += 1;
149+
Some(response)
150+
}
151+
}
152+
153+
fn size_hint(&self) -> (usize, Option<usize>) {
154+
let remaining = self.len - self.index;
155+
(remaining, Some(remaining))
156+
}
157+
}
158+
159+
impl<'a> ExactSizeIterator for QueryResponseIterator<'a> {
160+
fn len(&self) -> usize {
161+
self.len - self.index
162+
}
163+
}
164+
165+
impl Drop for QueryResponseList {
166+
fn drop(&mut self) {
167+
// SAFETY: self.inner is guaranteed to be valid by NonNull
168+
unsafe {
169+
nixl_capi_destroy_query_resp_list(self.inner.as_ptr());
170+
}
171+
}
172+
}

src/bindings/rust/src/descriptors/reg.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,26 @@ impl<'a> RegDescList<'a> {
4848

4949
/// Adds a descriptor to the list
5050
pub fn add_desc(&mut self, addr: usize, len: usize, dev_id: u64) -> Result<(), NixlError> {
51+
self.add_desc_with_meta(addr, len, dev_id, &[])
52+
}
53+
54+
/// Add a descriptor with metadata
55+
pub fn add_desc_with_meta(
56+
&mut self,
57+
addr: usize,
58+
len: usize,
59+
dev_id: u64,
60+
metadata: &[u8],
61+
) -> Result<(), NixlError> {
5162
let status = unsafe {
52-
nixl_capi_reg_dlist_add_desc(self.inner.as_ptr(), addr as uintptr_t, len, dev_id)
63+
nixl_capi_reg_dlist_add_desc(
64+
self.inner.as_ptr(),
65+
addr as uintptr_t,
66+
len,
67+
dev_id,
68+
metadata.as_ptr() as *const std::ffi::c_void,
69+
metadata.len(),
70+
)
5371
};
5472

5573
match status {

src/bindings/rust/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ use bindings::{
6262
nixl_capi_reg_dlist_resize, nixl_capi_register_mem, nixl_capi_string_list_get,
6363
nixl_capi_string_list_size, nixl_capi_xfer_dlist_add_desc, nixl_capi_xfer_dlist_clear,
6464
nixl_capi_xfer_dlist_has_overlaps, nixl_capi_xfer_dlist_len, nixl_capi_xfer_dlist_resize,
65+
nixl_capi_query_mem, nixl_capi_create_query_resp_list, nixl_capi_destroy_query_resp_list,
66+
nixl_capi_query_resp_list_size, nixl_capi_query_resp_list_has_value,
67+
nixl_capi_query_resp_list_get_params,
6568
};
6669

6770
// Re-export status codes
@@ -81,6 +84,7 @@ mod xfer;
8184
pub use agent::*;
8285
pub use descriptors::*;
8386
pub use notify::*;
87+
pub use utils::*;
8488
pub use xfer::*;
8589

8690
/// Errors that can occur when using NIXL

0 commit comments

Comments
 (0)