On Fri, Aug 11, 2023 at 01:18:17PM -0500, Eric Blake wrote:
See the earlier commit "Add Extent64 arg type" for
rationale in
supporting a new generator arg type. This patch adds the Rust
bindings for use of Extent64.
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
Tage, could you please review this patch? I'm about to commit my
64-bit extension patches, but had to rebase them on top of your Rust
patches that landed in the meantime. I'm new enough to Rust that I'm
not sure if I'm doing everything in the optimum manner, so I welcome
any insights you may offer.
This email is one of the two main patches I'm worried about, I'll
reply again with a link to a git repo and the diff to the generated
code after this patch plus minor tweaks to 9 and 10/25 are in place to
actually put this code to use (uncommenting extent64_callback and
actually defining the nbd_block_status_64 API is what makes the code
paths in this patch worth anything); plus the relevant changes to
patch to 11/25 that copies the language bindings unit tests I did for
all the other bindings to also work in Rust.
As promised, here's what changes in the generated Rust bindings once
my series is applied to the point of the new APIs being generated:
[Side note: I noticed that rustfmt rearranged 'use' statements
compared to how Rust.ml has them; while it is not essential, we might
as well consider obvious cleanup patches that makes the diff between
our generated output and rustfmt's cleanup of the same be closer
together, as it is easier to search Rust.ml for the places to touch if
I'm searching for the same strings.]
--- rust/src/bindings.rs.bak 2023-08-10 13:40:18.205211694 -0500
+++ rust/src/bindings.rs 2023-08-11 13:32:53.827881956 -0500
@@ -22,6 +22,7 @@
use crate::{types::*, *};
use bitflags::bitflags;
+use libnbd_sys::nbd_extent;
use os_socketaddr::OsSocketAddr;
use std::ffi::*;
use std::mem;
@@ -231,6 +232,40 @@
}
}
+pub(crate) unsafe fn extent64_to_raw<F>(f: F) -> sys::nbd_extent64_callback
+where
+ F: FnMut(&[u8], u64, &[NbdExtent], &mut c_int) -> c_int + Send +
Sync,
+{
+ unsafe extern "C" fn call_closure<F>(
+ data: *mut c_void,
+ metacontext_ffi: *const c_char,
+ offset_ffi: u64,
+ entries_ffi: *mut nbd_extent,
+ nr_entries_ffi: usize,
+ error_ffi: *mut c_int,
+ ) -> c_int
+ where
+ F: FnMut(&[u8], u64, &[NbdExtent], &mut c_int) -> c_int + Send +
Sync,
+ {
+ let callback_ptr = data as *mut F;
+ let callback = &mut *callback_ptr;
+ let metacontext: &[u8] = CStr::from_ptr(metacontext_ffi).to_bytes();
+ let offset: u64 = offset_ffi;
+ let entries: &[NbdExtent] = slice::from_raw_parts(
+ entries_ffi as *const NbdExtent,
+ nr_entries_ffi,
+ );
+ let error: &mut c_int = error_ffi.as_mut().unwrap();
+ callback(metacontext, offset, entries, error)
+ }
+ let callback_data = Box::into_raw(Box::new(f));
+ sys::nbd_extent64_callback {
+ callback: Some(call_closure::<F>),
+ user_data: callback_data as *mut _,
+ free: Some(utils::drop_data::<F>),
+ }
+}
+
pub(crate) unsafe fn list_to_raw<F>(f: F) -> sys::nbd_list_callback
where
F: FnMut(&[u8], &[u8]) -> c_int + Send + Sync,
@@ -1092,6 +1127,92 @@
}
}
+ /// control use of extended headers
+ ///
+ /// By default, libnbd tries to negotiate extended headers
+ /// with the server, as this protocol extension permits the
+ /// use of 64-bit zero, trim, and block status actions.
+ /// However, for integration testing, it can be useful to
+ /// clear this flag rather than find a way to alter the
+ /// server to fail the negotiation request.
+ ///
+ /// For backwards compatibility, the setting of this knob is
+ /// ignored if nbd_set_request_structured_replies(3) is also
+ /// set to false, since the use of extended headers implies
+ /// structured replies.
+ ///
+ pub fn set_request_extended_headers(&self, request: bool) -> Result<()>
{
+ // Convert all arguments to FFI-like types.
+ let request_ffi = request;
+
+ // Call the FFI-function.
+ let ffi_ret = unsafe {
+ sys::nbd_set_request_extended_headers(self.handle, request_ffi)
+ };
+
+ // Convert the result to something more rusty.
+ if ffi_ret < 0 {
+ Err(unsafe { Error::get_error(self.raw_handle()) })
+ } else {
+ Ok(())
+ }
+ }
+
+ /// see if extended headers are attempted
+ ///
+ /// Return the state of the request extended headers flag on
+ /// this handle.
+ ///
+ /// Note: If you want to find out if extended headers were
+ /// actually negotiated on a particular connection use
+ /// nbd_get_extended_headers_negotiated(3) instead.
+ ///
+ pub fn get_request_extended_headers(&self) -> bool {
+ // Convert all arguments to FFI-like types.
+
+ // Call the FFI-function.
+ let ffi_ret =
+ unsafe { sys::nbd_get_request_extended_headers(self.handle) };
+
+ // Convert the result to something more rusty.
+ ffi_ret != 0
+ }
+
+ /// see if extended headers are in use
+ ///
+ /// After connecting you may call this to find out if the
+ /// connection is using extended headers.
+ ///
+ /// When extended headers are not in use, commands are
+ /// limited to a 32-bit length, even when the libnbd API
+ /// uses a 64-bit parameter to express the length. But even
+ /// when extended headers are supported, the server may
+ /// enforce other limits, visible through
+ /// nbd_get_block_size(3).
+ ///
+ /// Note that when extended headers are negotiated, you
+ /// should prefer the use of nbd_block_status_64(3) instead
+ /// of nbd_block_status(3) if any of the meta contexts you
+ /// requested via nbd_add_meta_context(3) might return
+ /// 64-bit status values; however, all of the well-known
+ /// meta contexts covered by current "LIBNBD_CONTEXT_*"
+ /// constants only return 32-bit status.
+ ///
+ pub fn get_extended_headers_negotiated(&self) -> Result<bool> {
+ // Convert all arguments to FFI-like types.
+
+ // Call the FFI-function.
+ let ffi_ret =
+ unsafe { sys::nbd_get_extended_headers_negotiated(self.handle) };
+
+ // Convert the result to something more rusty.
+ if ffi_ret < 0 {
+ Err(unsafe { Error::get_error(self.raw_handle()) })
+ } else {
+ Ok(ffi_ret != 0)
+ }
+ }
+
/// control use of structured replies
///
/// By default, libnbd tries to negotiate structured replies
@@ -1104,6 +1225,9 @@
/// nbd_set_opt_mode(3) if it is desired to control when to
/// send nbd_opt_structured_reply(3) during negotiation.
///
+ /// Note that setting this knob to false also disables any
+ /// automatic request for extended headers.
+ ///
pub fn set_request_structured_replies(&self, request: bool) ->
Result<()> {
// Convert all arguments to FFI-like types.
let request_ffi = request;
@@ -1149,6 +1273,11 @@
/// second nbd_opt_structured_reply(3) returns false because
/// the server detected a duplicate request.
///
+ /// Note that if the connection negotiates extended headers,
+ /// this function returns true (as extended headers imply
+ /// structured replies) even if no explicit request for
+ /// structured replies was attempted.
+ ///
pub fn get_structured_replies_negotiated(&self) -> Result<bool> {
// Convert all arguments to FFI-like types.
@@ -2089,9 +2218,9 @@
/// During connection libnbd can negotiate zero or more
/// metadata contexts with the server. Metadata contexts are
/// features (such as "base:allocation") which describe
- /// information returned by the nbd_block_status(3) command
- /// (for "base:allocation" this is whether blocks of data
- /// are allocated, zero or sparse).
+ /// information returned by the nbd_block_status_64(3)
+ /// command (for "base:allocation" this is whether blocks of
+ /// data are allocated, zero or sparse).
///
/// This call adds one metadata context to the list to be
/// negotiated. You can call it as many times as needed. The
@@ -2140,9 +2269,9 @@
/// During connection libnbd can negotiate zero or more
/// metadata contexts with the server. Metadata contexts are
/// features (such as "base:allocation") which describe
- /// information returned by the nbd_block_status(3) command
- /// (for "base:allocation" this is whether blocks of data
- /// are allocated, zero or sparse).
+ /// information returned by the nbd_block_status_64(3)
+ /// command (for "base:allocation" this is whether blocks of
+ /// data are allocated, zero or sparse).
///
/// This command returns how many meta contexts have been
/// added to the list to request from the server via
@@ -2169,9 +2298,9 @@
/// During connection libnbd can negotiate zero or more
/// metadata contexts with the server. Metadata contexts are
/// features (such as "base:allocation") which describe
- /// information returned by the nbd_block_status(3) command
- /// (for "base:allocation" this is whether blocks of data
- /// are allocated, zero or sparse).
+ /// information returned by the nbd_block_status_64(3)
+ /// command (for "base:allocation" this is whether blocks of
+ /// data are allocated, zero or sparse).
///
/// This command returns the i'th meta context request, as
/// added by nbd_add_meta_context(3), and bounded by
@@ -2204,9 +2333,9 @@
/// During connection libnbd can negotiate zero or more
/// metadata contexts with the server. Metadata contexts are
/// features (such as "base:allocation") which describe
- /// information returned by the nbd_block_status(3) command
- /// (for "base:allocation" this is whether blocks of data
- /// are allocated, zero or sparse).
+ /// information returned by the nbd_block_status_64(3)
+ /// command (for "base:allocation" this is whether blocks of
+ /// data are allocated, zero or sparse).
///
/// This command resets the list of meta contexts to request
/// back to an empty list, for re-population by further use
@@ -3576,8 +3705,10 @@
/// nbd_can_trim(3) is false or nbd_is_read_only(3) is true.
///
/// Note that not all servers can support a "count" of 4GiB
- /// or larger. The NBD protocol does not yet have a way for
- /// a client to learn if the server will enforce an even
+ /// or larger; nbd_get_extended_headers_negotiated(3)
+ /// indicates which servers will parse a request larger than
+ /// 32 bits. The NBD protocol does not yet have a way for a
+ /// client to learn if the server will enforce an even
/// smaller maximum trim size, although a future extension
/// may add a constraint visible in nbd_get_block_size(3).
///
@@ -3628,8 +3759,10 @@
/// return an error if nbd_can_cache(3) is false.
///
/// Note that not all servers can support a "count" of 4GiB
- /// or larger. The NBD protocol does not yet have a way for
- /// a client to learn if the server will enforce an even
+ /// or larger; nbd_get_extended_headers_negotiated(3)
+ /// indicates which servers will parse a request larger than
+ /// 32 bits. The NBD protocol does not yet have a way for a
+ /// client to learn if the server will enforce an even
/// smaller maximum cache size, although a future extension
/// may add a constraint visible in nbd_get_block_size(3).
///
@@ -3678,8 +3811,10 @@
/// nbd_can_zero(3) is false or nbd_is_read_only(3) is true.
///
/// Note that not all servers can support a "count" of 4GiB
- /// or larger. The NBD protocol does not yet have a way for
- /// a client to learn if the server will enforce an even
+ /// or larger; nbd_get_extended_headers_negotiated(3)
+ /// indicates which servers will parse a request larger than
+ /// 32 bits. The NBD protocol does not yet have a way for a
+ /// client to learn if the server will enforce an even
/// smaller maximum zero size, although a future extension
/// may add a constraint visible in nbd_get_block_size(3).
/// Also, some servers may permit a larger zero request only
@@ -3728,7 +3863,7 @@
}
}
- /// send block status command to the NBD server
+ /// send block status command, with 32-bit callback
///
/// Issue the block status command to the NBD server. If
/// supported by the server, this causes metadata context
@@ -3741,11 +3876,22 @@
/// identical between contexts.
///
/// Note that not all servers can support a "count" of 4GiB
- /// or larger. The NBD protocol does not yet have a way for
- /// a client to learn if the server will enforce an even
+ /// or larger; nbd_get_extended_headers_negotiated(3)
+ /// indicates which servers will parse a request larger than
+ /// 32 bits. The NBD protocol does not yet have a way for a
+ /// client to learn if the server will enforce an even
/// smaller maximum block status size, although a future
/// extension may add a constraint visible in
- /// nbd_get_block_size(3).
+ /// nbd_get_block_size(3). Furthermore, this function is
+ /// inherently limited to 32-bit values. If the server
+ /// replies with a larger extent, the length of that extent
+ /// will be truncated to just below 32 bits and any further
+ /// extents from the server will be ignored. If the server
+ /// replies with a status value larger than 32 bits (only
+ /// possible when extended headers are in use), the callback
+ /// function will be passed an "EOVERFLOW" error. To get the
+ /// full extent information from a server that supports
+ /// 64-bit extents, you must use nbd_block_status_64(3).
///
/// Depending on which metadata contexts were enabled before
/// connecting (see nbd_add_meta_context(3)) and which are
@@ -3767,9 +3913,9 @@
/// pairs of integers with the first entry in each pair
/// being the length (in bytes) of the block and the second
/// entry being a status/flags field which is specific to
- /// the metadata context. (The number of pairs passed to the
- /// function is "nr_entries/2".) The NBD protocol document
- /// in the section about "NBD_REPLY_TYPE_BLOCK_STATUS"
+ /// the metadata context. The number of pairs passed to the
+ /// function is "nr_entries/2". The NBD protocol document in
+ /// the section about "NBD_REPLY_TYPE_BLOCK_STATUS"
/// describes the meaning of this array; for contexts known
/// to libnbd, <libnbd.h> contains constants beginning with
/// "LIBNBD_STATE_" that may help decipher the values. On
@@ -3836,6 +3982,114 @@
}
}
+ /// send block status command, with 64-bit callback
+ ///
+ /// Issue the block status command to the NBD server. If
+ /// supported by the server, this causes metadata context
+ /// information about blocks beginning from the specified
+ /// offset to be returned. The "count" parameter is a hint:
+ /// the server may choose to return less status, or the
+ /// final block may extend beyond the requested range. If
+ /// multiple contexts are supported, the number of blocks
+ /// and cumulative length of those blocks need not be
+ /// identical between contexts.
+ ///
+ /// Note that not all servers can support a "count" of 4GiB
+ /// or larger; nbd_get_extended_headers_negotiated(3)
+ /// indicates which servers will parse a request larger than
+ /// 32 bits. The NBD protocol does not yet have a way for a
+ /// client to learn if the server will enforce an even
+ /// smaller maximum block status size, although a future
+ /// extension may add a constraint visible in
+ /// nbd_get_block_size(3).
+ ///
+ /// Depending on which metadata contexts were enabled before
+ /// connecting (see nbd_add_meta_context(3)) and which are
+ /// supported by the server (see nbd_can_meta_context(3))
+ /// this call returns information about extents by calling
+ /// back to the "extent64" function. The callback cannot
+ /// call "nbd_*" APIs on the same handle since it holds the
+ /// handle lock and will cause a deadlock. If the callback
+ /// returns -1, and no earlier error has been detected, then
+ /// the overall block status command will fail with any
+ /// non-zero value stored into the callback's "error"
+ /// parameter (with a default of "EPROTO"); but any further
+ /// contexts will still invoke the callback.
+ ///
+ /// The "extent64" function is called once per type of
+ /// metadata available, with the "user_data" passed to this
+ /// function. The "metacontext" parameter is a string such
+ /// as "base:allocation". The "entries" array is an array of
+ /// nbd_extent structs, containing length (in bytes) of the
+ /// block and a status/flags field which is specific to the
+ /// metadata context. The number of array entries passed to
+ /// the function is "nr_entries". The NBD protocol document
+ /// in the section about "NBD_REPLY_TYPE_BLOCK_STATUS"
+ /// describes the meaning of this array; for contexts known
+ /// to libnbd, <libnbd.h> contains constants beginning with
+ /// "LIBNBD_STATE_" that may help decipher the values. On
+ /// entry to the callback, the "error" parameter contains
+ /// the errno value of any previously detected error.
+ ///
+ /// It is possible for the extent function to be called more
+ /// times than you expect (if the server is buggy), so
+ /// always check the "metacontext" field to ensure you are
+ /// receiving the data you expect. It is also possible that
+ /// the extent function is not called at all, even for
+ /// metadata contexts that you requested. This indicates
+ /// either that the server doesn't support the context or
+ /// for some other reason cannot return the data.
+ ///
+ /// The "flags" parameter may be 0 for no flags, or may
+ /// contain "LIBNBD_CMD_FLAG_REQ_ONE" meaning that the
+ /// server should return only one extent per metadata
+ /// context where that extent does not exceed "count" bytes;
+ /// however, libnbd does not validate that the server obeyed
+ /// the flag.
+ ///
+ /// By default, libnbd will reject attempts to use this
+ /// function with parameters that are likely to result in
+ /// server failure, such as requesting an unknown command
+ /// flag. The nbd_set_strict_mode(3) function can be used to
+ /// alter which scenarios should await a server reply rather
+ /// than failing fast.
+ ///
+ pub fn block_status_64(
+ &self,
+ count: u64,
+ offset: u64,
+ extent64: impl FnMut(&[u8], u64, &[NbdExtent], &mut c_int) ->
c_int
+ + Send
+ + Sync
+ + 'static,
+ flags: Option<CmdFlag>,
+ ) -> Result<()> {
+ // Convert all arguments to FFI-like types.
+ let count_ffi = count;
+ let offset_ffi = offset;
+ let extent64_ffi =
+ unsafe { crate::bindings::extent64_to_raw(extent64) };
+ let flags_ffi = flags.unwrap_or(CmdFlag::empty()).bits();
+
+ // Call the FFI-function.
+ let ffi_ret = unsafe {
+ sys::nbd_block_status_64(
+ self.handle,
+ count_ffi,
+ offset_ffi,
+ extent64_ffi,
+ flags_ffi,
+ )
+ };
+
+ // Convert the result to something more rusty.
+ if ffi_ret < 0 {
+ Err(unsafe { Error::get_error(self.raw_handle()) })
+ } else {
+ Ok(())
+ }
+ }
+
/// poll the handle once
///
/// This is a simple implementation of poll(2) which is used
@@ -5175,7 +5429,7 @@
}
}
- /// send block status command to the NBD server
+ /// send block status command, with 32-bit callback
///
/// Send the block status command to the NBD server.
///
@@ -5187,6 +5441,17 @@
/// Other parameters behave as documented in
/// nbd_block_status(3).
///
+ /// This function is inherently limited to 32-bit values. If
+ /// the server replies with a larger extent, the length of
+ /// that extent will be truncated to just below 32 bits and
+ /// any further extents from the server will be ignored. If
+ /// the server replies with a status value larger than 32
+ /// bits (only possible when extended headers are in use),
+ /// the callback function will be passed an "EOVERFLOW"
+ /// error. To get the full extent information from a server
+ /// that supports 64-bit extents, you must use
+ /// nbd_aio_block_status_64(3).
+ ///
/// By default, libnbd will reject attempts to use this
/// function with parameters that are likely to result in
/// server failure, such as requesting an unknown command
@@ -5231,6 +5496,73 @@
completion_ffi,
flags_ffi,
)
+ };
+
+ // Convert the result to something more rusty.
+ if ffi_ret < 0 {
+ Err(unsafe { Error::get_error(self.raw_handle()) })
+ } else {
+ Ok(Cookie(ffi_ret.try_into().unwrap()))
+ }
+ }
+
+ /// send block status command, with 64-bit callback
+ ///
+ /// Send the block status command to the NBD server.
+ ///
+ /// To check if the command completed, call
+ /// nbd_aio_command_completed(3). Or supply the optional
+ /// "completion_callback" which will be invoked as described
+ /// in "Completion callbacks" in libnbd(3).
+ ///
+ /// Other parameters behave as documented in
+ /// nbd_block_status_64(3).
+ ///
+ /// By default, libnbd will reject attempts to use this
+ /// function with parameters that are likely to result in
+ /// server failure, such as requesting an unknown command
+ /// flag. The nbd_set_strict_mode(3) function can be used to
+ /// alter which scenarios should await a server reply rather
+ /// than failing fast.
+ ///
+ pub fn aio_block_status_64(
+ &self,
+ count: u64,
+ offset: u64,
+ extent64: impl FnMut(&[u8], u64, &[NbdExtent], &mut c_int) ->
c_int
+ + Send
+ + Sync
+ + 'static,
+ completion: Option<
+ impl FnMut(&mut c_int) -> c_int + Send + Sync + 'static,
+ >,
+ flags: Option<CmdFlag>,
+ ) -> Result<Cookie> {
+ // Convert all arguments to FFI-like types.
+ let count_ffi = count;
+ let offset_ffi = offset;
+ let extent64_ffi =
+ unsafe { crate::bindings::extent64_to_raw(extent64) };
+ let completion_ffi = match completion {
+ Some(f) => unsafe { crate::bindings::completion_to_raw(f) },
+ None => sys::nbd_completion_callback {
+ callback: None,
+ free: None,
+ user_data: ptr::null_mut(),
+ },
+ };
+ let flags_ffi = flags.unwrap_or(CmdFlag::empty()).bits();
+
+ // Call the FFI-function.
+ let ffi_ret = unsafe {
+ sys::nbd_aio_block_status_64(
+ self.handle,
+ count_ffi,
+ offset_ffi,
+ extent64_ffi,
+ completion_ffi,
+ flags_ffi,
+ )
};
// Convert the result to something more rusty.
--- rust/libnbd-sys/src/generated.rs.bak 2023-08-10 14:23:10.488910232 -0500
+++ rust/libnbd-sys/src/generated.rs 2023-08-11 13:32:53.726881102 -0500
@@ -29,6 +29,13 @@
_unused: [u8; 0],
}
+#[repr(C)]
+#[derive(Debug, Clone, Copy)]
+pub struct nbd_extent {
+ length: u64,
+ flags: u64,
+}
+
extern "C" {
pub fn nbd_get_error() -> *const c_char;
pub fn nbd_get_errno() -> c_int;
@@ -91,6 +98,22 @@
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
+pub struct nbd_extent64_callback {
+ pub callback: Option<
+ unsafe extern "C" fn(
+ *mut c_void,
+ *const c_char,
+ u64,
+ *mut nbd_extent,
+ usize,
+ *mut c_int,
+ ) -> c_int,
+ >,
+ pub user_data: *mut c_void,
+ pub free: Option<unsafe extern "C" fn(*mut c_void)>,
+}
+#[repr(C)]
+#[derive(Debug, Clone, Copy)]
pub struct nbd_list_callback {
pub callback: Option<
unsafe extern "C" fn(
@@ -169,6 +192,14 @@
handle: *mut nbd_handle,
filename: *const c_char,
) -> c_int;
+ pub fn nbd_set_request_extended_headers(
+ handle: *mut nbd_handle,
+ request: bool,
+ ) -> c_int;
+ pub fn nbd_get_request_extended_headers(handle: *mut nbd_handle) -> c_int;
+ pub fn nbd_get_extended_headers_negotiated(
+ handle: *mut nbd_handle,
+ ) -> c_int;
pub fn nbd_set_request_structured_replies(
handle: *mut nbd_handle,
request: bool,
@@ -344,6 +375,13 @@
extent: nbd_extent_callback,
flags: u32,
) -> c_int;
+ pub fn nbd_block_status_64(
+ handle: *mut nbd_handle,
+ count: u64,
+ offset: u64,
+ extent64: nbd_extent64_callback,
+ flags: u32,
+ ) -> c_int;
pub fn nbd_poll(handle: *mut nbd_handle, timeout: c_int) -> c_int;
pub fn nbd_poll2(
handle: *mut nbd_handle,
@@ -489,6 +527,14 @@
completion: nbd_completion_callback,
flags: u32,
) -> i64;
+ pub fn nbd_aio_block_status_64(
+ handle: *mut nbd_handle,
+ count: u64,
+ offset: u64,
+ extent64: nbd_extent64_callback,
+ completion: nbd_completion_callback,
+ flags: u32,
+ ) -> i64;
pub fn nbd_aio_get_fd(handle: *mut nbd_handle) -> c_int;
pub fn nbd_aio_get_direction(handle: *mut nbd_handle) -> c_uint;
pub fn nbd_aio_notify_read(handle: *mut nbd_handle) -> c_int;
--
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization:
qemu.org |
libguestfs.org