Linux zero-copy
This commit is contained in:
parent
992fd16078
commit
2929a10b76
4 changed files with 66 additions and 6 deletions
|
|
@ -1,6 +1,6 @@
|
|||
use futures_lite::future::block_on;
|
||||
use nusb::{
|
||||
transfer::{Buffer, Bulk, In, Out},
|
||||
transfer::{Bulk, In, Out},
|
||||
MaybeFuture,
|
||||
};
|
||||
|
||||
|
|
@ -23,12 +23,14 @@ fn main() {
|
|||
|
||||
loop {
|
||||
while ep_in.pending() < 8 {
|
||||
ep_in.submit(Buffer::new(256));
|
||||
let buffer = ep_in.allocate(4096);
|
||||
ep_in.submit(buffer);
|
||||
}
|
||||
let result = block_on(ep_in.next_complete());
|
||||
println!("{result:?}");
|
||||
if result.status.is_err() {
|
||||
break;
|
||||
}
|
||||
ep_in.submit(result.data);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -584,6 +584,26 @@ impl<EpType: EndpointType, Dir: EndpointDirection> Endpoint<EpType, Dir> {
|
|||
|
||||
/// Methods for Bulk and Interrupt endpoints.
|
||||
impl<EpType: BulkOrInterrupt, Dir: EndpointDirection> Endpoint<EpType, Dir> {
|
||||
/// Allocate a buffer for use on this endpoint, zero-copy if possible.
|
||||
///
|
||||
/// A zero-copy buffer allows the kernel to DMA directly to/from this
|
||||
/// buffer for improved performance. However, because it is not allocated
|
||||
/// with the system allocator, it cannot be converted to a `Vec` without
|
||||
/// copying.
|
||||
///
|
||||
/// This is currently only supported on Linux, falling back to [`Buffer::new`]
|
||||
/// on other platforms, or if the memory allocation fails.
|
||||
pub fn allocate(&self, len: usize) -> Buffer {
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
{
|
||||
if let Ok(b) = self.backend.allocate(len) {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
Buffer::new(len)
|
||||
}
|
||||
|
||||
/// Begin a transfer on the endpoint.
|
||||
///
|
||||
/// Submitted transfers are queued and completed in order. Once the transfer
|
||||
|
|
|
|||
|
|
@ -722,6 +722,15 @@ impl LinuxEndpoint {
|
|||
Ok(usbfs::clear_halt(&inner.interface.device.fd, endpoint)?)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn allocate(&self, len: usize) -> Result<Buffer, Errno> {
|
||||
Buffer::mmap(&self.inner.interface.device.fd, len).inspect_err(|e| {
|
||||
warn!(
|
||||
"Failed to allocate zero-copy buffer of length {len} for endpoint {}: {e}",
|
||||
self.inner.address
|
||||
);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for LinuxEndpoint {
|
||||
|
|
|
|||
|
|
@ -51,26 +51,55 @@ pub struct Buffer {
|
|||
impl Buffer {
|
||||
/// Allocate a new bufffer with the default allocator.
|
||||
///
|
||||
/// This buffer will not support zero-copy transfers, but can be cheaply
|
||||
/// converted to a `Vec<u8>`.
|
||||
/// This buffer will not support [zero-copy
|
||||
/// transfers][`crate::Endpoint::allocate`], but can be cheaply converted to
|
||||
/// a `Vec<u8>`.
|
||||
///
|
||||
/// The passed size will be used as the `transfer_len`, and the `capacity`
|
||||
/// be at least that large.
|
||||
/// will be at least that large.
|
||||
///
|
||||
/// ### Panics
|
||||
/// * If the requested length is greater than `u32::MAX`.
|
||||
#[inline]
|
||||
pub fn new(transfer_len: usize) -> Self {
|
||||
let len_u32 = transfer_len.try_into().expect("length overflow");
|
||||
let mut vec = ManuallyDrop::new(Vec::with_capacity(transfer_len));
|
||||
Buffer {
|
||||
ptr: vec.as_mut_ptr(),
|
||||
len: 0,
|
||||
transfer_len: transfer_len.try_into().expect("capacity overflow"),
|
||||
transfer_len: len_u32,
|
||||
capacity: vec.capacity().try_into().expect("capacity overflow"),
|
||||
allocator: Allocator::Default,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub(crate) fn mmap(
|
||||
fd: &std::os::unix::prelude::OwnedFd,
|
||||
len: usize,
|
||||
) -> Result<Buffer, rustix::io::Errno> {
|
||||
let len_u32 = len.try_into().expect("length overflow");
|
||||
|
||||
let ptr = unsafe {
|
||||
rustix::mm::mmap(
|
||||
std::ptr::null_mut(),
|
||||
len,
|
||||
rustix::mm::ProtFlags::READ | rustix::mm::ProtFlags::WRITE,
|
||||
rustix::mm::MapFlags::SHARED,
|
||||
fd,
|
||||
0,
|
||||
)
|
||||
}?;
|
||||
|
||||
Ok(Buffer {
|
||||
ptr: ptr as *mut u8,
|
||||
len: 0,
|
||||
transfer_len: len_u32,
|
||||
capacity: len_u32,
|
||||
allocator: Allocator::Mmap,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the number of initialized bytes in the buffer.
|
||||
///
|
||||
/// For OUT transfers, this is the amount of data written to the buffer which will be sent when the buffer is submitted.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue