Struct virtio_driver::virtqueue::Virtqueue
source · pub struct Virtqueue<'a, R: Copy> { /* private fields */ }Expand description
A virtqueue of a virtio device.
R is used to store device-specific per-request data (like the request header or status byte)
in memory shared with the device and is copied on completion. Don’t put things there that the
device doesn’t have to access, in the interest of both security and performance.
Implementations§
source§impl<'a, R: Copy> Virtqueue<'a, R>
impl<'a, R: Copy> Virtqueue<'a, R>
sourcepub fn new(
iova_translator: Box<dyn IovaTranslator>,
buf: &'a mut [u8],
queue_size: u16
) -> Result<Self, Error>
pub fn new(
iova_translator: Box<dyn IovaTranslator>,
buf: &'a mut [u8],
queue_size: u16
) -> Result<Self, Error>
Creates a new virtqueue in the passed memory buffer.
buf has to be memory that is visible for the device. It is used to store all descriptors,
rings and device-specific per-request data for the queue.
Examples found in repository?
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
pub fn setup_queues(
transport: &mut VirtioBlkTransport,
num_queues: usize,
queue_size: u16,
) -> Result<Vec<Self>, Error> {
if virtio_blk_max_queues(transport)? < num_queues {
return Err(Error::new(
ErrorKind::InvalidInput,
"Too many queues requested",
));
}
let layout = VirtqueueLayout::new::<VirtioBlkReqBuf>(num_queues, queue_size as usize)?;
let queues: Vec<_> = {
// Not actually needless: must drop the borrow on the transport before alloc_queue_mem()
#[allow(clippy::needless_collect)]
let iova_translators: Vec<_> = iter::repeat_with(|| transport.iova_translator())
.take(num_queues)
.collect();
let mem = transport.alloc_queue_mem(&layout)?;
iova_translators
.into_iter()
.enumerate()
.map(|(i, iova_translator)| {
let mem_queue = unsafe {
std::slice::from_raw_parts_mut(
&mut mem[i * layout.end_offset] as *mut u8,
layout.end_offset,
)
};
Virtqueue::new(iova_translator, mem_queue, queue_size)
})
.collect::<Result<_, _>>()?
};
transport.setup_queues(&queues)?;
Ok(queues.into_iter().map(Self::new).collect())
}sourcepub fn queue_size(&self) -> u16
pub fn queue_size(&self) -> u16
Returns the number of entries in each of the descriptor table and rings.
sourcepub fn desc_table_ptr(&self) -> *const u8
pub fn desc_table_ptr(&self) -> *const u8
Returns a raw pointer to the start of the descriptor table.
sourcepub fn avail_ring_ptr(&self) -> *const u8
pub fn avail_ring_ptr(&self) -> *const u8
Returns a raw pointer to the start of the available ring.
sourcepub fn used_ring_ptr(&self) -> *const u8
pub fn used_ring_ptr(&self) -> *const u8
Returns a raw pointer to the start of the used ring.
sourcepub fn add_request<F>(&mut self, prepare: F) -> Result<u16, Error>where
F: FnOnce(&mut R, &mut dyn FnMut(iovec, bool) -> Result<(), Error>) -> Result<(), Error>,
pub fn add_request<F>(&mut self, prepare: F) -> Result<u16, Error>where
F: FnOnce(&mut R, &mut dyn FnMut(iovec, bool) -> Result<(), Error>) -> Result<(), Error>,
Enqueues a new request.
prepare is a function or closure that gets a reference to the device-specific per-request
data in its final location in the virtqueue memory and a FnMut to add virtio descriptors to
the request. It can set up the per-request data as necessary and must add all descriptors
needed for the request.
The parameters of the FnMut it received are the iovec describing the buffer to be added
and a boolean from_dev that is true if this buffer is written by the device and false
if it is read by the device.
Examples found in repository?
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
fn queue_request_full(
&mut self,
req_type: VirtioBlkReqType,
offset: u64,
buf: &[iovec],
dwz_data: Option<DiscardWriteZeroesData>,
context: C,
) -> Result<(), Error> {
let lba = to_lba(offset)?;
let desc_idx = self.vq.add_request(|req, add_desc| {
*req = VirtioBlkReqBuf {
header: VirtioBlkReqHeader::new(req_type, lba),
status: 0,
dwz_data: dwz_data.unwrap_or_default(),
};
add_desc(
iovec {
iov_base: &mut req.header as *mut _ as *mut c_void,
iov_len: mem::size_of::<VirtioBlkReqHeader>(),
},
false,
)?;
if dwz_data.is_some() {
add_desc(
iovec {
iov_base: &mut req.dwz_data as *mut _ as *mut c_void,
iov_len: mem::size_of::<DiscardWriteZeroesData>(),
},
false,
)?;
}
for b in buf {
add_desc(*b, req_type.is_from_dev())?;
}
add_desc(
iovec {
iov_base: &mut req.status as *mut _ as *mut c_void,
iov_len: 1,
},
true,
)?;
Ok(())
})?;
let old = self.req_contexts[desc_idx as usize].replace(context);
assert!(old.is_none());
Ok(())
}sourcepub fn completions(&mut self) -> VirtqueueIter<'_, 'a, R> ⓘ
pub fn completions(&mut self) -> VirtqueueIter<'_, 'a, R> ⓘ
Returns an iterator that returns all completed requests.