|
@@ -1,5 +1,6 @@
|
|
#[cfg(test)]
|
|
#[cfg(test)]
|
|
use core::cmp::min;
|
|
use core::cmp::min;
|
|
|
|
+use core::hint::spin_loop;
|
|
use core::mem::size_of;
|
|
use core::mem::size_of;
|
|
use core::ptr::{self, addr_of_mut, NonNull};
|
|
use core::ptr::{self, addr_of_mut, NonNull};
|
|
use core::sync::atomic::{fence, Ordering};
|
|
use core::sync::atomic::{fence, Ordering};
|
|
@@ -92,7 +93,11 @@ impl<H: Hal> VirtQueue<H> {
|
|
/// Add buffers to the virtqueue, return a token.
|
|
/// Add buffers to the virtqueue, return a token.
|
|
///
|
|
///
|
|
/// Ref: linux virtio_ring.c virtqueue_add
|
|
/// Ref: linux virtio_ring.c virtqueue_add
|
|
- pub fn add(&mut self, inputs: &[&[u8]], outputs: &[&mut [u8]]) -> Result<u16> {
|
|
|
|
|
|
+ ///
|
|
|
|
+ /// # Safety
|
|
|
|
+ ///
|
|
|
|
+ /// The input and output buffers must remain valid until the token is returned by `pop_used`.
|
|
|
|
+ pub unsafe fn add(&mut self, inputs: &[*const [u8]], outputs: &[*mut [u8]]) -> Result<u16> {
|
|
if inputs.is_empty() && outputs.is_empty() {
|
|
if inputs.is_empty() && outputs.is_empty() {
|
|
return Err(Error::InvalidParam);
|
|
return Err(Error::InvalidParam);
|
|
}
|
|
}
|
|
@@ -109,14 +114,14 @@ impl<H: Hal> VirtQueue<H> {
|
|
unsafe {
|
|
unsafe {
|
|
for input in inputs.iter() {
|
|
for input in inputs.iter() {
|
|
let mut desc = self.desc_ptr(self.free_head);
|
|
let mut desc = self.desc_ptr(self.free_head);
|
|
- (*desc).set_buf::<H>(input);
|
|
|
|
|
|
+ (*desc).set_buf::<H>(NonNull::new(*input as *mut [u8]).unwrap());
|
|
(*desc).flags = DescFlags::NEXT;
|
|
(*desc).flags = DescFlags::NEXT;
|
|
last = self.free_head;
|
|
last = self.free_head;
|
|
self.free_head = (*desc).next;
|
|
self.free_head = (*desc).next;
|
|
}
|
|
}
|
|
for output in outputs.iter() {
|
|
for output in outputs.iter() {
|
|
let desc = self.desc_ptr(self.free_head);
|
|
let desc = self.desc_ptr(self.free_head);
|
|
- (*desc).set_buf::<H>(output);
|
|
|
|
|
|
+ (*desc).set_buf::<H>(NonNull::new(*output).unwrap());
|
|
(*desc).flags = DescFlags::NEXT | DescFlags::WRITE;
|
|
(*desc).flags = DescFlags::NEXT | DescFlags::WRITE;
|
|
last = self.free_head;
|
|
last = self.free_head;
|
|
self.free_head = (*desc).next;
|
|
self.free_head = (*desc).next;
|
|
@@ -150,6 +155,32 @@ impl<H: Hal> VirtQueue<H> {
|
|
Ok(head)
|
|
Ok(head)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /// Add the given buffers to the virtqueue, notifies the device, blocks until the device uses
|
|
|
|
+ /// them, then pops them.
|
|
|
|
+ ///
|
|
|
|
+ /// This assumes that the device isn't processing any other buffers at the same time.
|
|
|
|
+ pub fn add_notify_wait_pop(
|
|
|
|
+ &mut self,
|
|
|
|
+ inputs: &[*const [u8]],
|
|
|
|
+ outputs: &[*mut [u8]],
|
|
|
|
+ transport: &mut impl Transport,
|
|
|
|
+ ) -> Result<u32> {
|
|
|
|
+ // Safe because we don't return until the same token has been popped, so they remain valid
|
|
|
|
+ // until then.
|
|
|
|
+ let token = unsafe { self.add(inputs, outputs) }?;
|
|
|
|
+
|
|
|
|
+ // Notify the queue.
|
|
|
|
+ transport.notify(self.queue_idx);
|
|
|
|
+
|
|
|
|
+ while !self.can_pop() {
|
|
|
|
+ spin_loop();
|
|
|
|
+ }
|
|
|
|
+ let (popped_token, length) = self.pop_used()?;
|
|
|
|
+ assert_eq!(popped_token, token);
|
|
|
|
+
|
|
|
|
+ Ok(length)
|
|
|
|
+ }
|
|
|
|
+
|
|
/// Returns a non-null pointer to the descriptor at the given index.
|
|
/// Returns a non-null pointer to the descriptor at the given index.
|
|
fn desc_ptr(&mut self, index: u16) -> *mut Descriptor {
|
|
fn desc_ptr(&mut self, index: u16) -> *mut Descriptor {
|
|
// Safe because self.desc is properly aligned and dereferenceable.
|
|
// Safe because self.desc is properly aligned and dereferenceable.
|
|
@@ -263,8 +294,11 @@ pub(crate) struct Descriptor {
|
|
}
|
|
}
|
|
|
|
|
|
impl Descriptor {
|
|
impl Descriptor {
|
|
- fn set_buf<H: Hal>(&mut self, buf: &[u8]) {
|
|
|
|
- self.addr = H::virt_to_phys(buf.as_ptr() as usize) as u64;
|
|
|
|
|
|
+ /// # Safety
|
|
|
|
+ ///
|
|
|
|
+ /// The caller must ensure that the buffer lives at least as long as the descriptor is active.
|
|
|
|
+ unsafe fn set_buf<H: Hal>(&mut self, buf: NonNull<[u8]>) {
|
|
|
|
+ self.addr = H::virt_to_phys(buf.as_ptr() as *mut u8 as usize) as u64;
|
|
self.len = buf.len() as u32;
|
|
self.len = buf.len() as u32;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -408,7 +442,10 @@ mod tests {
|
|
let mut header = VirtIOHeader::make_fake_header(MODERN_VERSION, 1, 0, 0, 4);
|
|
let mut header = VirtIOHeader::make_fake_header(MODERN_VERSION, 1, 0, 0, 4);
|
|
let mut transport = unsafe { MmioTransport::new(NonNull::from(&mut header)) }.unwrap();
|
|
let mut transport = unsafe { MmioTransport::new(NonNull::from(&mut header)) }.unwrap();
|
|
let mut queue = VirtQueue::<FakeHal>::new(&mut transport, 0, 4).unwrap();
|
|
let mut queue = VirtQueue::<FakeHal>::new(&mut transport, 0, 4).unwrap();
|
|
- assert_eq!(queue.add(&[], &[]).unwrap_err(), Error::InvalidParam);
|
|
|
|
|
|
+ assert_eq!(
|
|
|
|
+ unsafe { queue.add(&[], &[]) }.unwrap_err(),
|
|
|
|
+ Error::InvalidParam
|
|
|
|
+ );
|
|
}
|
|
}
|
|
|
|
|
|
#[test]
|
|
#[test]
|
|
@@ -418,9 +455,7 @@ mod tests {
|
|
let mut queue = VirtQueue::<FakeHal>::new(&mut transport, 0, 4).unwrap();
|
|
let mut queue = VirtQueue::<FakeHal>::new(&mut transport, 0, 4).unwrap();
|
|
assert_eq!(queue.available_desc(), 4);
|
|
assert_eq!(queue.available_desc(), 4);
|
|
assert_eq!(
|
|
assert_eq!(
|
|
- queue
|
|
|
|
- .add(&[&[], &[], &[]], &[&mut [], &mut []])
|
|
|
|
- .unwrap_err(),
|
|
|
|
|
|
+ unsafe { queue.add(&[&[], &[], &[]], &[&mut [], &mut []]) }.unwrap_err(),
|
|
Error::BufferTooSmall
|
|
Error::BufferTooSmall
|
|
);
|
|
);
|
|
}
|
|
}
|
|
@@ -435,9 +470,7 @@ mod tests {
|
|
|
|
|
|
// Add a buffer chain consisting of two device-readable parts followed by two
|
|
// Add a buffer chain consisting of two device-readable parts followed by two
|
|
// device-writable parts.
|
|
// device-writable parts.
|
|
- let token = queue
|
|
|
|
- .add(&[&[1, 2], &[3]], &[&mut [0, 0], &mut [0]])
|
|
|
|
- .unwrap();
|
|
|
|
|
|
+ let token = unsafe { queue.add(&[&[1, 2], &[3]], &[&mut [0, 0], &mut [0]]) }.unwrap();
|
|
|
|
|
|
assert_eq!(queue.available_desc(), 0);
|
|
assert_eq!(queue.available_desc(), 0);
|
|
assert!(!queue.can_pop());
|
|
assert!(!queue.can_pop());
|