mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2024-11-24 12:34:31 +00:00
Reformat comments
Test: presubmit Change-Id: I39c261d9985989873b698213c5d8b653fc13757b Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/5299850 Auto-Submit: Kaiyi Li <kaiyili@google.com> Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
This commit is contained in:
parent
9c0d3e16e7
commit
c28067d1d9
170 changed files with 835 additions and 749 deletions
|
@ -2067,7 +2067,7 @@ mod tests {
|
||||||
0x03, /* 3 name parts */
|
0x03, /* 3 name parts */
|
||||||
0x5F, 0x53, 0x42, 0x5F, /* _SB_ */
|
0x5F, 0x53, 0x42, 0x5F, /* _SB_ */
|
||||||
0x50, 0x43, 0x49, 0x30, /* PCI0 */
|
0x50, 0x43, 0x49, 0x30, /* PCI0 */
|
||||||
0x5F, 0x55, 0x49, 0x44, /* _UID */
|
0x5F, 0x55, 0x49, 0x44, /* _UID */
|
||||||
0x0b, /* WordPrefix */
|
0x0b, /* WordPrefix */
|
||||||
0x34, 0x12
|
0x34, 0x12
|
||||||
]
|
]
|
||||||
|
|
|
@ -453,8 +453,8 @@ pub trait LinuxArch {
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `components` - Parts to use to build the VM.
|
/// * `components` - Parts to use to build the VM.
|
||||||
/// * `vm_evt_wrtube` - Tube used by sub-devices to request that crosvm exit because guest
|
/// * `vm_evt_wrtube` - Tube used by sub-devices to request that crosvm exit because guest wants
|
||||||
/// wants to stop/shut down or requested reset.
|
/// to stop/shut down or requested reset.
|
||||||
/// * `system_allocator` - Allocator created by this trait's implementation of
|
/// * `system_allocator` - Allocator created by this trait's implementation of
|
||||||
/// `get_system_allocator_config`.
|
/// `get_system_allocator_config`.
|
||||||
/// * `serial_parameters` - Definitions for how the serial devices should be configured.
|
/// * `serial_parameters` - Definitions for how the serial devices should be configured.
|
||||||
|
@ -1288,8 +1288,8 @@ where
|
||||||
/// * `image` - The file containing the image to be loaded.
|
/// * `image` - The file containing the image to be loaded.
|
||||||
/// * `min_guest_addr` - The minimum address of the start of the image.
|
/// * `min_guest_addr` - The minimum address of the start of the image.
|
||||||
/// * `max_guest_addr` - The address to load the last byte of the image.
|
/// * `max_guest_addr` - The address to load the last byte of the image.
|
||||||
/// * `align` - The minimum alignment of the start address of the image in bytes
|
/// * `align` - The minimum alignment of the start address of the image in bytes (must be a power of
|
||||||
/// (must be a power of two).
|
/// two).
|
||||||
///
|
///
|
||||||
/// The guest address and size in bytes of the loaded image are returned.
|
/// The guest address and size in bytes of the loaded image are returned.
|
||||||
pub fn load_image_high<F>(
|
pub fn load_image_high<F>(
|
||||||
|
|
|
@ -94,8 +94,8 @@ pub const SERIAL_ADDR: [u64; 4] = [0x3f8, 0x2f8, 0x3e8, 0x2e8];
|
||||||
/// * `com_evt_1_3` - event for com1 and com3
|
/// * `com_evt_1_3` - event for com1 and com3
|
||||||
/// * `com_evt_1_4` - event for com2 and com4
|
/// * `com_evt_1_4` - event for com2 and com4
|
||||||
/// * `serial_parameters` - definitions of serial parameter configurations.
|
/// * `serial_parameters` - definitions of serial parameter configurations.
|
||||||
/// * `serial_jail` - minijail object cloned for use with each serial device.
|
/// * `serial_jail` - minijail object cloned for use with each serial device. All four of the
|
||||||
/// All four of the traditional PC-style serial ports (COM1-COM4) must be specified.
|
/// traditional PC-style serial ports (COM1-COM4) must be specified.
|
||||||
pub fn add_serial_devices(
|
pub fn add_serial_devices(
|
||||||
protection_type: ProtectionType,
|
protection_type: ProtectionType,
|
||||||
io_bus: &Bus,
|
io_bus: &Bus,
|
||||||
|
|
|
@ -179,7 +179,8 @@ mod tests {
|
||||||
let layout = Layout::from_size_align(size_of::<u32>() * 15, align_of::<u32>()).unwrap();
|
let layout = Layout::from_size_align(size_of::<u32>() * 15, align_of::<u32>()).unwrap();
|
||||||
let allocation = LayoutAllocation::zeroed(layout);
|
let allocation = LayoutAllocation::zeroed(layout);
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// Slice less than the allocation size, which will return a slice of only the requested length.
|
// Slice less than the allocation size, which will return a slice of only the requested
|
||||||
|
// length.
|
||||||
let slice: &[u32] = unsafe { allocation.as_slice(15) };
|
let slice: &[u32] = unsafe { allocation.as_slice(15) };
|
||||||
assert_eq!(slice.len(), 15);
|
assert_eq!(slice.len(), 15);
|
||||||
assert_eq!(slice[0], 0);
|
assert_eq!(slice[0], 0);
|
||||||
|
@ -192,7 +193,8 @@ mod tests {
|
||||||
let allocation = LayoutAllocation::zeroed(layout);
|
let allocation = LayoutAllocation::zeroed(layout);
|
||||||
|
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// Slice less than the allocation size, which will return a slice of only the requested length.
|
// Slice less than the allocation size, which will return a slice of only the requested
|
||||||
|
// length.
|
||||||
let slice: &[u32] = unsafe { allocation.as_slice(5) };
|
let slice: &[u32] = unsafe { allocation.as_slice(5) };
|
||||||
assert_eq!(slice.len(), 5);
|
assert_eq!(slice.len(), 5);
|
||||||
}
|
}
|
||||||
|
@ -203,7 +205,8 @@ mod tests {
|
||||||
let allocation = LayoutAllocation::zeroed(layout);
|
let allocation = LayoutAllocation::zeroed(layout);
|
||||||
|
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// Slice more than the allocation size, which will clamp the returned slice len to the limit.
|
// Slice more than the allocation size, which will clamp the returned slice len to the
|
||||||
|
// limit.
|
||||||
let slice: &[u32] = unsafe { allocation.as_slice(100) };
|
let slice: &[u32] = unsafe { allocation.as_slice(100) };
|
||||||
assert_eq!(slice.len(), 15);
|
assert_eq!(slice.len(), 15);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,8 +27,8 @@ use crate::Result;
|
||||||
/// - Uses eventfd on Linux.
|
/// - Uses eventfd on Linux.
|
||||||
/// - Uses synchapi event objects on Windows.
|
/// - Uses synchapi event objects on Windows.
|
||||||
/// - The `Event` and `WaitContext` APIs together cannot easily be implemented with the same
|
/// - The `Event` and `WaitContext` APIs together cannot easily be implemented with the same
|
||||||
/// semantics on all platforms. In particular, it is difficult to support multiple readers, so only
|
/// semantics on all platforms. In particular, it is difficult to support multiple readers, so
|
||||||
/// a single reader is allowed for now. Multiple readers will result in undefined behavior.
|
/// only a single reader is allowed for now. Multiple readers will result in undefined behavior.
|
||||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct Event(pub(crate) PlatformEvent);
|
pub struct Event(pub(crate) PlatformEvent);
|
||||||
|
|
|
@ -172,7 +172,8 @@ pub trait FileReadWriteAtVolatile {
|
||||||
/// method must behave as a single call to `read_at_volatile` with the buffers concatenated
|
/// method must behave as a single call to `read_at_volatile` with the buffers concatenated
|
||||||
/// would. The default implementation calls `read_at_volatile` with either the first nonempty
|
/// would. The default implementation calls `read_at_volatile` with either the first nonempty
|
||||||
/// buffer provided, or returns `Ok(0)` if none exists.
|
/// buffer provided, or returns `Ok(0)` if none exists.
|
||||||
/// On Windows file pointer will update with the read, but on Linux the file pointer will not change.
|
/// On Windows file pointer will update with the read, but on Linux the file pointer will not
|
||||||
|
/// change.
|
||||||
fn read_vectored_at_volatile(&mut self, bufs: &[VolatileSlice], offset: u64) -> Result<usize> {
|
fn read_vectored_at_volatile(&mut self, bufs: &[VolatileSlice], offset: u64) -> Result<usize> {
|
||||||
if let Some(&slice) = bufs.first() {
|
if let Some(&slice) = bufs.first() {
|
||||||
self.read_at_volatile(slice, offset)
|
self.read_at_volatile(slice, offset)
|
||||||
|
@ -182,8 +183,8 @@ pub trait FileReadWriteAtVolatile {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads bytes from this file at `offset` into the given slice until all bytes in the slice are
|
/// Reads bytes from this file at `offset` into the given slice until all bytes in the slice are
|
||||||
/// read, or an error is returned. On Windows file pointer will update with the read, but on Linux the
|
/// read, or an error is returned. On Windows file pointer will update with the read, but on
|
||||||
/// file pointer will not change.
|
/// Linux the file pointer will not change.
|
||||||
fn read_exact_at_volatile(&mut self, mut slice: VolatileSlice, mut offset: u64) -> Result<()> {
|
fn read_exact_at_volatile(&mut self, mut slice: VolatileSlice, mut offset: u64) -> Result<()> {
|
||||||
while slice.size() > 0 {
|
while slice.size() > 0 {
|
||||||
match self.read_at_volatile(slice, offset) {
|
match self.read_at_volatile(slice, offset) {
|
||||||
|
@ -209,7 +210,8 @@ pub trait FileReadWriteAtVolatile {
|
||||||
/// consumed. This method must behave as a call to `write_at_volatile` with the buffers
|
/// consumed. This method must behave as a call to `write_at_volatile` with the buffers
|
||||||
/// concatenated would. The default implementation calls `write_at_volatile` with either the
|
/// concatenated would. The default implementation calls `write_at_volatile` with either the
|
||||||
/// first nonempty buffer provided, or returns `Ok(0)` if none exists.
|
/// first nonempty buffer provided, or returns `Ok(0)` if none exists.
|
||||||
/// On Windows file pointer will update with the write, but on Linux the file pointer will not change.
|
/// On Windows file pointer will update with the write, but on Linux the file pointer will not
|
||||||
|
/// change.
|
||||||
fn write_vectored_at_volatile(&mut self, bufs: &[VolatileSlice], offset: u64) -> Result<usize> {
|
fn write_vectored_at_volatile(&mut self, bufs: &[VolatileSlice], offset: u64) -> Result<usize> {
|
||||||
if let Some(&slice) = bufs.first() {
|
if let Some(&slice) = bufs.first() {
|
||||||
self.write_at_volatile(slice, offset)
|
self.write_at_volatile(slice, offset)
|
||||||
|
|
|
@ -64,8 +64,8 @@ impl PlatformEvent {
|
||||||
}
|
}
|
||||||
Ok(PlatformEvent {
|
Ok(PlatformEvent {
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// This is safe because we checked ret for success and know the kernel gave us an fd that we
|
// This is safe because we checked ret for success and know the kernel gave us an fd
|
||||||
// own.
|
// that we own.
|
||||||
event_handle: unsafe { SafeDescriptor::from_raw_descriptor(ret) },
|
event_handle: unsafe { SafeDescriptor::from_raw_descriptor(ret) },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -575,7 +575,6 @@ pub fn max_open_files() -> Result<u64> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Moves the requested PID/TID to a particular cgroup
|
/// Moves the requested PID/TID to a particular cgroup
|
||||||
///
|
|
||||||
pub fn move_to_cgroup(cgroup_path: PathBuf, id_to_write: Pid, cgroup_file: &str) -> Result<()> {
|
pub fn move_to_cgroup(cgroup_path: PathBuf, id_to_write: Pid, cgroup_file: &str) -> Result<()> {
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
|
|
|
@ -83,8 +83,8 @@ pub(in crate::sys) fn sockaddr_un<P: AsRef<Path>>(
|
||||||
|
|
||||||
// Check if the input path is valid. Since
|
// Check if the input path is valid. Since
|
||||||
// * The pathname in sun_path should be null-terminated.
|
// * The pathname in sun_path should be null-terminated.
|
||||||
// * The length of the pathname, including the terminating null byte,
|
// * The length of the pathname, including the terminating null byte, should not exceed the size
|
||||||
// should not exceed the size of sun_path.
|
// of sun_path.
|
||||||
//
|
//
|
||||||
// and our input is a `Path`, we only need to check
|
// and our input is a `Path`, we only need to check
|
||||||
// * If the string size of `Path` should less than sizeof(sun_path)
|
// * If the string size of `Path` should less than sizeof(sun_path)
|
||||||
|
|
|
@ -366,9 +366,14 @@ fn parse_ctrl_group_name_and_id(
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `nl_attr_area` - Nested attributes area (CTRL_ATTR_MCAST_GROUPS data), where nl_attr's
|
/// * `nl_attr_area`
|
||||||
/// corresponding to specific groups are embed
|
///
|
||||||
/// * `group_name` - String with group_name for which we are looking group_id
|
/// Nested attributes area (CTRL_ATTR_MCAST_GROUPS data), where nl_attr's corresponding to
|
||||||
|
/// specific groups are embed
|
||||||
|
///
|
||||||
|
/// * `group_name`
|
||||||
|
///
|
||||||
|
/// String with group_name for which we are looking group_id
|
||||||
///
|
///
|
||||||
/// the CTRL_ATTR_MCAST_GROUPS data has nested attributes. Each of nested attribute is per
|
/// the CTRL_ATTR_MCAST_GROUPS data has nested attributes. Each of nested attribute is per
|
||||||
/// multicast group attributes, which have another nested attributes: CTRL_ATTR_MCAST_GRP_NAME and
|
/// multicast group attributes, which have another nested attributes: CTRL_ATTR_MCAST_GRP_NAME and
|
||||||
|
@ -473,7 +478,6 @@ impl NetlinkGenericRead {
|
||||||
/// ...
|
/// ...
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
///
|
|
||||||
pub fn get_multicast_group_id(&self, group_name: String) -> Option<u32> {
|
pub fn get_multicast_group_id(&self, group_name: String) -> Option<u32> {
|
||||||
for netlink_msg in self.iter() {
|
for netlink_msg in self.iter() {
|
||||||
debug_pr!(
|
debug_pr!(
|
||||||
|
|
|
@ -132,8 +132,8 @@ pub(in crate::sys) fn sockaddr_un<P: AsRef<Path>>(
|
||||||
|
|
||||||
// Check if the input path is valid. Since
|
// Check if the input path is valid. Since
|
||||||
// * The pathname in sun_path should be null-terminated.
|
// * The pathname in sun_path should be null-terminated.
|
||||||
// * The length of the pathname, including the terminating null byte,
|
// * The length of the pathname, including the terminating null byte, should not exceed the size
|
||||||
// should not exceed the size of sun_path.
|
// of sun_path.
|
||||||
//
|
//
|
||||||
// and our input is a `Path`, we only need to check
|
// and our input is a `Path`, we only need to check
|
||||||
// * If the string size of `Path` should less than sizeof(sun_path)
|
// * If the string size of `Path` should less than sizeof(sun_path)
|
||||||
|
|
|
@ -13,8 +13,8 @@ use std::os::unix::net::UnixStream;
|
||||||
use crate::FileReadWriteAtVolatile;
|
use crate::FileReadWriteAtVolatile;
|
||||||
use crate::FileReadWriteVolatile;
|
use crate::FileReadWriteVolatile;
|
||||||
|
|
||||||
// This module allows the below macros to refer to $crate::unix::file_traits::lib::X and ensures other
|
// This module allows the below macros to refer to $crate::unix::file_traits::lib::X and ensures
|
||||||
// crates don't need to add additional crates to their Cargo.toml.
|
// other crates don't need to add additional crates to their Cargo.toml.
|
||||||
pub mod lib {
|
pub mod lib {
|
||||||
pub use libc::c_int;
|
pub use libc::c_int;
|
||||||
pub use libc::c_void;
|
pub use libc::c_void;
|
||||||
|
|
|
@ -33,8 +33,10 @@ impl<T> InterruptibleResult for io::Result<T> {
|
||||||
///
|
///
|
||||||
/// The given expression `$x` can return
|
/// The given expression `$x` can return
|
||||||
///
|
///
|
||||||
/// * `crate::linux::Result` in which case the expression is retried if the `Error::errno()` is `EINTR`.
|
/// * `crate::linux::Result` in which case the expression is retried if the `Error::errno()` is
|
||||||
/// * `std::io::Result` in which case the expression is retried if the `ErrorKind` is `ErrorKind::Interrupted`.
|
/// `EINTR`.
|
||||||
|
/// * `std::io::Result` in which case the expression is retried if the `ErrorKind` is
|
||||||
|
/// `ErrorKind::Interrupted`.
|
||||||
///
|
///
|
||||||
/// Note that if expression returns i32 (i.e. either -1 or error code), then handle_eintr_errno()
|
/// Note that if expression returns i32 (i.e. either -1 or error code), then handle_eintr_errno()
|
||||||
/// or handle_eintr_rc() should be used instead.
|
/// or handle_eintr_rc() should be used instead.
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
//! The mmap module provides a safe interface to map memory and ensures UnmapViewOfFile is called when the
|
//! The mmap module provides a safe interface to map memory and ensures UnmapViewOfFile is called
|
||||||
//! mmap object leaves scope.
|
//! when the mmap object leaves scope.
|
||||||
|
|
||||||
use libc::c_void;
|
use libc::c_void;
|
||||||
use win_util::get_high_order;
|
use win_util::get_high_order;
|
||||||
|
|
|
@ -68,14 +68,14 @@ use crate::WaitContext;
|
||||||
///
|
///
|
||||||
/// The general rule is this should be *at least* as big as the largest message, otherwise
|
/// The general rule is this should be *at least* as big as the largest message, otherwise
|
||||||
/// unexpected blocking behavior can result; for example, if too small, this can interact badly with
|
/// unexpected blocking behavior can result; for example, if too small, this can interact badly with
|
||||||
/// crate::windows::StreamChannel, which expects to be able to make a complete write before releasing
|
/// crate::windows::StreamChannel, which expects to be able to make a complete write before
|
||||||
/// a lock that the opposite side needs to complete a read. This means that if the buffer is too
|
/// releasing a lock that the opposite side needs to complete a read. This means that if the buffer
|
||||||
/// small:
|
/// is too small:
|
||||||
/// * The writer can't complete its write and release the lock because the buffer is too small.
|
/// * The writer can't complete its write and release the lock because the buffer is too small.
|
||||||
/// * The reader can't start reading because the lock is held by the writer, so it can't
|
/// * The reader can't start reading because the lock is held by the writer, so it can't relieve
|
||||||
/// relieve buffer pressure. Note that for message pipes, the reader couldn't do anything
|
/// buffer pressure. Note that for message pipes, the reader couldn't do anything to help
|
||||||
/// to help anyway, because a message mode pipe should NOT have a partial read (which is
|
/// anyway, because a message mode pipe should NOT have a partial read (which is what we would
|
||||||
/// what we would need to relieve pressure).
|
/// need to relieve pressure).
|
||||||
/// * Conditions for deadlock are met, and both the reader & writer enter circular waiting.
|
/// * Conditions for deadlock are met, and both the reader & writer enter circular waiting.
|
||||||
pub const DEFAULT_BUFFER_SIZE: usize = 50 * 1024;
|
pub const DEFAULT_BUFFER_SIZE: usize = 50 * 1024;
|
||||||
|
|
||||||
|
@ -294,17 +294,15 @@ pub fn pair(
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `framing_mode` - Whether the system should provide a simple byte stream (Byte) or an
|
/// * `framing_mode` - Whether the system should provide a simple byte stream (Byte) or an
|
||||||
/// automatically framed sequence of messages (Message). In message mode it's an
|
/// automatically framed sequence of messages (Message). In message mode it's an error to read
|
||||||
/// error to read fewer bytes than were sent in a message from the other end of
|
/// fewer bytes than were sent in a message from the other end of the pipe.
|
||||||
/// the pipe.
|
|
||||||
/// * `blocking_mode` - Whether the system should wait on read() until data is available (Wait) or
|
/// * `blocking_mode` - Whether the system should wait on read() until data is available (Wait) or
|
||||||
/// return immediately if there is nothing available (NoWait).
|
/// return immediately if there is nothing available (NoWait).
|
||||||
/// * `timeout` - A timeout to apply for socket operations, in milliseconds.
|
/// * `timeout` - A timeout to apply for socket operations, in milliseconds. Setting this to
|
||||||
/// Setting this to zero will create sockets with the system
|
/// zero will create sockets with the system default timeout.
|
||||||
/// default timeout.
|
|
||||||
/// * `buffer_size` - The default buffer size for the named pipe. The system should expand the
|
/// * `buffer_size` - The default buffer size for the named pipe. The system should expand the
|
||||||
/// buffer automatically as needed, except in the case of NOWAIT pipes, where
|
/// buffer automatically as needed, except in the case of NOWAIT pipes, where it will just fail
|
||||||
/// it will just fail writes that don't fit in the buffer.
|
/// writes that don't fit in the buffer.
|
||||||
/// # Return value
|
/// # Return value
|
||||||
///
|
///
|
||||||
/// Returns a pair of pipes, of the form (server, client). Note that for some winapis, such as
|
/// Returns a pair of pipes, of the form (server, client). Note that for some winapis, such as
|
||||||
|
@ -354,19 +352,17 @@ pub fn pair_with_buffer_size(
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `pipe_name` - The path of the named pipe to create. Should be in the form
|
/// * `pipe_name` - The path of the named pipe to create. Should be in the form
|
||||||
/// `\\.\pipe\<some-name>`.
|
/// `\\.\pipe\<some-name>`.
|
||||||
/// * `framing_mode` - Whether the system should provide a simple byte stream (Byte) or an
|
/// * `framing_mode` - Whether the system should provide a simple byte stream (Byte) or an
|
||||||
/// automatically framed sequence of messages (Message). In message mode it's an
|
/// automatically framed sequence of messages (Message). In message mode it's an error to read
|
||||||
/// error to read fewer bytes than were sent in a message from the other end of
|
/// fewer bytes than were sent in a message from the other end of the pipe.
|
||||||
/// the pipe.
|
|
||||||
/// * `blocking_mode` - Whether the system should wait on read() until data is available (Wait) or
|
/// * `blocking_mode` - Whether the system should wait on read() until data is available (Wait) or
|
||||||
/// return immediately if there is nothing available (NoWait).
|
/// return immediately if there is nothing available (NoWait).
|
||||||
/// * `timeout` - A timeout to apply for socket operations, in milliseconds.
|
/// * `timeout` - A timeout to apply for socket operations, in milliseconds. Setting this to
|
||||||
/// Setting this to zero will create sockets with the system
|
/// zero will create sockets with the system default timeout.
|
||||||
/// default timeout.
|
|
||||||
/// * `buffer_size` - The default buffer size for the named pipe. The system should expand the
|
/// * `buffer_size` - The default buffer size for the named pipe. The system should expand the
|
||||||
/// buffer automatically as needed, except in the case of NOWAIT pipes, where
|
/// buffer automatically as needed, except in the case of NOWAIT pipes, where it will just fail
|
||||||
/// it will just fail writes that don't fit in the buffer.
|
/// writes that don't fit in the buffer.
|
||||||
/// * `overlapped` - Sets whether overlapped mode is set on the pipe.
|
/// * `overlapped` - Sets whether overlapped mode is set on the pipe.
|
||||||
pub fn create_server_pipe(
|
pub fn create_server_pipe(
|
||||||
pipe_name: &str,
|
pipe_name: &str,
|
||||||
|
@ -433,13 +429,12 @@ pub fn create_server_pipe(
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `pipe_name` - The path of the named pipe to create. Should be in the form
|
/// * `pipe_name` - The path of the named pipe to create. Should be in the form
|
||||||
/// `\\.\pipe\<some-name>`.
|
/// `\\.\pipe\<some-name>`.
|
||||||
/// * `framing_mode` - Whether the system should provide a simple byte stream (Byte) or an
|
/// * `framing_mode` - Whether the system should provide a simple byte stream (Byte) or an
|
||||||
/// automatically framed sequence of messages (Message). In message mode it's an
|
/// automatically framed sequence of messages (Message). In message mode it's an error to read
|
||||||
/// error to read fewer bytes than were sent in a message from the other end of
|
/// fewer bytes than were sent in a message from the other end of the pipe.
|
||||||
/// the pipe.
|
|
||||||
/// * `blocking_mode` - Whether the system should wait on read() until data is available (Wait) or
|
/// * `blocking_mode` - Whether the system should wait on read() until data is available (Wait) or
|
||||||
/// return immediately if there is nothing available (NoWait).
|
/// return immediately if there is nothing available (NoWait).
|
||||||
/// * `overlapped` - Sets whether the pipe is opened in overlapped mode.
|
/// * `overlapped` - Sets whether the pipe is opened in overlapped mode.
|
||||||
pub fn create_client_pipe(
|
pub fn create_client_pipe(
|
||||||
pipe_name: &str,
|
pipe_name: &str,
|
||||||
|
@ -711,9 +706,9 @@ impl PipeConnection {
|
||||||
/// (can be created with `OverlappedWrapper::new`) will be passed into
|
/// (can be created with `OverlappedWrapper::new`) will be passed into
|
||||||
/// `WriteFile`. That event will be triggered when the write operation is complete.
|
/// `WriteFile`. That event will be triggered when the write operation is complete.
|
||||||
///
|
///
|
||||||
/// In order to get how many bytes were written, call `get_overlapped_result`. That function will
|
/// In order to get how many bytes were written, call `get_overlapped_result`. That function
|
||||||
/// also help with waiting until the write operation is complete. The pipe must be opened in
|
/// will also help with waiting until the write operation is complete. The pipe must be
|
||||||
/// overlapped otherwise there may be unexpected behavior.
|
/// opened in overlapped otherwise there may be unexpected behavior.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// * buf & overlapped_wrapper MUST live until the overlapped operation is complete.
|
/// * buf & overlapped_wrapper MUST live until the overlapped operation is complete.
|
||||||
|
@ -1070,8 +1065,8 @@ pub struct NamedPipeInfo {
|
||||||
/// we ensure that the variable size message is written/read right after writing/reading
|
/// we ensure that the variable size message is written/read right after writing/reading
|
||||||
/// fixed size header. For example it avoid sending or receiving in messages in order like
|
/// fixed size header. For example it avoid sending or receiving in messages in order like
|
||||||
/// H1, H2, M1, M2
|
/// H1, H2, M1, M2
|
||||||
/// - where header H1 and its message M1 are sent by one event loop and H2 and its
|
/// - where header H1 and its message M1 are sent by one event loop and H2 and its message M2 are
|
||||||
/// message M2 are sent by another event loop.
|
/// sent by another event loop.
|
||||||
///
|
///
|
||||||
/// Do not expose direct access to reader or writer pipes.
|
/// Do not expose direct access to reader or writer pipes.
|
||||||
///
|
///
|
||||||
|
|
|
@ -156,10 +156,12 @@ pub fn set_time_period(res: Duration, begin: bool) -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret = if begin {
|
let ret = if begin {
|
||||||
// SAFETY: Trivially safe. Note that the casts are safe because we know res is within u32's range.
|
// SAFETY: Trivially safe. Note that the casts are safe because we know res is within u32's
|
||||||
|
// range.
|
||||||
unsafe { timeBeginPeriod(res.as_millis() as u32) }
|
unsafe { timeBeginPeriod(res.as_millis() as u32) }
|
||||||
} else {
|
} else {
|
||||||
// SAFETY: Trivially safe. Note that the casts are safe because we know res is within u32's range.
|
// SAFETY: Trivially safe. Note that the casts are safe because we know res is within u32's
|
||||||
|
// range.
|
||||||
unsafe { timeEndPeriod(res.as_millis() as u32) }
|
unsafe { timeEndPeriod(res.as_millis() as u32) }
|
||||||
};
|
};
|
||||||
if ret != TIMERR_NOERROR {
|
if ret != TIMERR_NOERROR {
|
||||||
|
|
|
@ -21,7 +21,8 @@ pub fn set_audio_thread_priority() -> Result<SafeMultimediaHandle> {
|
||||||
let multimedia_handle = unsafe {
|
let multimedia_handle = unsafe {
|
||||||
let mut task_index: u32 = 0;
|
let mut task_index: u32 = 0;
|
||||||
// "Pro Audio" is defined in:
|
// "Pro Audio" is defined in:
|
||||||
// HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Multimedia\SystemProfile\Tasks\Pro Audio
|
// HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows
|
||||||
|
// NT\CurrentVersion\Multimedia\SystemProfile\Tasks\Pro Audio
|
||||||
let pro_audio = std::ffi::CString::new("Pro Audio").unwrap();
|
let pro_audio = std::ffi::CString::new("Pro Audio").unwrap();
|
||||||
AvSetMmThreadCharacteristicsA(pro_audio.as_ptr(), &mut task_index)
|
AvSetMmThreadCharacteristicsA(pro_audio.as_ptr(), &mut task_index)
|
||||||
};
|
};
|
||||||
|
@ -58,8 +59,8 @@ impl Drop for SafeMultimediaHandle {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// Safe because we `multimedia_handle` is defined in the same thread and is created in the
|
// Safe because we `multimedia_handle` is defined in the same thread and is created in the
|
||||||
// function above. `multimedia_handle` needs be created from `AvSetMmThreadCharacteristicsA`.
|
// function above. `multimedia_handle` needs be created from
|
||||||
// This will also drop the `mulitmedia_handle`.
|
// `AvSetMmThreadCharacteristicsA`. This will also drop the `mulitmedia_handle`.
|
||||||
if unsafe { AvRevertMmThreadCharacteristics(self.multimedia_handle) } == FALSE {
|
if unsafe { AvRevertMmThreadCharacteristics(self.multimedia_handle) } == FALSE {
|
||||||
warn!(
|
warn!(
|
||||||
"Failed to revert audio thread. Error: {}",
|
"Failed to revert audio thread. Error: {}",
|
||||||
|
|
|
@ -219,8 +219,8 @@ impl StreamChannel {
|
||||||
// the notifier though, then we have to be sure, so we'll proceed to the next section.
|
// the notifier though, then we have to be sure, so we'll proceed to the next section.
|
||||||
let byte_count = self.get_readable_byte_count()?;
|
let byte_count = self.get_readable_byte_count()?;
|
||||||
if byte_count > 0 {
|
if byte_count > 0 {
|
||||||
// It's always safe to set the read notifier here because we know there is data in the
|
// It's always safe to set the read notifier here because we know there is data in
|
||||||
// pipe, and no one else could read it out from under us.
|
// the pipe, and no one else could read it out from under us.
|
||||||
self.read_notify.signal().map_err(|e| {
|
self.read_notify.signal().map_err(|e| {
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
|
|
|
@ -45,7 +45,8 @@ pub unsafe trait Terminal {
|
||||||
& !(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
|
& !(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
|
||||||
|
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// Safe because the syscall will only read the extent of mode and we check the return result.
|
// Safe because the syscall will only read the extent of mode and we check the return
|
||||||
|
// result.
|
||||||
if unsafe { SetConsoleMode(descriptor, new_mode) } == 0 {
|
if unsafe { SetConsoleMode(descriptor, new_mode) } == 0 {
|
||||||
return Err(Error::last());
|
return Err(Error::last());
|
||||||
}
|
}
|
||||||
|
@ -56,7 +57,8 @@ pub unsafe trait Terminal {
|
||||||
/// Set this terminal's mode to a previous state returned by `set_raw_mode()`.
|
/// Set this terminal's mode to a previous state returned by `set_raw_mode()`.
|
||||||
fn restore_mode(&self, mode: DWORD) -> Result<()> {
|
fn restore_mode(&self, mode: DWORD) -> Result<()> {
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// Safe because the syscall will only read the extent of mode and we check the return result.
|
// Safe because the syscall will only read the extent of mode and we check the return
|
||||||
|
// result.
|
||||||
if unsafe { SetConsoleMode(self.terminal_descriptor(), mode) } == 0 {
|
if unsafe { SetConsoleMode(self.terminal_descriptor(), mode) } == 0 {
|
||||||
Err(Error::last())
|
Err(Error::last())
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -275,9 +275,9 @@ fn duplicate_handle(desc: RawHandle, target_pid: Option<u32>) -> Result<RawHandl
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads a part of a Tube packet asserting that it was correctly read. This means:
|
/// Reads a part of a Tube packet asserting that it was correctly read. This means:
|
||||||
/// * Treats partial "message" (transport framing) reads are Ok, as long as we filled our buffer.
|
/// * Treats partial "message" (transport framing) reads are Ok, as long as we filled our buffer. We
|
||||||
/// We use this to ignore errors when reading the message header, which has the lengths we need
|
/// use this to ignore errors when reading the message header, which has the lengths we need to
|
||||||
/// to allocate our buffers for the remainder of the message.
|
/// allocate our buffers for the remainder of the message.
|
||||||
/// * We filled the supplied buffer.
|
/// * We filled the supplied buffer.
|
||||||
fn perform_read<F: FnMut(&mut [u8]) -> io::Result<usize>>(
|
fn perform_read<F: FnMut(&mut [u8]) -> io::Result<usize>>(
|
||||||
read_fn: &mut F,
|
read_fn: &mut F,
|
||||||
|
|
|
@ -215,24 +215,26 @@ impl<T: EventToken> EventContext<T> {
|
||||||
WAIT_OBJECT_0..=MAXIMUM_WAIT_OBJECTS_U32 => {
|
WAIT_OBJECT_0..=MAXIMUM_WAIT_OBJECTS_U32 => {
|
||||||
let mut event_index = (result - WAIT_OBJECT_0) as usize;
|
let mut event_index = (result - WAIT_OBJECT_0) as usize;
|
||||||
if event_index >= handles_len {
|
if event_index >= handles_len {
|
||||||
// This is not a valid index and should return an error. This case should not be possible
|
// This is not a valid index and should return an error. This case should not be
|
||||||
// and will likely not return a meaningful system error code, but is still an invalid case.
|
// possible and will likely not return a meaningful system
|
||||||
|
// error code, but is still an invalid case.
|
||||||
error!("Wait returned index out of range");
|
error!("Wait returned index out of range");
|
||||||
return errno_result();
|
return errno_result();
|
||||||
}
|
}
|
||||||
if event_index == 0 {
|
if event_index == 0 {
|
||||||
// The handles list has been modified and triggered the wait, try again with the updated
|
// The handles list has been modified and triggered the wait, try again with the
|
||||||
// handles list. Note it is possible the list was modified again after the wait which will
|
// updated handles list. Note it is possible the list was
|
||||||
// trigger the handles_modified_event again, but that will only err towards the safe side
|
// modified again after the wait which will trigger the
|
||||||
|
// handles_modified_event again, but that will only err towards the safe side
|
||||||
// of recursing an extra time.
|
// of recursing an extra time.
|
||||||
let _ = self.handles_modified_event.wait();
|
let _ = self.handles_modified_event.wait();
|
||||||
return self.wait_timeout(timeout);
|
return self.wait_timeout(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut events_to_return = SmallVec::<[TriggeredEvent<T>; 16]>::new();
|
let mut events_to_return = SmallVec::<[TriggeredEvent<T>; 16]>::new();
|
||||||
// Multiple events may be triggered at once, but WaitForMultipleObjects will only return one.
|
// Multiple events may be triggered at once, but WaitForMultipleObjects will only
|
||||||
// Once it returns, loop through the remaining triggers checking each to ensure they haven't
|
// return one. Once it returns, loop through the remaining triggers
|
||||||
// also been triggered.
|
// checking each to ensure they haven't also been triggered.
|
||||||
let mut handles_offset: usize = 0;
|
let mut handles_offset: usize = 0;
|
||||||
loop {
|
loop {
|
||||||
let event_to_return = raw_handles_list[event_index + handles_offset];
|
let event_to_return = raw_handles_list[event_index + handles_offset];
|
||||||
|
@ -270,8 +272,9 @@ impl<T: EventToken> EventContext<T> {
|
||||||
) as usize;
|
) as usize;
|
||||||
|
|
||||||
if event_index >= (handles_len - handles_offset) {
|
if event_index >= (handles_len - handles_offset) {
|
||||||
// This indicates a failure condition, as return values greater than the length
|
// This indicates a failure condition, as return values greater than the
|
||||||
// of the provided array are reserved for failures.
|
// length of the provided array are reserved for
|
||||||
|
// failures.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,8 +42,6 @@
|
||||||
//!
|
//!
|
||||||
//! init_with(cfg).unwrap();
|
//! init_with(cfg).unwrap();
|
||||||
//! error!("something went horribly wrong: {}", "out of RAMs");
|
//! error!("something went horribly wrong: {}", "out of RAMs");
|
||||||
//!
|
|
||||||
//!
|
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//!
|
//!
|
||||||
|
@ -372,7 +370,7 @@ pub fn init() -> Result<(), Error> {
|
||||||
///
|
///
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// * filter: See <https://docs.rs/env_logger/0.9/env_logger/index.html> for example filter
|
/// * filter: See <https://docs.rs/env_logger/0.9/env_logger/index.html> for example filter
|
||||||
/// specifications
|
/// specifications
|
||||||
/// * stderr: If set will output to stderr (in addition)
|
/// * stderr: If set will output to stderr (in addition)
|
||||||
/// * file: If set will output to this file (in addition)
|
/// * file: If set will output to this file (in addition)
|
||||||
/// * proc_name: proc name for Syslog implementation
|
/// * proc_name: proc name for Syslog implementation
|
||||||
|
|
|
@ -116,7 +116,6 @@
|
||||||
//! self.0 << 4
|
//! self.0 << 4
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//!
|
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Finally, fields may be of user-defined enum types. The enum must satisfy one of the following
|
//! Finally, fields may be of user-defined enum types. The enum must satisfy one of the following
|
||||||
|
|
|
@ -183,8 +183,8 @@ pub trait ShmStreamSource<E: std::error::Error>: Send {
|
||||||
/// Creates a new [`ShmStream`](ShmStream)
|
/// Creates a new [`ShmStream`](ShmStream)
|
||||||
///
|
///
|
||||||
/// Creates a new `ShmStream` object, which allows:
|
/// Creates a new `ShmStream` object, which allows:
|
||||||
/// * Waiting until the server has communicated that data is ready or
|
/// * Waiting until the server has communicated that data is ready or requested that we make
|
||||||
/// requested that we make more data available.
|
/// more data available.
|
||||||
/// * Setting the location and length of buffers for reading/writing audio data.
|
/// * Setting the location and length of buffers for reading/writing audio data.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
|
@ -193,15 +193,13 @@ pub trait ShmStreamSource<E: std::error::Error>: Send {
|
||||||
/// * `num_channels` - The number of audio channels for the stream.
|
/// * `num_channels` - The number of audio channels for the stream.
|
||||||
/// * `format` - The audio format to use for audio samples.
|
/// * `format` - The audio format to use for audio samples.
|
||||||
/// * `frame_rate` - The stream's frame rate in Hz.
|
/// * `frame_rate` - The stream's frame rate in Hz.
|
||||||
/// * `buffer_size` - The maximum size of an audio buffer. This will be the
|
/// * `buffer_size` - The maximum size of an audio buffer. This will be the size used for
|
||||||
/// size used for transfers of audio data between client
|
/// transfers of audio data between client and server.
|
||||||
/// and server.
|
|
||||||
/// * `effects` - Audio effects to use for the stream, such as echo-cancellation.
|
/// * `effects` - Audio effects to use for the stream, such as echo-cancellation.
|
||||||
/// * `client_shm` - The shared memory area that will contain samples.
|
/// * `client_shm` - The shared memory area that will contain samples.
|
||||||
/// * `buffer_offsets` - The two initial values to use as buffer offsets
|
/// * `buffer_offsets` - The two initial values to use as buffer offsets for streams. This way,
|
||||||
/// for streams. This way, the server will not write
|
/// the server will not write audio data to an arbitrary offset in `client_shm` if the client
|
||||||
/// audio data to an arbitrary offset in `client_shm`
|
/// fails to update offsets in time.
|
||||||
/// if the client fails to update offsets in time.
|
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
|
|
|
@ -10,16 +10,15 @@
|
||||||
//! return a PoisonError. This API codifies our error handling strategy around
|
//! return a PoisonError. This API codifies our error handling strategy around
|
||||||
//! poisoned mutexes in crosvm.
|
//! poisoned mutexes in crosvm.
|
||||||
//!
|
//!
|
||||||
//! - Crosvm releases are built with panic=abort so poisoning never occurs. A
|
//! - Crosvm releases are built with panic=abort so poisoning never occurs. A panic while a mutex is
|
||||||
//! panic while a mutex is held (or ever) takes down the entire process. Thus
|
//! held (or ever) takes down the entire process. Thus we would like for code not to have to
|
||||||
//! we would like for code not to have to consider the possibility of poison.
|
//! consider the possibility of poison.
|
||||||
//!
|
//!
|
||||||
//! - We could ask developers to always write `.lock().unwrap()` on a standard
|
//! - We could ask developers to always write `.lock().unwrap()` on a standard library mutex.
|
||||||
//! library mutex. However, we would like to stigmatize the use of unwrap. It
|
//! However, we would like to stigmatize the use of unwrap. It is confusing to permit unwrap but
|
||||||
//! is confusing to permit unwrap but only on mutex lock results. During code
|
//! only on mutex lock results. During code review it may not always be obvious whether a
|
||||||
//! review it may not always be obvious whether a particular unwrap is
|
//! particular unwrap is unwrapping a mutex lock result or a different error that should be
|
||||||
//! unwrapping a mutex lock result or a different error that should be handled
|
//! handled in a more principled way.
|
||||||
//! in a more principled way.
|
|
||||||
//!
|
//!
|
||||||
//! Developers should feel free to use sync::Mutex anywhere in crosvm that they
|
//! Developers should feel free to use sync::Mutex anywhere in crosvm that they
|
||||||
//! would otherwise be using std::sync::Mutex.
|
//! would otherwise be using std::sync::Mutex.
|
||||||
|
|
|
@ -22,8 +22,7 @@ use crate::BlockingPool;
|
||||||
/// This is convenient, though not preferred. Pros/cons:
|
/// This is convenient, though not preferred. Pros/cons:
|
||||||
/// + It avoids passing executor all the way to each call sites.
|
/// + It avoids passing executor all the way to each call sites.
|
||||||
/// + The call site can assume that executor will never shutdown.
|
/// + The call site can assume that executor will never shutdown.
|
||||||
/// + Provides similar functionality as async_task with a few improvements
|
/// + Provides similar functionality as async_task with a few improvements around ability to cancel.
|
||||||
/// around ability to cancel.
|
|
||||||
/// - Globals are harder to reason about.
|
/// - Globals are harder to reason about.
|
||||||
static EXECUTOR: Lazy<CancellableBlockingPool> =
|
static EXECUTOR: Lazy<CancellableBlockingPool> =
|
||||||
Lazy::new(|| CancellableBlockingPool::new(256, Duration::from_secs(10)));
|
Lazy::new(|| CancellableBlockingPool::new(256, Duration::from_secs(10)));
|
||||||
|
@ -279,7 +278,6 @@ impl CancellableBlockingPool {
|
||||||
/// This will block until all work that has been started by the worker threads is finished. Any
|
/// This will block until all work that has been started by the worker threads is finished. Any
|
||||||
/// work that was added to the `CancellableBlockingPool` but not yet picked up by a worker
|
/// work that was added to the `CancellableBlockingPool` but not yet picked up by a worker
|
||||||
/// thread will not complete and `await`ing on the `Task` for that work will panic.
|
/// thread will not complete and `await`ing on the `Task` for that work will panic.
|
||||||
///
|
|
||||||
pub fn shutdown(&self) -> Result<(), Error> {
|
pub fn shutdown(&self) -> Result<(), Error> {
|
||||||
self.shutdown_with_timeout(DEFAULT_SHUTDOWN_TIMEOUT)
|
self.shutdown_with_timeout(DEFAULT_SHUTDOWN_TIMEOUT)
|
||||||
}
|
}
|
||||||
|
|
|
@ -370,8 +370,8 @@ impl Condvar {
|
||||||
|
|
||||||
let set_on_release = if waiters.is_empty() {
|
let set_on_release = if waiters.is_empty() {
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// Clear the rwlock associated with this Condvar since there are no longer any waiters. Safe
|
// Clear the rwlock associated with this Condvar since there are no longer any waiters.
|
||||||
// because we the spin lock guarantees exclusive access.
|
// Safe because we the spin lock guarantees exclusive access.
|
||||||
unsafe { *self.mu.get() = 0 };
|
unsafe { *self.mu.get() = 0 };
|
||||||
|
|
||||||
0
|
0
|
||||||
|
|
|
@ -102,8 +102,8 @@ enum OpStatus {
|
||||||
WakeEvent,
|
WakeEvent,
|
||||||
}
|
}
|
||||||
|
|
||||||
// An IO source previously registered with an EpollReactor. Used to initiate asynchronous IO with the
|
// An IO source previously registered with an EpollReactor. Used to initiate asynchronous IO with
|
||||||
// associated executor.
|
// the associated executor.
|
||||||
pub struct RegisteredSource<F> {
|
pub struct RegisteredSource<F> {
|
||||||
pub(crate) source: F,
|
pub(crate) source: F,
|
||||||
ex: Weak<RawExecutor<EpollReactor>>,
|
ex: Weak<RawExecutor<EpollReactor>>,
|
||||||
|
|
|
@ -159,8 +159,9 @@ impl<F: AsRawDescriptor> PollSource<F> {
|
||||||
loop {
|
loop {
|
||||||
let res = if let Some(offset) = file_offset {
|
let res = if let Some(offset) = file_offset {
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// Safe because we trust the kernel not to write path the length given and the length is
|
// Safe because we trust the kernel not to write path the length given and the
|
||||||
// guaranteed to be valid from the pointer by io_slice_mut.
|
// length is guaranteed to be valid from the pointer by
|
||||||
|
// io_slice_mut.
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::preadv64(
|
libc::preadv64(
|
||||||
self.registered_source.duped_fd.as_raw_fd(),
|
self.registered_source.duped_fd.as_raw_fd(),
|
||||||
|
@ -171,8 +172,9 @@ impl<F: AsRawDescriptor> PollSource<F> {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// Safe because we trust the kernel not to write path the length given and the length is
|
// Safe because we trust the kernel not to write path the length given and the
|
||||||
// guaranteed to be valid from the pointer by io_slice_mut.
|
// length is guaranteed to be valid from the pointer by
|
||||||
|
// io_slice_mut.
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::readv(
|
libc::readv(
|
||||||
self.registered_source.duped_fd.as_raw_fd(),
|
self.registered_source.duped_fd.as_raw_fd(),
|
||||||
|
@ -272,8 +274,9 @@ impl<F: AsRawDescriptor> PollSource<F> {
|
||||||
loop {
|
loop {
|
||||||
let res = if let Some(offset) = file_offset {
|
let res = if let Some(offset) = file_offset {
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// Safe because we trust the kernel not to write path the length given and the length is
|
// Safe because we trust the kernel not to write path the length given and the
|
||||||
// guaranteed to be valid from the pointer by io_slice_mut.
|
// length is guaranteed to be valid from the pointer by
|
||||||
|
// io_slice_mut.
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::pwritev64(
|
libc::pwritev64(
|
||||||
self.registered_source.duped_fd.as_raw_fd(),
|
self.registered_source.duped_fd.as_raw_fd(),
|
||||||
|
@ -284,8 +287,9 @@ impl<F: AsRawDescriptor> PollSource<F> {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// Safe because we trust the kernel not to write path the length given and the length is
|
// Safe because we trust the kernel not to write path the length given and the
|
||||||
// guaranteed to be valid from the pointer by io_slice_mut.
|
// length is guaranteed to be valid from the pointer by
|
||||||
|
// io_slice_mut.
|
||||||
unsafe {
|
unsafe {
|
||||||
libc::writev(
|
libc::writev(
|
||||||
self.registered_source.duped_fd.as_raw_fd(),
|
self.registered_source.duped_fd.as_raw_fd(),
|
||||||
|
|
|
@ -30,8 +30,8 @@
|
||||||
//! What if the kernel's reference to the buffer outlives the buffer itself? This could happen if a
|
//! What if the kernel's reference to the buffer outlives the buffer itself? This could happen if a
|
||||||
//! read operation was submitted, then the memory is dropped. To solve this, the executor takes an
|
//! read operation was submitted, then the memory is dropped. To solve this, the executor takes an
|
||||||
//! Arc to the backing memory. Vecs being read to are also wrapped in an Arc before being passed to
|
//! Arc to the backing memory. Vecs being read to are also wrapped in an Arc before being passed to
|
||||||
//! the executor. The executor holds the Arc and ensures all operations are complete before dropping
|
//! the executor. The executor holds the Arc and ensures all operations are complete before
|
||||||
//! it, that guarantees the memory is valid for the duration.
|
//! dropping it, that guarantees the memory is valid for the duration.
|
||||||
//!
|
//!
|
||||||
//! The buffers _have_ to be on the heap. Because we don't have a way to cancel a future if it is
|
//! The buffers _have_ to be on the heap. Because we don't have a way to cancel a future if it is
|
||||||
//! dropped(can't rely on drop running), there is no way to ensure the kernel's buffer remains valid
|
//! dropped(can't rely on drop running), there is no way to ensure the kernel's buffer remains valid
|
||||||
|
@ -936,8 +936,8 @@ mod tests {
|
||||||
.register_source(&ex, &rx)
|
.register_source(&ex, &rx)
|
||||||
.expect("register source failed");
|
.expect("register source failed");
|
||||||
|
|
||||||
// Submit the op to the kernel. Next, test that the source keeps its Arc open for the duration
|
// Submit the op to the kernel. Next, test that the source keeps its Arc open for the
|
||||||
// of the op.
|
// duration of the op.
|
||||||
let pending_op = registered_source
|
let pending_op = registered_source
|
||||||
.start_read_to_mem(None, Arc::clone(&bm), [MemRegion { offset: 0, len: 8 }])
|
.start_read_to_mem(None, Arc::clone(&bm), [MemRegion { offset: 0, len: 8 }])
|
||||||
.expect("failed to start read to mem");
|
.expect("failed to start read to mem");
|
||||||
|
@ -983,8 +983,8 @@ mod tests {
|
||||||
.register_source(&ex, &tx)
|
.register_source(&ex, &tx)
|
||||||
.expect("register source failed");
|
.expect("register source failed");
|
||||||
|
|
||||||
// Submit the op to the kernel. Next, test that the source keeps its Arc open for the duration
|
// Submit the op to the kernel. Next, test that the source keeps its Arc open for the
|
||||||
// of the op.
|
// duration of the op.
|
||||||
let pending_op = registered_source
|
let pending_op = registered_source
|
||||||
.start_write_from_mem(None, Arc::clone(&bm), [MemRegion { offset: 0, len: 8 }])
|
.start_write_from_mem(None, Arc::clone(&bm), [MemRegion { offset: 0, len: 8 }])
|
||||||
.expect("failed to start write to mem");
|
.expect("failed to start write to mem");
|
||||||
|
|
|
@ -39,11 +39,12 @@ impl EventAsync {
|
||||||
Self::new_without_reset(
|
Self::new_without_reset(
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// Safe because:
|
// Safe because:
|
||||||
// a) the underlying Event should be validated by the caller.
|
// * the underlying Event should be validated by the caller.
|
||||||
// b) we do NOT take ownership of the underlying Event. If we did that would cause an early
|
// * we do NOT take ownership of the underlying Event. If we did that would cause an
|
||||||
// free (and later a double free @ the end of this scope). This is why we have to wrap
|
// early free (and later a double free @ the end of this scope). This is why we have
|
||||||
// it in ManuallyDrop.
|
// to wrap it in ManuallyDrop.
|
||||||
// c) we own the clone that is produced exclusively, so it is safe to take ownership of it.
|
// * we own the clone that is produced exclusively, so it is safe to take ownership of
|
||||||
|
// it.
|
||||||
unsafe {
|
unsafe {
|
||||||
ManuallyDrop::new(Event::from_raw_descriptor(descriptor.as_raw_descriptor()))
|
ManuallyDrop::new(Event::from_raw_descriptor(descriptor.as_raw_descriptor()))
|
||||||
}
|
}
|
||||||
|
|
|
@ -259,8 +259,8 @@ impl WeakWake for HandleReactor {
|
||||||
/// 1. The reactor in use is a HandleReactor.
|
/// 1. The reactor in use is a HandleReactor.
|
||||||
/// 2. Immediately after the IO syscall, this future MUST be awaited. We rely on the fact that
|
/// 2. Immediately after the IO syscall, this future MUST be awaited. We rely on the fact that
|
||||||
/// the executor cannot poll the IOCP before this future is polled for the first time to
|
/// the executor cannot poll the IOCP before this future is polled for the first time to
|
||||||
/// ensure the waker has been registered. (If the executor polls the IOCP before the waker
|
/// ensure the waker has been registered. (If the executor polls the IOCP before the waker is
|
||||||
/// is registered, the future will stall.)
|
/// registered, the future will stall.)
|
||||||
pub(crate) struct OverlappedOperation {
|
pub(crate) struct OverlappedOperation {
|
||||||
overlapped: BoxedOverlapped,
|
overlapped: BoxedOverlapped,
|
||||||
ex: Weak<RawExecutor<HandleReactor>>,
|
ex: Weak<RawExecutor<HandleReactor>>,
|
||||||
|
|
|
@ -331,8 +331,8 @@ impl<F: AsRawDescriptor> HandleSource<F> {
|
||||||
.spawn(
|
.spawn(
|
||||||
move || {
|
move || {
|
||||||
let mut file = get_thread_file(descriptors);
|
let mut file = get_thread_file(descriptors);
|
||||||
// ZeroRange calls `punch_hole` which doesn't extend the File size if it needs to.
|
// ZeroRange calls `punch_hole` which doesn't extend the File size if it needs
|
||||||
// Will fix if it becomes a problem.
|
// to. Will fix if it becomes a problem.
|
||||||
file.write_zeroes_at(file_offset, len as usize)
|
file.write_zeroes_at(file_offset, len as usize)
|
||||||
.map_err(Error::IoWriteZeroesError)?;
|
.map_err(Error::IoWriteZeroesError)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -203,7 +203,8 @@ impl<F: AsRawDescriptor> OverlappedSource<F> {
|
||||||
.map_err(Error::BackingMemoryVolatileSliceFetchFailed)?;
|
.map_err(Error::BackingMemoryVolatileSliceFetchFailed)?;
|
||||||
|
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// Safe because we're passing a volatile slice (valid ptr), and the size of the memory region it refers to.
|
// Safe because we're passing a volatile slice (valid ptr), and the size of the memory
|
||||||
|
// region it refers to.
|
||||||
unsafe {
|
unsafe {
|
||||||
read(
|
read(
|
||||||
self.source.as_raw_descriptor(),
|
self.source.as_raw_descriptor(),
|
||||||
|
@ -291,7 +292,8 @@ impl<F: AsRawDescriptor> OverlappedSource<F> {
|
||||||
.map_err(Error::BackingMemoryVolatileSliceFetchFailed)?;
|
.map_err(Error::BackingMemoryVolatileSliceFetchFailed)?;
|
||||||
|
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// Safe because we're passing a volatile slice (valid ptr), and the size of the memory region it refers to.
|
// Safe because we're passing a volatile slice (valid ptr), and the size of the memory
|
||||||
|
// region it refers to.
|
||||||
unsafe {
|
unsafe {
|
||||||
write(
|
write(
|
||||||
self.source.as_raw_descriptor(),
|
self.source.as_raw_descriptor(),
|
||||||
|
|
|
@ -168,9 +168,10 @@ where
|
||||||
// Safe because self.descriptor is valid in any state except New or Finished.
|
// Safe because self.descriptor is valid in any state except New or Finished.
|
||||||
//
|
//
|
||||||
// Note: this method call is critical for supplying the safety guarantee relied upon by
|
// Note: this method call is critical for supplying the safety guarantee relied upon by
|
||||||
// wait_for_handle_waker. Upon return, it ensures that wait_for_handle_waker is not running
|
// wait_for_handle_waker. Upon return, it ensures that wait_for_handle_waker is not
|
||||||
// and won't be scheduled again, which makes it safe to drop self.inner_for_callback
|
// running and won't be scheduled again, which makes it safe to drop
|
||||||
// (wait_for_handle_waker has a non owning pointer to self.inner_for_callback).
|
// self.inner_for_callback (wait_for_handle_waker has a non owning pointer
|
||||||
|
// to self.inner_for_callback).
|
||||||
unsafe { unregister_wait(wait_object) }
|
unsafe { unregister_wait(wait_object) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,7 +96,6 @@ pub fn push_descriptors_internal(keep_rds: &mut Vec<RawDescriptor>) {
|
||||||
/// Categories that are enabled will have their events traced at runtime via
|
/// Categories that are enabled will have their events traced at runtime via
|
||||||
/// `trace_event_begin!()`, `trace_event_end!()`, or `trace_event!()` scoped tracing.
|
/// `trace_event_begin!()`, `trace_event_end!()`, or `trace_event!()` scoped tracing.
|
||||||
/// The categories that are marked as false will have their events skipped.
|
/// The categories that are marked as false will have their events skipped.
|
||||||
///
|
|
||||||
macro_rules! setup_trace_marker {
|
macro_rules! setup_trace_marker {
|
||||||
($(($cat:ident, $enabled:literal)),+) => {
|
($(($cat:ident, $enabled:literal)),+) => {
|
||||||
#[allow(non_camel_case_types, missing_docs)]
|
#[allow(non_camel_case_types, missing_docs)]
|
||||||
|
@ -159,7 +158,6 @@ macro_rules! setup_trace_marker {
|
||||||
/// - `$uid: Exit: exec`
|
/// - `$uid: Exit: exec`
|
||||||
///
|
///
|
||||||
/// where `$uid` will be the same unique value across those two events.
|
/// where `$uid` will be the same unique value across those two events.
|
||||||
///
|
|
||||||
macro_rules! trace_event {
|
macro_rules! trace_event {
|
||||||
($category:ident, $name:literal, $($arg:expr),+) => {{
|
($category:ident, $name:literal, $($arg:expr),+) => {{
|
||||||
if($crate::ENABLED_CATEGORIES[$crate::TracedCategories::$category as usize].load(std::sync::atomic::Ordering::Relaxed)) {
|
if($crate::ENABLED_CATEGORIES[$crate::TracedCategories::$category as usize].load(std::sync::atomic::Ordering::Relaxed)) {
|
||||||
|
|
|
@ -427,10 +427,10 @@ use bitmasks::*;
|
||||||
///
|
///
|
||||||
/// This function packs bits in NTSTATUS results (generally what a Windows exit code should be).
|
/// This function packs bits in NTSTATUS results (generally what a Windows exit code should be).
|
||||||
/// There are three primary cases it deals with:
|
/// There are three primary cases it deals with:
|
||||||
/// 1. Vendor specific exits. These are error codes we generate explicitly in crosvm. We will
|
/// 1. Vendor specific exits. These are error codes we generate explicitly in crosvm. We will pack
|
||||||
/// pack these codes with the lower 6 "facility" bits ([21, 16]) set so they can't collide
|
/// these codes with the lower 6 "facility" bits ([21, 16]) set so they can't collide with the
|
||||||
/// with the other cases (this makes our facility value > FACILITY_MAXIMUM_VALUE). The top
|
/// other cases (this makes our facility value > FACILITY_MAXIMUM_VALUE). The top 6 bits of the
|
||||||
/// 6 bits of the facility field ([27, 22]) will be clear at this point.
|
/// facility field ([27, 22]) will be clear at this point.
|
||||||
///
|
///
|
||||||
/// 2. Non vendor NTSTATUS exits. These are error codes which come from Windows. We flip the
|
/// 2. Non vendor NTSTATUS exits. These are error codes which come from Windows. We flip the
|
||||||
/// vendor bit on these because we're going to pack the facility field, and leaving it unset
|
/// vendor bit on these because we're going to pack the facility field, and leaving it unset
|
||||||
|
@ -440,9 +440,8 @@ use bitmasks::*;
|
||||||
/// however, if for some reason we see a non vendor code with any of those bits set, we will
|
/// however, if for some reason we see a non vendor code with any of those bits set, we will
|
||||||
/// fall through to case #3.
|
/// fall through to case #3.
|
||||||
///
|
///
|
||||||
/// 3. Non NTSTATUS errors. We detect these with two heuristics:
|
/// 3. Non NTSTATUS errors. We detect these with two heuristics: a) Reserved field is set. b) The
|
||||||
/// a) Reserved field is set.
|
/// facility field has exceeded the bottom six bits ([21, 16]).
|
||||||
/// b) The facility field has exceeded the bottom six bits ([21, 16]).
|
|
||||||
///
|
///
|
||||||
/// For such cases, we pack as much of the error as we can into the lower 6 bits of the
|
/// For such cases, we pack as much of the error as we can into the lower 6 bits of the
|
||||||
/// facility field, and code field (2 bytes). In this case, the most significant bit of the
|
/// facility field, and code field (2 bytes). In this case, the most significant bit of the
|
||||||
|
|
|
@ -368,14 +368,15 @@ pub extern "C" fn crosvm_client_max_usb_devices() -> usize {
|
||||||
USB_CONTROL_MAX_PORTS
|
USB_CONTROL_MAX_PORTS
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns all USB devices passed through the crosvm instance whose control socket is listening on `socket_path`.
|
/// Returns all USB devices passed through the crosvm instance whose control socket is listening on
|
||||||
|
/// `socket_path`.
|
||||||
///
|
///
|
||||||
/// The function returns the amount of entries written.
|
/// The function returns the amount of entries written.
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `socket_path` - Path to the crosvm control socket
|
/// * `socket_path` - Path to the crosvm control socket
|
||||||
/// * `entries` - Pointer to an array of `UsbDeviceEntry` where the details about the attached
|
/// * `entries` - Pointer to an array of `UsbDeviceEntry` where the details about the attached
|
||||||
/// devices will be written to
|
/// devices will be written to
|
||||||
/// * `entries_length` - Amount of entries in the array specified by `entries`
|
/// * `entries_length` - Amount of entries in the array specified by `entries`
|
||||||
///
|
///
|
||||||
/// Use the value returned by [`crosvm_client_max_usb_devices()`] to determine the size of the input
|
/// Use the value returned by [`crosvm_client_max_usb_devices()`] to determine the size of the input
|
||||||
|
@ -863,7 +864,8 @@ pub struct BalloonWSRConfigFfi {
|
||||||
report_threshold: u64,
|
report_threshold: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns balloon working set of the crosvm instance whose control socket is listening on socket_path.
|
/// Returns balloon working set of the crosvm instance whose control socket is listening on
|
||||||
|
/// socket_path.
|
||||||
///
|
///
|
||||||
/// The function returns true on success or false if an error occurred.
|
/// The function returns true on success or false if an error occurred.
|
||||||
///
|
///
|
||||||
|
|
|
@ -312,10 +312,10 @@ impl GoldfishBattery {
|
||||||
/// Create GoldfishBattery device model
|
/// Create GoldfishBattery device model
|
||||||
///
|
///
|
||||||
/// * `mmio_base` - The 32-bit mmio base address.
|
/// * `mmio_base` - The 32-bit mmio base address.
|
||||||
/// * `irq_num` - The corresponding interrupt number of the irq_evt
|
/// * `irq_num` - The corresponding interrupt number of the irq_evt which will be put into the
|
||||||
/// which will be put into the ACPI DSDT.
|
/// ACPI DSDT.
|
||||||
/// * `irq_evt` - The interrupt event used to notify driver about
|
/// * `irq_evt` - The interrupt event used to notify driver about the battery properties
|
||||||
/// the battery properties changing.
|
/// changing.
|
||||||
/// * `socket` - Battery control socket
|
/// * `socket` - Battery control socket
|
||||||
pub fn new(
|
pub fn new(
|
||||||
mmio_base: u64,
|
mmio_base: u64,
|
||||||
|
|
|
@ -670,7 +670,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
// Attempt to add a file to an fw_cfg device w/ no fileslots and assert that nothing gets inserted
|
// Attempt to add a file to an fw_cfg device w/ no fileslots and assert that nothing gets
|
||||||
|
// inserted
|
||||||
fn write_file_one_slot_expect_nop() {
|
fn write_file_one_slot_expect_nop() {
|
||||||
let mut fw_cfg = FwCfgDevice::new(0, default_params()).unwrap();
|
let mut fw_cfg = FwCfgDevice::new(0, default_params()).unwrap();
|
||||||
let data = vec![MAGIC_BYTE];
|
let data = vec![MAGIC_BYTE];
|
||||||
|
@ -681,7 +682,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
// Attempt to add two files to an fw_cfg w/ only one fileslot and assert only first insert succeeds.
|
// Attempt to add two files to an fw_cfg w/ only one fileslot and assert only first insert
|
||||||
|
// succeeds.
|
||||||
fn write_two_files_no_slots_expect_nop_on_second() {
|
fn write_two_files_no_slots_expect_nop_on_second() {
|
||||||
let mut fw_cfg = FwCfgDevice::new(1, default_params()).unwrap();
|
let mut fw_cfg = FwCfgDevice::new(1, default_params()).unwrap();
|
||||||
let data = vec![MAGIC_BYTE];
|
let data = vec![MAGIC_BYTE];
|
||||||
|
@ -706,8 +708,8 @@ mod tests {
|
||||||
fn read_fw_cfg_signature() {
|
fn read_fw_cfg_signature() {
|
||||||
let mut data: Vec<u8> = vec![0];
|
let mut data: Vec<u8> = vec![0];
|
||||||
let (mut device, bai) = setup_read(&FILENAMES, &get_contents(), FW_CFG_SIGNATURE_SELECTOR);
|
let (mut device, bai) = setup_read(&FILENAMES, &get_contents(), FW_CFG_SIGNATURE_SELECTOR);
|
||||||
// To logically compare the revison vector to FW_CFG_REVISION byte-by-byte, we must use to_be_bytes()
|
// To logically compare the revison vector to FW_CFG_REVISION byte-by-byte, we must use
|
||||||
// since we are comparing byte arrays, not integers.
|
// to_be_bytes() since we are comparing byte arrays, not integers.
|
||||||
let signature = read_u32(&mut device, bai, &mut data[..]).to_be_bytes();
|
let signature = read_u32(&mut device, bai, &mut data[..]).to_be_bytes();
|
||||||
assert_eq!(signature, FW_CFG_SIGNATURE);
|
assert_eq!(signature, FW_CFG_SIGNATURE);
|
||||||
}
|
}
|
||||||
|
@ -717,8 +719,8 @@ mod tests {
|
||||||
fn read_fw_cfg_revision() {
|
fn read_fw_cfg_revision() {
|
||||||
let mut data: Vec<u8> = vec![0];
|
let mut data: Vec<u8> = vec![0];
|
||||||
let (mut device, bai) = setup_read(&FILENAMES, &get_contents(), FW_CFG_REVISION_SELECTOR);
|
let (mut device, bai) = setup_read(&FILENAMES, &get_contents(), FW_CFG_REVISION_SELECTOR);
|
||||||
// To logically compare the revison vector to FW_CFG_REVISION byte-by-byte, we must use to_be_bytes()
|
// To logically compare the revison vector to FW_CFG_REVISION byte-by-byte, we must use
|
||||||
// since we are comparing byte arrays, not integers.
|
// to_be_bytes() since we are comparing byte arrays, not integers.
|
||||||
let revision = read_u32(&mut device, bai, &mut data[..]).to_be_bytes();
|
let revision = read_u32(&mut device, bai, &mut data[..]).to_be_bytes();
|
||||||
assert_eq!(revision, FW_CFG_REVISION);
|
assert_eq!(revision, FW_CFG_REVISION);
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,8 +52,9 @@ pub struct IrqLevelEvent {
|
||||||
/// An event used by the device backend to signal hypervisor/VM about data or new unit
|
/// An event used by the device backend to signal hypervisor/VM about data or new unit
|
||||||
/// of work being available.
|
/// of work being available.
|
||||||
trigger_evt: Event,
|
trigger_evt: Event,
|
||||||
/// An event used by the hypervisor to signal device backend that it completed processing a unit
|
/// An event used by the hypervisor to signal device backend that it completed processing a
|
||||||
/// of work and that device should re-raise `trigger_evt` if additional work needs to be done.
|
/// unit of work and that device should re-raise `trigger_evt` if additional work needs to
|
||||||
|
/// be done.
|
||||||
resample_evt: Event,
|
resample_evt: Event,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,8 +82,9 @@ pub struct Apic {
|
||||||
/// Base duration for the APIC timer. A timer set with initial count = 1 and timer frequency
|
/// Base duration for the APIC timer. A timer set with initial count = 1 and timer frequency
|
||||||
/// divide = 1 runs for this long.
|
/// divide = 1 runs for this long.
|
||||||
cycle_length: Duration,
|
cycle_length: Duration,
|
||||||
// Register state bytes. Each register is 16-byte aligned, but only its first 4 bytes are used.
|
// Register state bytes. Each register is 16-byte aligned, but only its first 4 bytes are
|
||||||
// The register MMIO space is 4 KiB, but only the first 1 KiB (64 registers * 16 bytes) is used.
|
// used. The register MMIO space is 4 KiB, but only the first 1 KiB (64 registers * 16
|
||||||
|
// bytes) is used.
|
||||||
regs: [u8; APIC_MEM_LENGTH_BYTES as usize],
|
regs: [u8; APIC_MEM_LENGTH_BYTES as usize],
|
||||||
// Multiprocessing initialization state: running, waiting for SIPI, etc.
|
// Multiprocessing initialization state: running, waiting for SIPI, etc.
|
||||||
mp_state: MPState,
|
mp_state: MPState,
|
||||||
|
@ -96,14 +97,15 @@ pub struct Apic {
|
||||||
// When the timer started or last ticked. For one-shot timers, this is the Instant when the
|
// When the timer started or last ticked. For one-shot timers, this is the Instant when the
|
||||||
// timer started. For periodic timers, it's the Instant when it started or last expired.
|
// timer started. For periodic timers, it's the Instant when it started or last expired.
|
||||||
last_tick: Instant,
|
last_tick: Instant,
|
||||||
// Pending startup interrupt vector. There can only be one pending startup interrupt at a time.
|
// Pending startup interrupt vector. There can only be one pending startup interrupt at a
|
||||||
|
// time.
|
||||||
sipi: Option<Vector>,
|
sipi: Option<Vector>,
|
||||||
// True if there's a pending INIT interrupt to send to the CPU.
|
// True if there's a pending INIT interrupt to send to the CPU.
|
||||||
init: bool,
|
init: bool,
|
||||||
// The number of pending non-maskable interrupts to be injected into the CPU. The architecture
|
// The number of pending non-maskable interrupts to be injected into the CPU. The architecture
|
||||||
// specifies that multiple NMIs can be sent concurrently and will be processed in order. Unlike
|
// specifies that multiple NMIs can be sent concurrently and will be processed in order.
|
||||||
// fixed interrupts there's no architecturally defined place where the NMIs are queued or
|
// Unlike fixed interrupts there's no architecturally defined place where the NMIs are
|
||||||
// stored, we need to store them separately.
|
// queued or stored, we need to store them separately.
|
||||||
nmis: u32,
|
nmis: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -728,11 +730,11 @@ pub struct InterruptDestination {
|
||||||
/// The APIC ID that sent this interrupt.
|
/// The APIC ID that sent this interrupt.
|
||||||
pub source_id: u8,
|
pub source_id: u8,
|
||||||
/// In physical destination mode, used to specify the APIC ID of the destination processor.
|
/// In physical destination mode, used to specify the APIC ID of the destination processor.
|
||||||
/// In logical destination mode, used to specify a message destination address (MDA) that can be
|
/// In logical destination mode, used to specify a message destination address (MDA) that can
|
||||||
/// used to select specific processors in clusters. Only used if shorthand is None.
|
/// be used to select specific processors in clusters. Only used if shorthand is None.
|
||||||
pub dest_id: u8,
|
pub dest_id: u8,
|
||||||
/// Specifies a quick destination of all processors, all excluding self, or self. If None, then
|
/// Specifies a quick destination of all processors, all excluding self, or self. If None,
|
||||||
/// dest_id and mode are used to find the destinations.
|
/// then dest_id and mode are used to find the destinations.
|
||||||
pub shorthand: DestinationShorthand,
|
pub shorthand: DestinationShorthand,
|
||||||
/// Specifies if physical or logical addressing is used for matching dest_id.
|
/// Specifies if physical or logical addressing is used for matching dest_id.
|
||||||
pub mode: DestinationMode,
|
pub mode: DestinationMode,
|
||||||
|
@ -747,7 +749,8 @@ pub struct InterruptData {
|
||||||
pub delivery: DeliveryMode,
|
pub delivery: DeliveryMode,
|
||||||
/// Edge- or level-triggered.
|
/// Edge- or level-triggered.
|
||||||
pub trigger: TriggerMode,
|
pub trigger: TriggerMode,
|
||||||
/// For level-triggered interrupts, specifies whether the line should be asserted or deasserted.
|
/// For level-triggered interrupts, specifies whether the line should be asserted or
|
||||||
|
/// deasserted.
|
||||||
pub level: Level,
|
pub level: Level,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -883,14 +886,14 @@ impl Reg {
|
||||||
#[repr(usize)]
|
#[repr(usize)]
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
enum VectorReg {
|
enum VectorReg {
|
||||||
/// In-service register. A bit is set for each interrupt vector currently being serviced by the
|
/// In-service register. A bit is set for each interrupt vector currently being serviced by
|
||||||
/// processor.
|
/// the processor.
|
||||||
Isr = Reg::ISR,
|
Isr = Reg::ISR,
|
||||||
/// Trigger mode register. Records whether interrupts are edge-triggered (bit is clear) or
|
/// Trigger mode register. Records whether interrupts are edge-triggered (bit is clear) or
|
||||||
/// level-triggered (bit is set).
|
/// level-triggered (bit is set).
|
||||||
Tmr = Reg::TMR,
|
Tmr = Reg::TMR,
|
||||||
/// Interrupt request register. A bit is set for each interrupt vector received by the APIC but
|
/// Interrupt request register. A bit is set for each interrupt vector received by the APIC
|
||||||
/// not yet serviced by the processor.
|
/// but not yet serviced by the processor.
|
||||||
Irr = Reg::IRR,
|
Irr = Reg::IRR,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1439,7 +1442,7 @@ mod tests {
|
||||||
level: Level::Assert,
|
level: Level::Assert,
|
||||||
});
|
});
|
||||||
// Non-fixed irqs should be injected even if vcpu_ready is false. */
|
// Non-fixed irqs should be injected even if vcpu_ready is false. */
|
||||||
let irqs = a.get_pending_irqs(/*vcpu_ready=*/ false);
|
let irqs = a.get_pending_irqs(/* vcpu_ready= */ false);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
irqs,
|
irqs,
|
||||||
PendingInterrupts {
|
PendingInterrupts {
|
||||||
|
@ -1460,7 +1463,7 @@ mod tests {
|
||||||
trigger: TriggerMode::Edge,
|
trigger: TriggerMode::Edge,
|
||||||
level: Level::Assert,
|
level: Level::Assert,
|
||||||
});
|
});
|
||||||
let irqs = a.get_pending_irqs(/*vcpu_ready=*/ true);
|
let irqs = a.get_pending_irqs(/* vcpu_ready= */ true);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
irqs,
|
irqs,
|
||||||
PendingInterrupts {
|
PendingInterrupts {
|
||||||
|
@ -1470,7 +1473,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
assert_eq!(a.nmis, 0);
|
assert_eq!(a.nmis, 0);
|
||||||
|
|
||||||
let irqs = a.get_pending_irqs(/*vcpu_ready=*/ true);
|
let irqs = a.get_pending_irqs(/* vcpu_ready= */ true);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
irqs,
|
irqs,
|
||||||
PendingInterrupts {
|
PendingInterrupts {
|
||||||
|
@ -1491,7 +1494,7 @@ mod tests {
|
||||||
trigger: TriggerMode::Level,
|
trigger: TriggerMode::Level,
|
||||||
level: Level::Assert,
|
level: Level::Assert,
|
||||||
});
|
});
|
||||||
let irqs = a.get_pending_irqs(/*vcpu_ready=*/ false);
|
let irqs = a.get_pending_irqs(/* vcpu_ready= */ false);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
irqs,
|
irqs,
|
||||||
PendingInterrupts {
|
PendingInterrupts {
|
||||||
|
@ -1502,7 +1505,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
assert_eq!(a.highest_bit_in_vector(VectorReg::Irr), Some(0x10));
|
assert_eq!(a.highest_bit_in_vector(VectorReg::Irr), Some(0x10));
|
||||||
assert_eq!(a.highest_bit_in_vector(VectorReg::Isr), None);
|
assert_eq!(a.highest_bit_in_vector(VectorReg::Isr), None);
|
||||||
let irqs = a.get_pending_irqs(/*vcpu_ready=*/ true);
|
let irqs = a.get_pending_irqs(/* vcpu_ready= */ true);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
irqs,
|
irqs,
|
||||||
PendingInterrupts {
|
PendingInterrupts {
|
||||||
|
@ -1527,7 +1530,7 @@ mod tests {
|
||||||
trigger: TriggerMode::Level,
|
trigger: TriggerMode::Level,
|
||||||
level: Level::Assert,
|
level: Level::Assert,
|
||||||
});
|
});
|
||||||
let _ = a.get_pending_irqs(/*vcpu_ready=*/ true);
|
let _ = a.get_pending_irqs(/* vcpu_ready= */ true);
|
||||||
|
|
||||||
// An interrupt in a higher priority class should be injected immediately if the window is
|
// An interrupt in a higher priority class should be injected immediately if the window is
|
||||||
// open.
|
// open.
|
||||||
|
@ -1537,7 +1540,7 @@ mod tests {
|
||||||
trigger: TriggerMode::Level,
|
trigger: TriggerMode::Level,
|
||||||
level: Level::Assert,
|
level: Level::Assert,
|
||||||
});
|
});
|
||||||
let irqs = a.get_pending_irqs(/*vcpu_ready=*/ false);
|
let irqs = a.get_pending_irqs(/* vcpu_ready= */ false);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
irqs,
|
irqs,
|
||||||
PendingInterrupts {
|
PendingInterrupts {
|
||||||
|
@ -1548,7 +1551,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
assert_eq!(a.highest_bit_in_vector(VectorReg::Irr), Some(0x20));
|
assert_eq!(a.highest_bit_in_vector(VectorReg::Irr), Some(0x20));
|
||||||
assert_eq!(a.highest_bit_in_vector(VectorReg::Isr), Some(0x10));
|
assert_eq!(a.highest_bit_in_vector(VectorReg::Isr), Some(0x10));
|
||||||
let irqs = a.get_pending_irqs(/*vcpu_ready=*/ true);
|
let irqs = a.get_pending_irqs(/* vcpu_ready= */ true);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
irqs,
|
irqs,
|
||||||
PendingInterrupts {
|
PendingInterrupts {
|
||||||
|
@ -1573,7 +1576,7 @@ mod tests {
|
||||||
trigger: TriggerMode::Level,
|
trigger: TriggerMode::Level,
|
||||||
level: Level::Assert,
|
level: Level::Assert,
|
||||||
});
|
});
|
||||||
let _ = a.get_pending_irqs(/*vcpu_ready=*/ true);
|
let _ = a.get_pending_irqs(/* vcpu_ready= */ true);
|
||||||
|
|
||||||
// An interrupt in the same or lower priority class should be deferred.
|
// An interrupt in the same or lower priority class should be deferred.
|
||||||
a.accept_irq(&InterruptData {
|
a.accept_irq(&InterruptData {
|
||||||
|
@ -1582,12 +1585,13 @@ mod tests {
|
||||||
trigger: TriggerMode::Level,
|
trigger: TriggerMode::Level,
|
||||||
level: Level::Assert,
|
level: Level::Assert,
|
||||||
});
|
});
|
||||||
let irqs = a.get_pending_irqs(/*vcpu_ready=*/ true);
|
let irqs = a.get_pending_irqs(/* vcpu_ready= */ true);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
irqs,
|
irqs,
|
||||||
PendingInterrupts {
|
PendingInterrupts {
|
||||||
fixed: None,
|
fixed: None,
|
||||||
needs_window: false, // Not injectable due to higher priority ISRV, so no window needed.
|
// Not injectable due to higher priority ISRV, so no window needed.
|
||||||
|
needs_window: false,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -1597,7 +1601,7 @@ mod tests {
|
||||||
// EOI lets it be injected.
|
// EOI lets it be injected.
|
||||||
let msg = a.write(Reg::EOI as u64, &[0; 4]).unwrap();
|
let msg = a.write(Reg::EOI as u64, &[0; 4]).unwrap();
|
||||||
assert_eq!(msg, ApicBusMsg::Eoi(0x10));
|
assert_eq!(msg, ApicBusMsg::Eoi(0x10));
|
||||||
let irqs = a.get_pending_irqs(/*vcpu_ready=*/ true);
|
let irqs = a.get_pending_irqs(/* vcpu_ready= */ true);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
irqs,
|
irqs,
|
||||||
PendingInterrupts {
|
PendingInterrupts {
|
||||||
|
@ -1621,7 +1625,7 @@ mod tests {
|
||||||
level: Level::Assert,
|
level: Level::Assert,
|
||||||
});
|
});
|
||||||
a.set_reg(Reg::TPR, 0x20);
|
a.set_reg(Reg::TPR, 0x20);
|
||||||
let irqs = a.get_pending_irqs(/*vcpu_ready=*/ true);
|
let irqs = a.get_pending_irqs(/* vcpu_ready= */ true);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
irqs,
|
irqs,
|
||||||
PendingInterrupts {
|
PendingInterrupts {
|
||||||
|
@ -1631,7 +1635,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
a.set_reg(Reg::TPR, 0x19);
|
a.set_reg(Reg::TPR, 0x19);
|
||||||
let irqs = a.get_pending_irqs(/*vcpu_ready=*/ true);
|
let irqs = a.get_pending_irqs(/* vcpu_ready= */ true);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
irqs,
|
irqs,
|
||||||
PendingInterrupts {
|
PendingInterrupts {
|
||||||
|
|
|
@ -214,8 +214,8 @@ impl IrqChip for GeniezoneKernelIrqChip {
|
||||||
|
|
||||||
/// Return a vector of all registered irq numbers and their associated events and event
|
/// Return a vector of all registered irq numbers and their associated events and event
|
||||||
/// indices. These should be used by the main thread to wait for irq events.
|
/// indices. These should be used by the main thread to wait for irq events.
|
||||||
/// For the GeniezoneKernelIrqChip, the kernel handles listening to irq events being triggered by
|
/// For the GeniezoneKernelIrqChip, the kernel handles listening to irq events being triggered
|
||||||
/// devices, so this function always returns an empty Vec.
|
/// by devices, so this function always returns an empty Vec.
|
||||||
fn irq_event_tokens(&self) -> Result<Vec<(IrqEventIndex, IrqEventSource, Event)>> {
|
fn irq_event_tokens(&self) -> Result<Vec<(IrqEventIndex, IrqEventSource, Event)>> {
|
||||||
Ok(Vec::new())
|
Ok(Vec::new())
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,10 +96,10 @@ struct OutEventSnapshot {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Snapshot of [Ioapic] state. Some fields were intentionally excluded:
|
/// Snapshot of [Ioapic] state. Some fields were intentionally excluded:
|
||||||
/// * [Ioapic::resample_events]: these will get re-registered when the VM is
|
/// * [Ioapic::resample_events]: these will get re-registered when the VM is created (e.g. prior to
|
||||||
/// created (e.g. prior to restoring a snapshot).
|
/// restoring a snapshot).
|
||||||
/// * [Ioapic::out_events]: this isn't serializable as it contains Events.
|
/// * [Ioapic::out_events]: this isn't serializable as it contains Events. Replaced by
|
||||||
/// Replaced by [IoapicSnapshot::out_event_snapshots].
|
/// [IoapicSnapshot::out_event_snapshots].
|
||||||
/// * [Ioapic::irq_tube]: will be set up as part of creating the VM.
|
/// * [Ioapic::irq_tube]: will be set up as part of creating the VM.
|
||||||
///
|
///
|
||||||
/// See [Ioapic] for descriptions of fields by the same names.
|
/// See [Ioapic] for descriptions of fields by the same names.
|
||||||
|
@ -131,8 +131,8 @@ pub struct Ioapic {
|
||||||
ioregsel: u8,
|
ioregsel: u8,
|
||||||
/// ioapicid register. Bits 24 - 27 contain the APIC ID for this device.
|
/// ioapicid register. Bits 24 - 27 contain the APIC ID for this device.
|
||||||
ioapicid: u32,
|
ioapicid: u32,
|
||||||
/// Remote IRR for Edge Triggered Real Time Clock interrupts, which allows the CMOS to know when
|
/// Remote IRR for Edge Triggered Real Time Clock interrupts, which allows the CMOS to know
|
||||||
/// one of its interrupts is being coalesced.
|
/// when one of its interrupts is being coalesced.
|
||||||
rtc_remote_irr: bool,
|
rtc_remote_irr: bool,
|
||||||
/// Outgoing irq events that are used to inject MSI interrupts.
|
/// Outgoing irq events that are used to inject MSI interrupts.
|
||||||
/// Also contains the serializable form used for snapshotting.
|
/// Also contains the serializable form used for snapshotting.
|
||||||
|
|
|
@ -170,8 +170,8 @@ impl AiaDescriptor {
|
||||||
addr: raw_aplic_addr as u64,
|
addr: raw_aplic_addr as u64,
|
||||||
flags: 0,
|
flags: 0,
|
||||||
};
|
};
|
||||||
// Safe because we allocated the struct that's being passed in, and raw_aplic_addr is pointing
|
// Safe because we allocated the struct that's being passed in, and raw_aplic_addr is
|
||||||
// to a uniquely owned local, mutable variable.
|
// pointing to a uniquely owned local, mutable variable.
|
||||||
let ret = unsafe { ioctl_with_ref(self, KVM_SET_DEVICE_ATTR(), &kvm_attr) };
|
let ret = unsafe { ioctl_with_ref(self, KVM_SET_DEVICE_ATTR(), &kvm_attr) };
|
||||||
if ret != 0 {
|
if ret != 0 {
|
||||||
return errno_result();
|
return errno_result();
|
||||||
|
|
|
@ -611,8 +611,8 @@ impl IrqChip for KvmSplitIrqChip {
|
||||||
vcpu.interrupt(vector)?;
|
vcpu.interrupt(vector)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The second interrupt request should be handled immediately, so ask vCPU to exit as soon as
|
// The second interrupt request should be handled immediately, so ask vCPU to exit as soon
|
||||||
// possible.
|
// as possible.
|
||||||
if self.interrupt_requested(vcpu_id) {
|
if self.interrupt_requested(vcpu_id) {
|
||||||
vcpu.set_interrupt_window_requested(true);
|
vcpu.set_interrupt_window_requested(true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,7 +161,8 @@ pub trait IrqChip: Send {
|
||||||
/// Add a vcpu to the irq chip.
|
/// Add a vcpu to the irq chip.
|
||||||
fn add_vcpu(&mut self, vcpu_id: usize, vcpu: &dyn Vcpu) -> Result<()>;
|
fn add_vcpu(&mut self, vcpu_id: usize, vcpu: &dyn Vcpu) -> Result<()>;
|
||||||
|
|
||||||
/// Register an event with edge-trigger semantic that can trigger an interrupt for a particular GSI.
|
/// Register an event with edge-trigger semantic that can trigger an interrupt for a particular
|
||||||
|
/// GSI.
|
||||||
fn register_edge_irq_event(
|
fn register_edge_irq_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
irq: u32,
|
irq: u32,
|
||||||
|
@ -172,7 +173,8 @@ pub trait IrqChip: Send {
|
||||||
/// Unregister an event with edge-trigger semantic for a particular GSI.
|
/// Unregister an event with edge-trigger semantic for a particular GSI.
|
||||||
fn unregister_edge_irq_event(&mut self, irq: u32, irq_event: &IrqEdgeEvent) -> Result<()>;
|
fn unregister_edge_irq_event(&mut self, irq: u32, irq_event: &IrqEdgeEvent) -> Result<()>;
|
||||||
|
|
||||||
/// Register an event with level-trigger semantic that can trigger an interrupt for a particular GSI.
|
/// Register an event with level-trigger semantic that can trigger an interrupt for a particular
|
||||||
|
/// GSI.
|
||||||
fn register_level_irq_event(
|
fn register_level_irq_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
irq: u32,
|
irq: u32,
|
||||||
|
|
|
@ -801,7 +801,7 @@ mod tests {
|
||||||
icw_init_both(&mut data.pic);
|
icw_init_both(&mut data.pic);
|
||||||
|
|
||||||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||||
data.pic.service_irq(/*irq=*/ 12, /*level=*/ true);
|
data.pic.service_irq(/* irq= */ 12, /* level= */ true);
|
||||||
|
|
||||||
// Check that IRQ is requesting acknowledgment.
|
// Check that IRQ is requesting acknowledgment.
|
||||||
assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, (1 << 4));
|
assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, (1 << 4));
|
||||||
|
@ -830,12 +830,12 @@ mod tests {
|
||||||
icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
|
icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
|
||||||
|
|
||||||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||||
data.pic.service_irq(/*irq=*/ 12, /*level=*/ true);
|
data.pic.service_irq(/* irq= */ 12, /* level= */ true);
|
||||||
assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 4));
|
assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 4));
|
||||||
|
|
||||||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||||
// Request higher-priority IRQ on secondary.
|
// Request higher-priority IRQ on secondary.
|
||||||
data.pic.service_irq(/*irq=*/ 8, /*level=*/ true);
|
data.pic.service_irq(/* irq= */ 8, /* level= */ true);
|
||||||
assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 0));
|
assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 0));
|
||||||
|
|
||||||
// Check that IRQ is ack'd and EOI is automatically done.
|
// Check that IRQ is ack'd and EOI is automatically done.
|
||||||
|
@ -859,10 +859,10 @@ mod tests {
|
||||||
icw_init_both_with_icw4(&mut data.pic, 0x01);
|
icw_init_both_with_icw4(&mut data.pic, 0x01);
|
||||||
|
|
||||||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||||
data.pic.service_irq(/*irq=*/ 12, /*level=*/ true);
|
data.pic.service_irq(/* irq= */ 12, /* level= */ true);
|
||||||
assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 4));
|
assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 4));
|
||||||
|
|
||||||
data.pic.service_irq(/*irq=*/ 8, /*level=*/ true);
|
data.pic.service_irq(/* irq= */ 8, /* level= */ true);
|
||||||
// Primary cannot get any IRQ, so this should not provide any interrupt.
|
// Primary cannot get any IRQ, so this should not provide any interrupt.
|
||||||
assert_eq!(data.pic.get_external_interrupt(), None);
|
assert_eq!(data.pic.get_external_interrupt(), None);
|
||||||
|
|
||||||
|
@ -899,7 +899,7 @@ mod tests {
|
||||||
// OCW2: Mask IRQ line 6 on secondary (IRQ 14).
|
// OCW2: Mask IRQ line 6 on secondary (IRQ 14).
|
||||||
data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0x40]);
|
data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0x40]);
|
||||||
|
|
||||||
data.pic.service_irq(/*irq=*/ 14, /*level=*/ true);
|
data.pic.service_irq(/* irq= */ 14, /* level= */ true);
|
||||||
assert_eq!(data.pic.get_external_interrupt(), None);
|
assert_eq!(data.pic.get_external_interrupt(), None);
|
||||||
|
|
||||||
assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 1 << 6);
|
assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 1 << 6);
|
||||||
|
@ -932,9 +932,9 @@ mod tests {
|
||||||
data.pic.write(pic_bus_address(PIC_PRIMARY_DATA), &[0xff]);
|
data.pic.write(pic_bus_address(PIC_PRIMARY_DATA), &[0xff]);
|
||||||
data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0xff]);
|
data.pic.write(pic_bus_address(PIC_SECONDARY_DATA), &[0xff]);
|
||||||
|
|
||||||
data.pic.service_irq(/*irq=*/ 14, /*level=*/ true);
|
data.pic.service_irq(/* irq= */ 14, /* level= */ true);
|
||||||
data.pic.service_irq(/*irq=*/ 4, /*level=*/ true);
|
data.pic.service_irq(/* irq= */ 4, /* level= */ true);
|
||||||
data.pic.service_irq(/*irq=*/ 12, /*level=*/ true);
|
data.pic.service_irq(/* irq= */ 12, /* level= */ true);
|
||||||
|
|
||||||
// Primary cannot get any IRQs since they're all masked.
|
// Primary cannot get any IRQs since they're all masked.
|
||||||
assert_eq!(data.pic.get_external_interrupt(), None);
|
assert_eq!(data.pic.get_external_interrupt(), None);
|
||||||
|
@ -968,8 +968,8 @@ mod tests {
|
||||||
|
|
||||||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||||
// Poplate some data on irr/isr. IRQ4 will be in isr and IRQ5 in irr.
|
// Poplate some data on irr/isr. IRQ4 will be in isr and IRQ5 in irr.
|
||||||
data.pic.service_irq(/*irq=*/ 5, /*level=*/ true);
|
data.pic.service_irq(/* irq= */ 5, /* level= */ true);
|
||||||
data.pic.service_irq(/*irq=*/ 4, /*level=*/ true);
|
data.pic.service_irq(/* irq= */ 4, /* level= */ true);
|
||||||
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
|
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
|
||||||
|
|
||||||
// Read primary IRR.
|
// Read primary IRR.
|
||||||
|
@ -1010,7 +1010,7 @@ mod tests {
|
||||||
icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
|
icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
|
||||||
|
|
||||||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||||
data.pic.service_irq(/*irq=*/ 2, /*level=*/ true);
|
data.pic.service_irq(/* irq= */ 2, /* level= */ true);
|
||||||
// 0x70 is secondary IRQ base, 7 is for a spurious IRQ.
|
// 0x70 is secondary IRQ base, 7 is for a spurious IRQ.
|
||||||
assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 7));
|
assert_eq!(data.pic.get_external_interrupt(), Some(0x70 + 7));
|
||||||
}
|
}
|
||||||
|
@ -1022,11 +1022,11 @@ mod tests {
|
||||||
icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
|
icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
|
||||||
|
|
||||||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||||
data.pic.service_irq(/*irq=*/ 4, /*level=*/ true);
|
data.pic.service_irq(/* irq= */ 4, /* level= */ true);
|
||||||
// get_external_interrupt clears the irr so it is possible to request the same IRQ again.
|
// get_external_interrupt clears the irr so it is possible to request the same IRQ again.
|
||||||
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
|
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
|
||||||
|
|
||||||
data.pic.service_irq(/*irq=*/ 4, /*level=*/ true);
|
data.pic.service_irq(/* irq= */ 4, /* level= */ true);
|
||||||
|
|
||||||
// In edge triggered mode, there should be no IRQ after this EOI.
|
// In edge triggered mode, there should be no IRQ after this EOI.
|
||||||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||||
|
@ -1044,11 +1044,11 @@ mod tests {
|
||||||
data.pic.write(pic_bus_address(PIC_PRIMARY_ELCR), &[0x10]);
|
data.pic.write(pic_bus_address(PIC_PRIMARY_ELCR), &[0x10]);
|
||||||
|
|
||||||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||||
data.pic.service_irq(/*irq=*/ 4, /*level=*/ true);
|
data.pic.service_irq(/* irq= */ 4, /* level= */ true);
|
||||||
// get_external_interrupt clears the irr so it is possible to request the same IRQ again.
|
// get_external_interrupt clears the irr so it is possible to request the same IRQ again.
|
||||||
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
|
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
|
||||||
|
|
||||||
data.pic.service_irq(/*irq=*/ 4, /*level=*/ true);
|
data.pic.service_irq(/* irq= */ 4, /* level= */ true);
|
||||||
|
|
||||||
// In level-triggered mode, there should be another IRQ request after this EOI.
|
// In level-triggered mode, there should be another IRQ request after this EOI.
|
||||||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||||
|
@ -1063,7 +1063,7 @@ mod tests {
|
||||||
icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
|
icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
|
||||||
|
|
||||||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||||
data.pic.service_irq(/*irq=*/ 4, /*level=*/ true);
|
data.pic.service_irq(/* irq= */ 4, /* level= */ true);
|
||||||
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
|
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 4));
|
||||||
|
|
||||||
// Specific EOI command on IRQ3. Primary PIC's ISR should be unaffected since it's targeted
|
// Specific EOI command on IRQ3. Primary PIC's ISR should be unaffected since it's targeted
|
||||||
|
@ -1089,9 +1089,9 @@ mod tests {
|
||||||
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x00]);
|
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x00]);
|
||||||
|
|
||||||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||||
data.pic.service_irq(/*irq=*/ 5, /*level=*/ true);
|
data.pic.service_irq(/* irq= */ 5, /* level= */ true);
|
||||||
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 5));
|
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 5));
|
||||||
data.pic.service_irq(/*irq=*/ 5, /*level=*/ false);
|
data.pic.service_irq(/* irq= */ 5, /* level= */ false);
|
||||||
|
|
||||||
// EOI automatically happened. Now priority should not be rotated.
|
// EOI automatically happened. Now priority should not be rotated.
|
||||||
assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
|
assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
|
||||||
|
@ -1104,9 +1104,9 @@ mod tests {
|
||||||
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x80]);
|
.write(pic_bus_address(PIC_PRIMARY_COMMAND), &[0x80]);
|
||||||
|
|
||||||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||||
data.pic.service_irq(/*irq=*/ 5, /*level*/ true);
|
data.pic.service_irq(/* irq= */ 5, /* level */ true);
|
||||||
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 5));
|
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 5));
|
||||||
data.pic.service_irq(/*irq=*/ 5, /*level=*/ false);
|
data.pic.service_irq(/* irq= */ 5, /* level= */ false);
|
||||||
|
|
||||||
// EOI automatically happened, and the priority *should* be rotated.
|
// EOI automatically happened, and the priority *should* be rotated.
|
||||||
assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
|
assert_eq!(data.pic.pics[PicSelect::Primary as usize].isr, 0);
|
||||||
|
@ -1120,9 +1120,9 @@ mod tests {
|
||||||
icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
|
icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
|
||||||
|
|
||||||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||||
data.pic.service_irq(/*irq=*/ 5, /*level=*/ true);
|
data.pic.service_irq(/* irq= */ 5, /* level= */ true);
|
||||||
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 5));
|
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 5));
|
||||||
data.pic.service_irq(/*irq=*/ 5, /*level=*/ false);
|
data.pic.service_irq(/* irq= */ 5, /* level= */ false);
|
||||||
|
|
||||||
// Rotate on specific EOI IRQ4. Since this is a different IRQ number, Should not have an
|
// Rotate on specific EOI IRQ4. Since this is a different IRQ number, Should not have an
|
||||||
// effect on isr.
|
// effect on isr.
|
||||||
|
@ -1146,9 +1146,9 @@ mod tests {
|
||||||
icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
|
icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
|
||||||
|
|
||||||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||||
data.pic.service_irq(/*irq=*/ 5, /*level=*/ true);
|
data.pic.service_irq(/* irq= */ 5, /* level= */ true);
|
||||||
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 5));
|
assert_eq!(data.pic.get_external_interrupt(), Some(0x08 + 5));
|
||||||
data.pic.service_irq(/*irq=*/ 5, /*level=*/ false);
|
data.pic.service_irq(/* irq= */ 5, /* level= */ false);
|
||||||
|
|
||||||
// Rotate on non-specific EOI.
|
// Rotate on non-specific EOI.
|
||||||
data.pic
|
data.pic
|
||||||
|
@ -1166,7 +1166,7 @@ mod tests {
|
||||||
icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
|
icw_init_both_with_icw4(&mut data.pic, FULLY_NESTED_NO_AUTO_EOI);
|
||||||
|
|
||||||
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
// TODO(mutexlox): Verify APIC interaction when it is implemented.
|
||||||
data.pic.service_irq(/*irq=*/ 12, /*level=*/ true);
|
data.pic.service_irq(/* irq= */ 12, /* level= */ true);
|
||||||
|
|
||||||
assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 1 << 2);
|
assert_eq!(data.pic.pics[PicSelect::Primary as usize].irr, 1 << 2);
|
||||||
assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 1 << 4);
|
assert_eq!(data.pic.pics[PicSelect::Secondary as usize].irr, 1 << 4);
|
||||||
|
|
|
@ -673,7 +673,7 @@ impl<V: VcpuX86_64 + 'static> IrqChip for UserspaceIrqChip<V> {
|
||||||
/// `wait_until_runnable` calls go back to waiting for runnability normally.
|
/// `wait_until_runnable` calls go back to waiting for runnability normally.
|
||||||
fn kick_halted_vcpus(&self) {
|
fn kick_halted_vcpus(&self) {
|
||||||
for waiter in self.waiters.iter() {
|
for waiter in self.waiters.iter() {
|
||||||
waiter.set_and_notify(/*interrupted=*/ true);
|
waiter.set_and_notify(/* interrupted= */ true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -389,8 +389,8 @@ impl IrqChip for WhpxSplitIrqChip {
|
||||||
vcpu.interrupt(vector as u32)?;
|
vcpu.interrupt(vector as u32)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The second interrupt request should be handled immediately, so ask vCPU to exit as soon as
|
// The second interrupt request should be handled immediately, so ask vCPU to exit as soon
|
||||||
// possible.
|
// as possible.
|
||||||
if self.interrupt_requested(vcpu_id) {
|
if self.interrupt_requested(vcpu_id) {
|
||||||
vcpu.set_interrupt_window_requested(true);
|
vcpu.set_interrupt_window_requested(true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -395,8 +395,8 @@ pub trait PciDevice: Send + Suspendable {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a reference to the API client for sending VmMemoryRequest. Any devices that uses ioevents
|
/// Gets a reference to the API client for sending VmMemoryRequest. Any devices that uses
|
||||||
/// must provide this.
|
/// ioevents must provide this.
|
||||||
fn get_vm_memory_client(&self) -> Option<&VmMemoryClient> {
|
fn get_vm_memory_client(&self) -> Option<&VmMemoryClient> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -684,12 +684,12 @@ impl PitCounter {
|
||||||
speaker.set_output(self.get_output().into());
|
speaker.set_output(self.get_output().into());
|
||||||
speaker.set_iochk_nmi(0);
|
speaker.set_iochk_nmi(0);
|
||||||
speaker.set_serr_nmi(0);
|
speaker.set_serr_nmi(0);
|
||||||
speaker.get(/*offset=*/ 0, /*width=*/ 8) as u8
|
speaker.get(/* offset= */ 0, /* width= */ 8) as u8
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_speaker(&mut self, datum: u8) {
|
fn write_speaker(&mut self, datum: u8) {
|
||||||
let mut speaker = SpeakerPortFields::new();
|
let mut speaker = SpeakerPortFields::new();
|
||||||
speaker.set(/*offset=*/ 0, /*width=*/ 8, datum.into());
|
speaker.set(/* offset= */ 0, /* width= */ 8, datum.into());
|
||||||
let new_gate = speaker.get_gate() != 0;
|
let new_gate = speaker.get_gate() != 0;
|
||||||
match self.get_command_mode() {
|
match self.get_command_mode() {
|
||||||
Some(CommandMode::CommandInterrupt) | Some(CommandMode::CommandSWStrobe) => (),
|
Some(CommandMode::CommandInterrupt) | Some(CommandMode::CommandSWStrobe) => (),
|
||||||
|
|
|
@ -282,7 +282,8 @@ impl VfioPlatformDevice {
|
||||||
let host = mmap.as_ptr() as u64;
|
let host = mmap.as_ptr() as u64;
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// Safe because the given guest_map_start is valid guest bar address. and
|
// Safe because the given guest_map_start is valid guest bar address. and
|
||||||
// the host pointer is correct and valid guaranteed by MemoryMapping interface.
|
// the host pointer is correct and valid guaranteed by MemoryMapping
|
||||||
|
// interface.
|
||||||
match unsafe {
|
match unsafe {
|
||||||
self.device
|
self.device
|
||||||
.vfio_dma_map(guest_map_start, mmap_size, host, true)
|
.vfio_dma_map(guest_map_start, mmap_size, host, true)
|
||||||
|
|
|
@ -218,9 +218,10 @@ impl Serial {
|
||||||
for event in events.iter() {
|
for event in events.iter() {
|
||||||
match event.token {
|
match event.token {
|
||||||
Token::Kill => {
|
Token::Kill => {
|
||||||
// Ignore the kill event until there are no other events to process so that
|
// Ignore the kill event until there are no other events to process
|
||||||
// we drain `rx` as much as possible. The next `wait_ctx.wait()` call will
|
// so that we drain `rx` as much as possible. The next
|
||||||
// immediately re-entry this case since we don't call `kill_evt.wait()`.
|
// `wait_ctx.wait()` call will immediately re-entry this case since
|
||||||
|
// we don't call `kill_evt.wait()`.
|
||||||
if events.iter().all(|e| matches!(e.token, Token::Kill)) {
|
if events.iter().all(|e| matches!(e.token, Token::Kill)) {
|
||||||
return rx;
|
return rx;
|
||||||
}
|
}
|
||||||
|
@ -262,7 +263,8 @@ impl Serial {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// Being interrupted is not an error, but everything else is.
|
// Being interrupted is not an error, but everything else
|
||||||
|
// is.
|
||||||
if e.kind() != io::ErrorKind::Interrupted {
|
if e.kind() != io::ErrorKind::Interrupted {
|
||||||
error!(
|
error!(
|
||||||
"failed to read for bytes to queue into serial device: {}",
|
"failed to read for bytes to queue into serial device: {}",
|
||||||
|
|
|
@ -175,7 +175,7 @@ impl SerialParameters {
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `evt` - event used for interrupt events
|
/// * `evt` - event used for interrupt events
|
||||||
/// * `keep_rds` - Vector of FDs required by this device if it were sandboxed in a child
|
/// * `keep_rds` - Vector of FDs required by this device if it were sandboxed in a child
|
||||||
/// process. `evt` will always be added to this vector by this function.
|
/// process. `evt` will always be added to this vector by this function.
|
||||||
pub fn create_serial_device<T: SerialDevice>(
|
pub fn create_serial_device<T: SerialDevice>(
|
||||||
&self,
|
&self,
|
||||||
protection_type: ProtectionType,
|
protection_type: ProtectionType,
|
||||||
|
|
|
@ -118,8 +118,8 @@ fn tsc_sync_mitigations_inner(
|
||||||
let host_tsc_now = rdtsc();
|
let host_tsc_now = rdtsc();
|
||||||
|
|
||||||
for i in 0..num_vcpus {
|
for i in 0..num_vcpus {
|
||||||
// This handles the case where num_vcpus > num_cores, even though we try to avoid that in
|
// This handles the case where num_vcpus > num_cores, even though we try to avoid that
|
||||||
// practice.
|
// in practice.
|
||||||
let pinned_core = i % num_cores;
|
let pinned_core = i % num_cores;
|
||||||
|
|
||||||
mitigations.affinities[i] = Some(vec![pinned_core]);
|
mitigations.affinities[i] = Some(vec![pinned_core]);
|
||||||
|
|
|
@ -660,8 +660,8 @@ impl BackendDeviceType {
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let transfer_status = {
|
let transfer_status = {
|
||||||
// We need to hold the lock to avoid race condition.
|
// We need to hold the lock to avoid race condition.
|
||||||
// While we are trying to submit the transfer, another thread might want to cancel the same
|
// While we are trying to submit the transfer, another thread might want to cancel the
|
||||||
// transfer. Holding the lock here makes sure one of them is cancelled.
|
// same transfer. Holding the lock here makes sure one of them is cancelled.
|
||||||
let mut state = xhci_transfer.state().lock();
|
let mut state = xhci_transfer.state().lock();
|
||||||
match mem::replace(&mut *state, XhciTransferState::Cancelled) {
|
match mem::replace(&mut *state, XhciTransferState::Cancelled) {
|
||||||
XhciTransferState::Created => {
|
XhciTransferState::Created => {
|
||||||
|
|
|
@ -385,7 +385,7 @@ pub fn init_xhci_mmio_space_and_regs() -> (RegisterSpace, XhciRegs) {
|
||||||
guest_write_1_to_clear_mask: 0,);
|
guest_write_1_to_clear_mask: 0,);
|
||||||
mmio.add_register_array(&doorbells);
|
mmio.add_register_array(&doorbells);
|
||||||
|
|
||||||
/*Runtime Registers */
|
/* Runtime Registers */
|
||||||
|
|
||||||
mmio.add_register(
|
mmio.add_register(
|
||||||
// mfindex
|
// mfindex
|
||||||
|
|
|
@ -172,8 +172,8 @@ impl EventLoop {
|
||||||
.map_err(Error::WaitContextAddDescriptor)
|
.map_err(Error::WaitContextAddDescriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes event for this `descriptor`. This function is safe to call even when the `descriptor`
|
/// Removes event for this `descriptor`. This function is safe to call even when the
|
||||||
/// is not actively being polled because it's been paused.
|
/// `descriptor` is not actively being polled because it's been paused.
|
||||||
///
|
///
|
||||||
/// EventLoop does not guarantee all events for `descriptor` is handled.
|
/// EventLoop does not guarantee all events for `descriptor` is handled.
|
||||||
pub fn remove_event_for_descriptor(&self, descriptor: &dyn AsRawDescriptor) -> Result<()> {
|
pub fn remove_event_for_descriptor(&self, descriptor: &dyn AsRawDescriptor) -> Result<()> {
|
||||||
|
|
|
@ -757,8 +757,8 @@ impl VfioGroup {
|
||||||
|
|
||||||
let container_raw_descriptor = container.as_raw_descriptor();
|
let container_raw_descriptor = container.as_raw_descriptor();
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// Safe as we are the owner of group_file and container_raw_descriptor which are valid value,
|
// Safe as we are the owner of group_file and container_raw_descriptor which are valid
|
||||||
// and we verify the ret value
|
// value, and we verify the ret value
|
||||||
ret = unsafe {
|
ret = unsafe {
|
||||||
ioctl_with_ref(
|
ioctl_with_ref(
|
||||||
&group_file,
|
&group_file,
|
||||||
|
@ -1355,10 +1355,10 @@ impl VfioDevice {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enable vfio device's irq and associate Irqfd Event with device.
|
/// Enable vfio device's irq and associate Irqfd Event with device.
|
||||||
/// When MSIx is enabled, multi vectors will be supported, and vectors starting from subindex to subindex +
|
/// When MSIx is enabled, multi vectors will be supported, and vectors starting from subindex to
|
||||||
/// descriptors length will be assigned with irqfd in the descriptors array.
|
/// subindex + descriptors length will be assigned with irqfd in the descriptors array.
|
||||||
/// when index = VFIO_PCI_REQ_IRQ_INDEX, kernel vfio will trigger this event when physical device
|
/// when index = VFIO_PCI_REQ_IRQ_INDEX, kernel vfio will trigger this event when physical
|
||||||
/// is removed.
|
/// device is removed.
|
||||||
/// If descriptor is None, -1 is assigned to the irq. A value of -1 is used to either de-assign
|
/// If descriptor is None, -1 is assigned to the irq. A value of -1 is used to either de-assign
|
||||||
/// interrupts if already assigned or skip un-assigned interrupts.
|
/// interrupts if already assigned or skip un-assigned interrupts.
|
||||||
pub fn irq_enable(
|
pub fn irq_enable(
|
||||||
|
|
|
@ -108,8 +108,8 @@ pub struct DiskOption {
|
||||||
)]
|
)]
|
||||||
pub io_concurrency: NonZeroU32,
|
pub io_concurrency: NonZeroU32,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
/// Experimental option to run multiple worker threads in parallel. If false, only single thread
|
/// Experimental option to run multiple worker threads in parallel. If false, only single
|
||||||
/// runs by default. Note this option is not effective for vhost-user blk device.
|
/// thread runs by default. Note this option is not effective for vhost-user blk device.
|
||||||
pub multiple_workers: bool,
|
pub multiple_workers: bool,
|
||||||
#[serde(default, alias = "async_executor")]
|
#[serde(default, alias = "async_executor")]
|
||||||
/// The async executor kind to simulate the block device with. This option takes
|
/// The async executor kind to simulate the block device with. This option takes
|
||||||
|
|
|
@ -436,9 +436,9 @@ impl VirtioDevice for Console {
|
||||||
match queues_state {
|
match queues_state {
|
||||||
None => Ok(()),
|
None => Ok(()),
|
||||||
Some((mem, interrupt, queues)) => {
|
Some((mem, interrupt, queues)) => {
|
||||||
// TODO(khei): activate is just what we want at the moment, but we should probably move
|
// TODO(khei): activate is just what we want at the moment, but we should probably
|
||||||
// it into a "start workers" function to make it obvious that it isn't strictly
|
// move it into a "start workers" function to make it obvious that
|
||||||
// used for activate events.
|
// it isn't strictly used for activate events.
|
||||||
self.activate(mem, interrupt, queues)?;
|
self.activate(mem, interrupt, queues)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,15 +22,15 @@ pub enum CachePolicy {
|
||||||
/// the FUSE client (i.e., the file system does not have exclusive access to the directory).
|
/// the FUSE client (i.e., the file system does not have exclusive access to the directory).
|
||||||
Never,
|
Never,
|
||||||
|
|
||||||
/// The client is free to choose when and how to cache file data. This is the default policy and
|
/// The client is free to choose when and how to cache file data. This is the default policy
|
||||||
/// uses close-to-open consistency as described in the enum documentation.
|
/// and uses close-to-open consistency as described in the enum documentation.
|
||||||
#[default]
|
#[default]
|
||||||
Auto,
|
Auto,
|
||||||
|
|
||||||
/// The client should always cache file data. This means that the FUSE client will not
|
/// The client should always cache file data. This means that the FUSE client will not
|
||||||
/// invalidate any cached data that was returned by the file system the last time the file was
|
/// invalidate any cached data that was returned by the file system the last time the file was
|
||||||
/// opened. This policy should only be selected when the file system has exclusive access to the
|
/// opened. This policy should only be selected when the file system has exclusive access to
|
||||||
/// directory.
|
/// the directory.
|
||||||
Always,
|
Always,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,17 +108,17 @@ pub struct Config {
|
||||||
#[serde(default, alias = "cache")]
|
#[serde(default, alias = "cache")]
|
||||||
pub cache_policy: CachePolicy,
|
pub cache_policy: CachePolicy,
|
||||||
|
|
||||||
/// Whether the file system should enabled writeback caching. This can improve performance as it
|
/// Whether the file system should enabled writeback caching. This can improve performance as
|
||||||
/// allows the FUSE client to cache and coalesce multiple writes before sending them to the file
|
/// it allows the FUSE client to cache and coalesce multiple writes before sending them to
|
||||||
/// system. However, enabling this option can increase the risk of data corruption if the file
|
/// the file system. However, enabling this option can increase the risk of data corruption
|
||||||
/// contents can change without the knowledge of the FUSE client (i.e., the server does **NOT**
|
/// if the file contents can change without the knowledge of the FUSE client (i.e., the
|
||||||
/// have exclusive access). Additionally, the file system should have read access to all files
|
/// server does **NOT** have exclusive access). Additionally, the file system should have
|
||||||
/// in the directory it is serving as the FUSE client may send read requests even for files
|
/// read access to all files in the directory it is serving as the FUSE client may send
|
||||||
/// opened with `O_WRONLY`.
|
/// read requests even for files opened with `O_WRONLY`.
|
||||||
///
|
///
|
||||||
/// Therefore callers should only enable this option when they can guarantee that: 1) the file
|
/// Therefore callers should only enable this option when they can guarantee that: 1) the file
|
||||||
/// system has exclusive access to the directory and 2) the file system has read permissions for
|
/// system has exclusive access to the directory and 2) the file system has read permissions
|
||||||
/// all files in that directory.
|
/// for all files in that directory.
|
||||||
///
|
///
|
||||||
/// The default value for this option is `false`.
|
/// The default value for this option is `false`.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
@ -127,8 +127,8 @@ pub struct Config {
|
||||||
/// Controls whether security.* xattrs (except for security.selinux) are re-written. When this
|
/// Controls whether security.* xattrs (except for security.selinux) are re-written. When this
|
||||||
/// is set to true, the server will add a "user.virtiofs" prefix to xattrs in the security
|
/// is set to true, the server will add a "user.virtiofs" prefix to xattrs in the security
|
||||||
/// namespace. Setting these xattrs requires CAP_SYS_ADMIN in the namespace where the file
|
/// namespace. Setting these xattrs requires CAP_SYS_ADMIN in the namespace where the file
|
||||||
/// system was mounted and since the server usually runs in an unprivileged user namespace, it's
|
/// system was mounted and since the server usually runs in an unprivileged user namespace,
|
||||||
/// unlikely to have that capability.
|
/// it's unlikely to have that capability.
|
||||||
///
|
///
|
||||||
/// The default value for this option is `false`.
|
/// The default value for this option is `false`.
|
||||||
#[serde(default, alias = "rewrite-security-xattrs")]
|
#[serde(default, alias = "rewrite-security-xattrs")]
|
||||||
|
@ -140,21 +140,21 @@ pub struct Config {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub ascii_casefold: bool,
|
pub ascii_casefold: bool,
|
||||||
|
|
||||||
// UIDs which are privileged to perform quota-related operations. We cannot perform a CAP_FOWNER
|
// UIDs which are privileged to perform quota-related operations. We cannot perform a
|
||||||
// check so we consult this list when the VM tries to set the project quota and the process uid
|
// CAP_FOWNER check so we consult this list when the VM tries to set the project quota and
|
||||||
// doesn't match the owner uid. In that case, all uids in this list are treated as if they have
|
// the process uid doesn't match the owner uid. In that case, all uids in this list are
|
||||||
// CAP_FOWNER.
|
// treated as if they have CAP_FOWNER.
|
||||||
#[cfg(feature = "arc_quota")]
|
#[cfg(feature = "arc_quota")]
|
||||||
#[serde(default, deserialize_with = "deserialize_privileged_quota_uids")]
|
#[serde(default, deserialize_with = "deserialize_privileged_quota_uids")]
|
||||||
pub privileged_quota_uids: Vec<libc::uid_t>,
|
pub privileged_quota_uids: Vec<libc::uid_t>,
|
||||||
|
|
||||||
/// Use DAX for shared files.
|
/// Use DAX for shared files.
|
||||||
///
|
///
|
||||||
/// Enabling DAX can improve performance for frequently accessed files by mapping regions of the
|
/// Enabling DAX can improve performance for frequently accessed files by mapping regions of
|
||||||
/// file directly into the VM's memory region, allowing direct access with the cost of slightly
|
/// the file directly into the VM's memory region, allowing direct access with the cost of
|
||||||
/// increased latency the first time the file is accessed. Additionally, since the mapping is
|
/// slightly increased latency the first time the file is accessed. Additionally, since the
|
||||||
/// shared directly from the host kernel's file cache, enabling DAX can improve performance even
|
/// mapping is shared directly from the host kernel's file cache, enabling DAX can improve
|
||||||
/// when the cache policy is `Never`.
|
/// performance even when the cache policy is `Never`.
|
||||||
///
|
///
|
||||||
/// The default value for this option is `false`.
|
/// The default value for this option is `false`.
|
||||||
#[serde(default, alias = "dax")]
|
#[serde(default, alias = "dax")]
|
||||||
|
|
|
@ -162,10 +162,10 @@ ioctl_iowr_nr!(FS_IOC_GET_ENCRYPTION_POLICY_EX, 'f' as u32, 22, [u8; 9]);
|
||||||
#[derive(Clone, Copy, AsBytes, FromZeroes, FromBytes)]
|
#[derive(Clone, Copy, AsBytes, FromZeroes, FromBytes)]
|
||||||
struct fsxattr {
|
struct fsxattr {
|
||||||
fsx_xflags: u32, /* xflags field value (get/set) */
|
fsx_xflags: u32, /* xflags field value (get/set) */
|
||||||
fsx_extsize: u32, /* extsize field value (get/set)*/
|
fsx_extsize: u32, /* extsize field value (get/set) */
|
||||||
fsx_nextents: u32, /* nextents field value (get) */
|
fsx_nextents: u32, /* nextents field value (get) */
|
||||||
fsx_projid: u32, /* project identifier (get/set) */
|
fsx_projid: u32, /* project identifier (get/set) */
|
||||||
fsx_cowextsize: u32, /* CoW extsize field value (get/set)*/
|
fsx_cowextsize: u32, /* CoW extsize field value (get/set) */
|
||||||
fsx_pad: [u8; 8],
|
fsx_pad: [u8; 8],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1506,9 +1506,10 @@ impl PassthroughFs {
|
||||||
let data = self.find_handle(handle, inode)?;
|
let data = self.find_handle(handle, inode)?;
|
||||||
|
|
||||||
{
|
{
|
||||||
// We can't enable verity while holding a writable fd. We don't know whether the file
|
// We can't enable verity while holding a writable fd. We don't know whether the
|
||||||
// was opened for writing so check it here. We don't expect this to be a frequent
|
// file was opened for writing so check it here. We don't expect
|
||||||
// operation so the extra latency should be fine.
|
// this to be a frequent operation so the extra latency should be
|
||||||
|
// fine.
|
||||||
let mut file = data.file.lock();
|
let mut file = data.file.lock();
|
||||||
let flags = FileFlags::from_file(&*file).map_err(io::Error::from)?;
|
let flags = FileFlags::from_file(&*file).map_err(io::Error::from)?;
|
||||||
match flags {
|
match flags {
|
||||||
|
@ -3712,7 +3713,8 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// atomic_open with flag O_RDWR | O_CREATE | O_EXCL, should return positive dentry and file handler
|
// atomic_open with flag O_RDWR | O_CREATE | O_EXCL, should return positive dentry and file
|
||||||
|
// handler
|
||||||
let res = atomic_open(
|
let res = atomic_open(
|
||||||
&fs,
|
&fs,
|
||||||
&temp_dir.path().join("dir/c.txt"),
|
&temp_dir.path().join("dir/c.txt"),
|
||||||
|
|
|
@ -1182,9 +1182,9 @@ pub struct Gpu {
|
||||||
udmabuf: bool,
|
udmabuf: bool,
|
||||||
rutabaga_server_descriptor: Option<SafeDescriptor>,
|
rutabaga_server_descriptor: Option<SafeDescriptor>,
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
/// Because the Windows GpuDisplay can't expose an epollfd, it has to inform the GPU worker which
|
/// Because the Windows GpuDisplay can't expose an epollfd, it has to inform the GPU worker
|
||||||
/// descriptors to add to its wait context. That's what this Tube is used for (it is provided
|
/// which descriptors to add to its wait context. That's what this Tube is used for (it is
|
||||||
/// to each display backend.
|
/// provided to each display backend.
|
||||||
gpu_display_wait_descriptor_ctrl_wr: SendTube,
|
gpu_display_wait_descriptor_ctrl_wr: SendTube,
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
/// The GPU worker uses this Tube to receive the descriptors that should be added to its wait
|
/// The GPU worker uses this Tube to receive the descriptors that should be added to its wait
|
||||||
|
|
|
@ -548,7 +548,8 @@ impl VirtioGpu {
|
||||||
&self.display
|
&self.display
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the list of supported display resolutions as a slice of `(width, height, enabled)` tuples.
|
/// Gets the list of supported display resolutions as a slice of `(width, height, enabled)`
|
||||||
|
/// tuples.
|
||||||
pub fn display_info(&self) -> Vec<(u32, u32, bool)> {
|
pub fn display_info(&self) -> Vec<(u32, u32, bool)> {
|
||||||
(0..VIRTIO_GPU_MAX_SCANOUTS)
|
(0..VIRTIO_GPU_MAX_SCANOUTS)
|
||||||
.map(|scanout_id| scanout_id as u32)
|
.map(|scanout_id| scanout_id as u32)
|
||||||
|
|
|
@ -64,7 +64,8 @@ pub fn new_keyboard_config(idx: u32) -> VirtioInputConfig {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Instantiates a VirtioInputConfig object with the default configuration for a collection of switches.
|
/// Instantiates a VirtioInputConfig object with the default configuration for a collection of
|
||||||
|
/// switches.
|
||||||
pub fn new_switches_config(idx: u32) -> VirtioInputConfig {
|
pub fn new_switches_config(idx: u32) -> VirtioInputConfig {
|
||||||
VirtioInputConfig::new(
|
VirtioInputConfig::new(
|
||||||
virtio_input_device_ids::new(0, 0, 0, 0),
|
virtio_input_device_ids::new(0, 0, 0, 0),
|
||||||
|
@ -76,7 +77,8 @@ pub fn new_switches_config(idx: u32) -> VirtioInputConfig {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Instantiates a VirtioInputConfig object with the default configuration for a collection of rotary.
|
/// Instantiates a VirtioInputConfig object with the default configuration for a collection of
|
||||||
|
/// rotary.
|
||||||
pub fn new_rotary_config(idx: u32) -> VirtioInputConfig {
|
pub fn new_rotary_config(idx: u32) -> VirtioInputConfig {
|
||||||
VirtioInputConfig::new(
|
VirtioInputConfig::new(
|
||||||
virtio_input_device_ids::new(0, 0, 0, 0),
|
virtio_input_device_ids::new(0, 0, 0, 0),
|
||||||
|
|
|
@ -238,8 +238,8 @@ pub fn abs_info<T: AsRawDescriptor>(descriptor: &T) -> BTreeMap<u16, virtio_inpu
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Grabs an event device (see EVIOCGGRAB ioctl for details). After this function succeeds the given
|
/// Grabs an event device (see EVIOCGGRAB ioctl for details). After this function succeeds the given
|
||||||
/// descriptor has exclusive access to the device, effectively making it unusable for any other process in
|
/// descriptor has exclusive access to the device, effectively making it unusable for any other
|
||||||
/// the host.
|
/// process in the host.
|
||||||
pub fn grab_evdev<T: AsRawDescriptor>(descriptor: &mut T) -> Result<()> {
|
pub fn grab_evdev<T: AsRawDescriptor>(descriptor: &mut T) -> Result<()> {
|
||||||
let val: u32 = 1;
|
let val: u32 = 1;
|
||||||
let ret = {
|
let ret = {
|
||||||
|
|
|
@ -214,9 +214,9 @@ impl virtio_input_bitmap {
|
||||||
if byte_pos < ret.len() {
|
if byte_pos < ret.len() {
|
||||||
ret.bitmap[byte_pos] |= bit_byte;
|
ret.bitmap[byte_pos] |= bit_byte;
|
||||||
} else {
|
} else {
|
||||||
// This would only happen if new event codes (or types, or ABS_*, etc) are defined to be
|
// This would only happen if new event codes (or types, or ABS_*, etc) are defined
|
||||||
// larger than or equal to 1024, in which case a new version of the virtio input
|
// to be larger than or equal to 1024, in which case a new version
|
||||||
// protocol needs to be defined.
|
// of the virtio input protocol needs to be defined.
|
||||||
// There is nothing we can do about this error except log it.
|
// There is nothing we can do about this error except log it.
|
||||||
error!("Attempted to set an out of bounds bit: {}", idx);
|
error!("Attempted to set an out of bounds bit: {}", idx);
|
||||||
}
|
}
|
||||||
|
|
|
@ -411,7 +411,8 @@ impl State {
|
||||||
|
|
||||||
let vfio_map_result = match dmabuf_map {
|
let vfio_map_result = match dmabuf_map {
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// Safe because [dmabuf_map, dmabuf_map + size) refers to an external mmap'ed region.
|
// Safe because [dmabuf_map, dmabuf_map + size) refers to an external mmap'ed
|
||||||
|
// region.
|
||||||
Some(dmabuf_map) => unsafe {
|
Some(dmabuf_map) => unsafe {
|
||||||
mapper
|
mapper
|
||||||
.1
|
.1
|
||||||
|
|
|
@ -169,9 +169,8 @@ pub struct CreateIpcMapperRet {
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `endpoint_id` - For the remote iommu to identify the device/ipc mapper.
|
/// * `endpoint_id` - For the remote iommu to identify the device/ipc mapper.
|
||||||
/// * `request_tx` - A tube to send `TranslateRequest` to a remote iommu. This
|
/// * `request_tx` - A tube to send `TranslateRequest` to a remote iommu. This should be cloned and
|
||||||
/// should be cloned and shared between different ipc mappers
|
/// shared between different ipc mappers with different `endpoint_id`s.
|
||||||
/// with different `endpoint_id`s.
|
|
||||||
pub fn create_ipc_mapper(endpoint_id: u32, request_tx: Tube) -> CreateIpcMapperRet {
|
pub fn create_ipc_mapper(endpoint_id: u32, request_tx: Tube) -> CreateIpcMapperRet {
|
||||||
let (response_tx, response_rx) = Tube::pair().expect("failed to create tube pair");
|
let (response_tx, response_rx) = Tube::pair().expect("failed to create tube pair");
|
||||||
CreateIpcMapperRet {
|
CreateIpcMapperRet {
|
||||||
|
|
|
@ -155,26 +155,24 @@ pub enum AddMapResult {
|
||||||
/// the virtio request that triggered the fault.
|
/// the virtio request that triggered the fault.
|
||||||
///
|
///
|
||||||
/// As such, the flow of a fault is:
|
/// As such, the flow of a fault is:
|
||||||
/// 1) The guest sends an virtio-iommu message that triggers a fault. Faults can
|
/// 1) The guest sends an virtio-iommu message that triggers a fault. Faults can be triggered by
|
||||||
/// be triggered by unmap or detach messages, or by attach messages if such
|
/// unmap or detach messages, or by attach messages if such messages are re-attaching an
|
||||||
/// messages are re-attaching an endpoint to a new domain. One example of
|
/// endpoint to a new domain. One example of a guest event that can trigger such a message is a
|
||||||
/// a guest event that can trigger such a message is a userspace VVU device
|
/// userspace VVU device process crashing and triggering the guest kernel to re-attach the VVU
|
||||||
/// process crashing and triggering the guest kernel to re-attach the VVU
|
|
||||||
/// device to the null endpoint.
|
/// device to the null endpoint.
|
||||||
/// 2) The viommu device removes an exported mapping from the mapper.
|
/// 2) The viommu device removes an exported mapping from the mapper.
|
||||||
/// 3) The mapper signals the IOMMU fault eventfd and returns the fault
|
/// 3) The mapper signals the IOMMU fault eventfd and returns the fault resolution event to the
|
||||||
/// resolution event to the viommu device.
|
/// viommu device.
|
||||||
/// 4) The viommu device starts waiting on the fault resolution event. Note that
|
/// 4) The viommu device starts waiting on the fault resolution event. Note that although the
|
||||||
/// although the viommu device and mapper are both running on the same
|
/// viommu device and mapper are both running on the same executor, this wait is async. This
|
||||||
/// executor, this wait is async. This means that although further processing
|
/// means that although further processing of virtio-iommu requests is paused, the mapper
|
||||||
/// of virtio-iommu requests is paused, the mapper continues to run.
|
/// continues to run.
|
||||||
/// 5) The client receives the IOMMU fault.
|
/// 5) The client receives the IOMMU fault.
|
||||||
/// 6) The client releases all exported regions.
|
/// 6) The client releases all exported regions.
|
||||||
/// 7) Once the mapper receives the final release message from the client,
|
/// 7) Once the mapper receives the final release message from the client, it signals the fault
|
||||||
/// it signals the fault resolution event that the viommu device is
|
/// resolution event that the viommu device is waiting on.
|
||||||
/// waiting on.
|
/// 8) The viommu device finishes processing the original virtio iommu request and sends a reply to
|
||||||
/// 8) The viommu device finishes processing the original virtio iommu
|
/// the guest.
|
||||||
/// request and sends a reply to the guest.
|
|
||||||
pub trait MemoryMapper: Send {
|
pub trait MemoryMapper: Send {
|
||||||
/// Creates a new mapping. If the mapping overlaps with an existing
|
/// Creates a new mapping. If the mapping overlaps with an existing
|
||||||
/// mapping, return Ok(false).
|
/// mapping, return Ok(false).
|
||||||
|
|
|
@ -118,10 +118,10 @@ impl MemoryMapper for VfioWrapper {
|
||||||
fn supports_detach(&self) -> bool {
|
fn supports_detach(&self) -> bool {
|
||||||
// A few reasons why we don't support detach:
|
// A few reasons why we don't support detach:
|
||||||
//
|
//
|
||||||
// 1. Seems it's not possible to dynamically attach and detach a IOMMU domain if the
|
// 1. Seems it's not possible to dynamically attach and detach a IOMMU domain if the virtio
|
||||||
// virtio IOMMU device is running on top of VFIO
|
// IOMMU device is running on top of VFIO
|
||||||
// 2. Even if VIRTIO_IOMMU_T_DETACH is implemented in front-end driver, it could violate
|
// 2. Even if VIRTIO_IOMMU_T_DETACH is implemented in front-end driver, it could violate the
|
||||||
// the following virtio IOMMU spec: Detach an endpoint from a domain. when this request
|
// following virtio IOMMU spec: Detach an endpoint from a domain. when this request
|
||||||
// completes, the endpoint cannot access any mapping from that domain anymore.
|
// completes, the endpoint cannot access any mapping from that domain anymore.
|
||||||
//
|
//
|
||||||
// This is because VFIO doesn't support detaching a single device. When the virtio-iommu
|
// This is because VFIO doesn't support detaching a single device. When the virtio-iommu
|
||||||
|
|
|
@ -452,8 +452,8 @@ impl PvClockWorker {
|
||||||
(
|
(
|
||||||
Self::get_suspended_duration(&suspend_time),
|
Self::get_suspended_duration(&suspend_time),
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// Safe because _rdtsc takes no arguments, and we trust _rdtsc to not modify any
|
// Safe because _rdtsc takes no arguments, and we trust _rdtsc to not modify
|
||||||
// other memory.
|
// any other memory.
|
||||||
unsafe { _rdtsc() } - suspend_time.tsc_value,
|
unsafe { _rdtsc() } - suspend_time.tsc_value,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -49,8 +49,8 @@ pub struct QueueConfig {
|
||||||
max_size: u16,
|
max_size: u16,
|
||||||
|
|
||||||
/// The queue size in elements the driver selected. This is always guaranteed to be a power of
|
/// The queue size in elements the driver selected. This is always guaranteed to be a power of
|
||||||
/// two less than or equal to `max_size`, as required for split virtqueues. These invariants are
|
/// two less than or equal to `max_size`, as required for split virtqueues. These invariants
|
||||||
/// enforced by `set_size()`.
|
/// are enforced by `set_size()`.
|
||||||
size: u16,
|
size: u16,
|
||||||
|
|
||||||
/// Indicates if the queue is finished with configuration
|
/// Indicates if the queue is finished with configuration
|
||||||
|
@ -59,7 +59,8 @@ pub struct QueueConfig {
|
||||||
/// MSI-X vector for the queue. Don't care for INTx
|
/// MSI-X vector for the queue. Don't care for INTx
|
||||||
vector: u16,
|
vector: u16,
|
||||||
|
|
||||||
/// Ring features (e.g. `VIRTIO_RING_F_EVENT_IDX`, `VIRTIO_F_RING_PACKED`) offered by the device
|
/// Ring features (e.g. `VIRTIO_RING_F_EVENT_IDX`, `VIRTIO_F_RING_PACKED`) offered by the
|
||||||
|
/// device
|
||||||
features: u64,
|
features: u64,
|
||||||
|
|
||||||
// Device feature bits accepted by the driver
|
// Device feature bits accepted by the driver
|
||||||
|
@ -267,7 +268,8 @@ impl QueueConfig {
|
||||||
if self.activated {
|
if self.activated {
|
||||||
bail!("queue is already activated");
|
bail!("queue is already activated");
|
||||||
}
|
}
|
||||||
// If VIRTIO_F_RING_PACKED feature bit is set, create a packed queue, otherwise create a split queue
|
// If VIRTIO_F_RING_PACKED feature bit is set, create a packed queue, otherwise create a
|
||||||
|
// split queue
|
||||||
let queue: Queue = if ((self.acked_features >> VIRTIO_F_RING_PACKED) & 1) != 0 {
|
let queue: Queue = if ((self.acked_features >> VIRTIO_F_RING_PACKED) & 1) != 0 {
|
||||||
let pq =
|
let pq =
|
||||||
PackedQueue::new(self, mem, event).context("Failed to create a packed queue.")?;
|
PackedQueue::new(self, mem, event).context("Failed to create a packed queue.")?;
|
||||||
|
@ -335,13 +337,15 @@ impl QueueConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Usage: define_queue_method!(method_name, return_type[, mut][, arg1: arg1_type, arg2: arg2_type, ...])
|
/// Usage: define_queue_method!(method_name, return_type[, mut][, arg1: arg1_type, arg2: arg2_type,
|
||||||
|
/// ...])
|
||||||
///
|
///
|
||||||
/// - `method_name`: The name of the method to be defined (as an identifier).
|
/// - `method_name`: The name of the method to be defined (as an identifier).
|
||||||
/// - `return_type`: The return type of the method.
|
/// - `return_type`: The return type of the method.
|
||||||
/// - `mut` (optional): Include this keyword if the method requires a mutable reference to `self` (`&mut self`).
|
/// - `mut` (optional): Include this keyword if the method requires a mutable reference to `self`
|
||||||
/// - `arg1: arg1_type, arg2: arg2_type, ...` (optional): Include method parameters as a comma-separated list
|
/// (`&mut self`).
|
||||||
/// of `name: type` pairs, if the method takes any arguments.
|
/// - `arg1: arg1_type, arg2: arg2_type, ...` (optional): Include method parameters as a
|
||||||
|
/// comma-separated list of `name: type` pairs, if the method takes any arguments.
|
||||||
macro_rules! define_queue_method {
|
macro_rules! define_queue_method {
|
||||||
(
|
(
|
||||||
$(#[$doc:meta])*
|
$(#[$doc:meta])*
|
||||||
|
|
|
@ -212,8 +212,8 @@ impl PackedQueue {
|
||||||
|
|
||||||
/// Set the device event suppression
|
/// Set the device event suppression
|
||||||
///
|
///
|
||||||
// This field is used to specify the timing of when the driver notifies the
|
/// This field is used to specify the timing of when the driver notifies the
|
||||||
// device that the descriptor table is ready to be processed.
|
/// device that the descriptor table is ready to be processed.
|
||||||
fn set_avail_event(&mut self, event: PackedDescEvent) {
|
fn set_avail_event(&mut self, event: PackedDescEvent) {
|
||||||
fence(Ordering::SeqCst);
|
fence(Ordering::SeqCst);
|
||||||
self.mem
|
self.mem
|
||||||
|
|
|
@ -345,11 +345,11 @@ impl SplitQueue {
|
||||||
/// (similar to how DC circuits are analyzed).
|
/// (similar to how DC circuits are analyzed).
|
||||||
///
|
///
|
||||||
/// The two distances are as follows:
|
/// The two distances are as follows:
|
||||||
/// * `A` is the distance between the driver's requested notification
|
/// * `A` is the distance between the driver's requested notification point, and the current
|
||||||
/// point, and the current position in the ring.
|
/// position in the ring.
|
||||||
///
|
///
|
||||||
/// * `B` is the distance between the last time we notified the guest,
|
/// * `B` is the distance between the last time we notified the guest, and the current position
|
||||||
/// and the current position in the ring.
|
/// in the ring.
|
||||||
///
|
///
|
||||||
/// If we graph these distances for the situation where we want to notify
|
/// If we graph these distances for the situation where we want to notify
|
||||||
/// the guest, and when we don't want to notify the guest, we see that
|
/// the guest, and when we don't want to notify the guest, we see that
|
||||||
|
@ -389,15 +389,13 @@ impl SplitQueue {
|
||||||
/// anymore. (Notifications will never be sent.) But why is that? The algebra
|
/// anymore. (Notifications will never be sent.) But why is that? The algebra
|
||||||
/// here *appears* to work out, but all semantic meaning is lost. There are
|
/// here *appears* to work out, but all semantic meaning is lost. There are
|
||||||
/// two explanations for why this happens:
|
/// two explanations for why this happens:
|
||||||
/// * The intuitive one: the terms in the inequality are not actually
|
/// * The intuitive one: the terms in the inequality are not actually separable; in other words,
|
||||||
/// separable; in other words, (next_used - last_used) is an inseparable
|
/// (next_used - last_used) is an inseparable term, so subtracting next_used from both sides
|
||||||
/// term, so subtracting next_used from both sides of the original
|
/// of the original inequality and zeroing them out is semantically invalid. But why aren't
|
||||||
/// inequality and zeroing them out is semantically invalid. But why aren't
|
|
||||||
/// they separable? See below.
|
/// they separable? See below.
|
||||||
/// * The theoretical one: canceling like terms relies a vector space law:
|
/// * The theoretical one: canceling like terms relies a vector space law: a + x = b + x => a =
|
||||||
/// a + x = b + x => a = b (cancellation law). For congruences / equality
|
/// b (cancellation law). For congruences / equality under modulo, this law is satisfied, but
|
||||||
/// under modulo, this law is satisfied, but for inequalities under mod, it
|
/// for inequalities under mod, it is not; therefore, we cannot cancel like terms.
|
||||||
/// is not; therefore, we cannot cancel like terms.
|
|
||||||
///
|
///
|
||||||
/// ```text
|
/// ```text
|
||||||
/// ┌──────────────────────────────────┐
|
/// ┌──────────────────────────────────┐
|
||||||
|
|
|
@ -374,7 +374,8 @@ impl Inquiry {
|
||||||
0xb2 => {
|
0xb2 => {
|
||||||
// Page length
|
// Page length
|
||||||
outbuf[3] = 4;
|
outbuf[3] = 4;
|
||||||
// skip outbuf[4]: crosvm does not support logical block provisioning threshold sets.
|
// skip outbuf[4]: crosvm does not support logical block provisioning threshold
|
||||||
|
// sets.
|
||||||
const UNMAP: u8 = 1 << 7;
|
const UNMAP: u8 = 1 << 7;
|
||||||
const WRITE_SAME_16: u8 = 1 << 6;
|
const WRITE_SAME_16: u8 = 1 << 6;
|
||||||
const WRITE_SAME_10: u8 = 1 << 5;
|
const WRITE_SAME_10: u8 = 1 << 5;
|
||||||
|
|
|
@ -727,11 +727,12 @@ async fn notify_reset_signal(reset_signal: &(AsyncRwLock<bool>, Condvar)) {
|
||||||
|
|
||||||
/// Runs all workers once and exit if any worker exit.
|
/// Runs all workers once and exit if any worker exit.
|
||||||
///
|
///
|
||||||
/// Returns [`LoopState::Break`] if the worker `f_kill` or `f_resample` exit, or something went wrong
|
/// Returns [`LoopState::Break`] if the worker `f_kill` or `f_resample` exit, or something went
|
||||||
/// on shutdown process. The caller should not run the worker again and should exit the main loop.
|
/// wrong on shutdown process. The caller should not run the worker again and should exit the main
|
||||||
|
/// loop.
|
||||||
///
|
///
|
||||||
/// If this function returns [`LoopState::Continue`], the caller can continue the main loop by resetting
|
/// If this function returns [`LoopState::Continue`], the caller can continue the main loop by
|
||||||
/// the streams and run the worker again.
|
/// resetting the streams and run the worker again.
|
||||||
fn run_worker_once(
|
fn run_worker_once(
|
||||||
ex: &Executor,
|
ex: &Executor,
|
||||||
streams: &Rc<AsyncRwLock<Vec<AsyncRwLock<StreamInfo>>>>,
|
streams: &Rc<AsyncRwLock<Vec<AsyncRwLock<StreamInfo>>>>,
|
||||||
|
|
|
@ -56,7 +56,8 @@ pub struct StreamInfoBuilder {
|
||||||
impl StreamInfoBuilder {
|
impl StreamInfoBuilder {
|
||||||
/// Creates a StreamInfoBuilder with minimal required fields:
|
/// Creates a StreamInfoBuilder with minimal required fields:
|
||||||
///
|
///
|
||||||
/// * `stream_source_generator`: Generator which generates stream source in [`StreamInfo::prepare()`].
|
/// * `stream_source_generator`: Generator which generates stream source in
|
||||||
|
/// [`StreamInfo::prepare()`].
|
||||||
pub fn new(stream_source_generator: Arc<SysAudioStreamSourceGenerator>) -> Self {
|
pub fn new(stream_source_generator: Arc<SysAudioStreamSourceGenerator>) -> Self {
|
||||||
StreamInfoBuilder {
|
StreamInfoBuilder {
|
||||||
stream_source_generator,
|
stream_source_generator,
|
||||||
|
@ -64,8 +65,8 @@ impl StreamInfoBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the [`StreamEffect`]s to use when creating a stream from the stream source in [`StreamInfo::prepare()`].
|
/// Set the [`StreamEffect`]s to use when creating a stream from the stream source in
|
||||||
/// The default value is no effects.
|
/// [`StreamInfo::prepare()`]. The default value is no effects.
|
||||||
pub fn effects(mut self, effects: Vec<StreamEffect>) -> Self {
|
pub fn effects(mut self, effects: Vec<StreamEffect>) -> Self {
|
||||||
self.effects = effects;
|
self.effects = effects;
|
||||||
self
|
self
|
||||||
|
@ -392,7 +393,8 @@ impl StreamInfo {
|
||||||
buffer_bytes: self.buffer_bytes,
|
buffer_bytes: self.buffer_bytes,
|
||||||
period_bytes: self.period_bytes,
|
period_bytes: self.period_bytes,
|
||||||
direction: self.direction, // VIRTIO_SND_D_*
|
direction: self.direction, // VIRTIO_SND_D_*
|
||||||
state: self.state, // VIRTIO_SND_R_PCM_SET_PARAMS -> VIRTIO_SND_R_PCM_STOP, or 0 (uninitialized)
|
// VIRTIO_SND_R_PCM_SET_PARAMS -> VIRTIO_SND_R_PCM_STOP, or 0 (uninitialized)
|
||||||
|
state: self.state,
|
||||||
effects: self.effects.clone(),
|
effects: self.effects.clone(),
|
||||||
just_reset: self.just_reset,
|
just_reset: self.just_reset,
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,8 @@ pub enum Error {
|
||||||
/// Failed to parse parameters.
|
/// Failed to parse parameters.
|
||||||
#[error("Invalid snd parameter: {0}")]
|
#[error("Invalid snd parameter: {0}")]
|
||||||
UnknownParameter(String),
|
UnknownParameter(String),
|
||||||
/// Invalid PCM device config index. Happens when the length of PCM device config is less than the number of PCM devices.
|
/// Invalid PCM device config index. Happens when the length of PCM device config is less than
|
||||||
|
/// the number of PCM devices.
|
||||||
#[error("Invalid PCM device config index: {0}")]
|
#[error("Invalid PCM device config index: {0}")]
|
||||||
InvalidPCMDeviceConfigIndex(usize),
|
InvalidPCMDeviceConfigIndex(usize),
|
||||||
/// Invalid PCM info direction (VIRTIO_SND_D_OUTPUT = 0, VIRTIO_SND_D_INPUT = 1)
|
/// Invalid PCM info direction (VIRTIO_SND_D_OUTPUT = 0, VIRTIO_SND_D_INPUT = 1)
|
||||||
|
|
|
@ -115,9 +115,10 @@ impl StreamInfo {
|
||||||
self.channels as usize,
|
self.channels as usize,
|
||||||
self.format,
|
self.format,
|
||||||
self.frame_rate as usize,
|
self.frame_rate as usize,
|
||||||
// `buffer_size` in `audio_streams` API indicates the buffer size in bytes that the stream
|
// `buffer_size` in `audio_streams` API indicates the buffer size in bytes that the
|
||||||
// consumes (or transmits) each time (next_playback/capture_buffer).
|
// stream consumes (or transmits) each time
|
||||||
// `period_bytes` in virtio-snd device (or ALSA) indicates the device transmits (or
|
// (next_playback/capture_buffer). `period_bytes` in virtio-snd
|
||||||
|
// device (or ALSA) indicates the device transmits (or
|
||||||
// consumes) for each PCM message.
|
// consumes) for each PCM message.
|
||||||
// Therefore, `buffer_size` in `audio_streams` == `period_bytes` in virtio-snd.
|
// Therefore, `buffer_size` in `audio_streams` == `period_bytes` in virtio-snd.
|
||||||
self.period_bytes / frame_size,
|
self.period_bytes / frame_size,
|
||||||
|
|
|
@ -344,8 +344,8 @@ impl Stream {
|
||||||
|
|
||||||
impl Drop for Stream {
|
impl Drop for Stream {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// Try to stop and release the stream in case it was playing, these operations will fail if the
|
// Try to stop and release the stream in case it was playing, these operations will fail if
|
||||||
// stream is already released, just ignore that failure
|
// the stream is already released, just ignore that failure
|
||||||
let _ = self.vios_client.lock().stop_stream(self.stream_id);
|
let _ = self.vios_client.lock().stop_stream(self.stream_id);
|
||||||
let _ = self.vios_client.lock().release_stream(self.stream_id);
|
let _ = self.vios_client.lock().release_stream(self.stream_id);
|
||||||
|
|
||||||
|
|
|
@ -252,7 +252,8 @@ impl Worker {
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
VIRTIO_SND_S_OK,
|
VIRTIO_SND_S_OK,
|
||||||
// Safe to unwrap because we just ensured all the ids are valid
|
// Safe to unwrap because we just ensured all the ids are
|
||||||
|
// valid
|
||||||
(start_id..end_id)
|
(start_id..end_id)
|
||||||
.map(|id| {
|
.map(|id| {
|
||||||
self.vios_client.lock().jack_info(id).unwrap()
|
self.vios_client.lock().jack_info(id).unwrap()
|
||||||
|
@ -319,7 +320,8 @@ impl Worker {
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
VIRTIO_SND_S_OK,
|
VIRTIO_SND_S_OK,
|
||||||
// Safe to unwrap because we just ensured all the ids are valid
|
// Safe to unwrap because we just ensured all the ids are
|
||||||
|
// valid
|
||||||
(start_id..end_id)
|
(start_id..end_id)
|
||||||
.map(|id| {
|
.map(|id| {
|
||||||
self.vios_client.lock().chmap_info(id).unwrap()
|
self.vios_client.lock().chmap_info(id).unwrap()
|
||||||
|
@ -348,7 +350,8 @@ impl Worker {
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
VIRTIO_SND_S_OK,
|
VIRTIO_SND_S_OK,
|
||||||
// Safe to unwrap because we just ensured all the ids are valid
|
// Safe to unwrap because we just ensured all the ids are
|
||||||
|
// valid
|
||||||
(start_id..end_id)
|
(start_id..end_id)
|
||||||
.map(|id| {
|
.map(|id| {
|
||||||
self.vios_client.lock().stream_info(id).unwrap()
|
self.vios_client.lock().stream_info(id).unwrap()
|
||||||
|
|
|
@ -231,7 +231,8 @@ fn create_vu_multi_port_device(
|
||||||
let port = x
|
let port = x
|
||||||
.create_serial_device::<ConsolePort>(
|
.create_serial_device::<ConsolePort>(
|
||||||
ProtectionType::Unprotected,
|
ProtectionType::Unprotected,
|
||||||
// We need to pass an event as per Serial Device API but we don't really use it anyway.
|
// We need to pass an event as per Serial Device API but we don't really use it
|
||||||
|
// anyway.
|
||||||
&Event::new()?,
|
&Event::new()?,
|
||||||
keep_rds,
|
keep_rds,
|
||||||
)
|
)
|
||||||
|
|
|
@ -281,13 +281,13 @@ pub fn run_gpu_device(opts: Options) -> anyhow::Result<()> {
|
||||||
let gpu = Rc::new(RefCell::new(Gpu::new(
|
let gpu = Rc::new(RefCell::new(Gpu::new(
|
||||||
config.exit_evt_wrtube,
|
config.exit_evt_wrtube,
|
||||||
config.gpu_control_device_tube,
|
config.gpu_control_device_tube,
|
||||||
/*resource_bridges=*/ Vec::new(),
|
/* resource_bridges= */ Vec::new(),
|
||||||
display_backends,
|
display_backends,
|
||||||
&gpu_params,
|
&gpu_params,
|
||||||
/*render_server_descriptor*/ None,
|
/* render_server_descriptor */ None,
|
||||||
input_event_backend_config.event_devices,
|
input_event_backend_config.event_devices,
|
||||||
base_features,
|
base_features,
|
||||||
/*channels=*/ &Default::default(),
|
/* channels= */ &Default::default(),
|
||||||
wndproc_thread,
|
wndproc_thread,
|
||||||
)));
|
)));
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,6 @@
|
||||||
//! Ok(())
|
//! Ok(())
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
|
||||||
// Implementation note:
|
// Implementation note:
|
||||||
// This code lets us take advantage of the vmm_vhost low level implementation of the vhost user
|
// This code lets us take advantage of the vmm_vhost low level implementation of the vhost user
|
||||||
// protocol. DeviceRequestHandler implements the VhostUserSlaveReqHandler trait from vmm_vhost,
|
// protocol. DeviceRequestHandler implements the VhostUserSlaveReqHandler trait from vmm_vhost,
|
||||||
|
|
|
@ -33,7 +33,8 @@ pub fn read_from_tube_transporter(
|
||||||
let tube_transporter = TubeTransporterReader::create_tube_transporter_reader(
|
let tube_transporter = TubeTransporterReader::create_tube_transporter_reader(
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// Safe because we know that raw_transport_tube is valid (passed by inheritance), and that
|
// Safe because we know that raw_transport_tube is valid (passed by inheritance), and that
|
||||||
// the blocking & framing modes are accurate because we create them ourselves in the broker.
|
// the blocking & framing modes are accurate because we create them ourselves in the
|
||||||
|
// broker.
|
||||||
unsafe {
|
unsafe {
|
||||||
PipeConnection::from_raw_descriptor(
|
PipeConnection::from_raw_descriptor(
|
||||||
raw_transport_tube,
|
raw_transport_tube,
|
||||||
|
|
|
@ -74,7 +74,8 @@ pub trait VhostUserDevice {
|
||||||
ex: &Executor,
|
ex: &Executor,
|
||||||
) -> anyhow::Result<Box<dyn VhostUserSlaveReqHandler>>;
|
) -> anyhow::Result<Box<dyn VhostUserSlaveReqHandler>>;
|
||||||
|
|
||||||
/// The preferred ExecutorKind of an Executor to accept by [`VhostUserDevice::into_req_handler()`].
|
/// The preferred ExecutorKind of an Executor to accept by
|
||||||
|
/// [`VhostUserDevice::into_req_handler()`].
|
||||||
fn executor_kind(&self) -> Option<ExecutorKind> {
|
fn executor_kind(&self) -> Option<ExecutorKind> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,8 +55,8 @@ struct InputBuffer {
|
||||||
mapping: MemoryMappingArena,
|
mapping: MemoryMappingArena,
|
||||||
/// Resource ID that we will signal using `NotifyEndOfBitstreamBuffer` upon destruction.
|
/// Resource ID that we will signal using `NotifyEndOfBitstreamBuffer` upon destruction.
|
||||||
resource_id: u32,
|
resource_id: u32,
|
||||||
/// Pointer to the event queue to send the `NotifyEndOfBitstreamBuffer` event to. The event will
|
/// Pointer to the event queue to send the `NotifyEndOfBitstreamBuffer` event to. The event
|
||||||
/// not be sent if the pointer becomes invalid.
|
/// will not be sent if the pointer becomes invalid.
|
||||||
event_queue: Weak<SyncEventQueue<DecoderEvent>>,
|
event_queue: Weak<SyncEventQueue<DecoderEvent>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ pub enum Token {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A tag for commands being processed asynchronously in the back-end device.
|
/// A tag for commands being processed asynchronously in the back-end device.
|
||||||
|
///
|
||||||
/// TODO(b/149720783): Remove this enum by using async primitives.
|
/// TODO(b/149720783): Remove this enum by using async primitives.
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)]
|
||||||
pub enum AsyncCmdTag {
|
pub enum AsyncCmdTag {
|
||||||
|
@ -102,11 +103,13 @@ pub enum VideoEvtResponseType {
|
||||||
|
|
||||||
pub trait Device {
|
pub trait Device {
|
||||||
/// Processes a virtio-video command.
|
/// Processes a virtio-video command.
|
||||||
/// If the command expects a synchronous response, it returns a response as `VideoCmdResponseType::Sync`.
|
/// If the command expects a synchronous response, it returns a response as
|
||||||
/// Otherwise, it returns a name of the descriptor chain that will be used when a response is prepared.
|
/// `VideoCmdResponseType::Sync`. Otherwise, it returns a name of the descriptor chain that
|
||||||
/// Implementations of this method is passed a WaitContext object which can be used to add or remove
|
/// will be used when a response is prepared. Implementations of this method is passed a
|
||||||
/// descriptors to wait on. It is expected that only Token::Event items would be added. When a Token::Event
|
/// WaitContext object which can be used to add or remove descriptors to wait on. It is
|
||||||
/// event arrives, process_event() will be invoked.
|
/// expected that only Token::Event items would be added. When a Token::Event event arrives,
|
||||||
|
/// process_event() will be invoked.
|
||||||
|
///
|
||||||
/// TODO(b/149720783): Make this an async function.
|
/// TODO(b/149720783): Make this an async function.
|
||||||
fn process_cmd(
|
fn process_cmd(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -119,8 +122,10 @@ pub trait Device {
|
||||||
|
|
||||||
/// Processes an available `Token::Event` event and returns a list of `VideoEvtResponseType`
|
/// Processes an available `Token::Event` event and returns a list of `VideoEvtResponseType`
|
||||||
/// responses. It returns None if an invalid event comes.
|
/// responses. It returns None if an invalid event comes.
|
||||||
/// For responses to be sent via command queue, the return type is `VideoEvtResponseType::AsyncCmd`.
|
/// For responses to be sent via command queue, the return type is
|
||||||
/// For responses to be sent via event queue, the return type is `VideoEvtResponseType::Event`.
|
/// `VideoEvtResponseType::AsyncCmd`. For responses to be sent via event queue, the return
|
||||||
|
/// type is `VideoEvtResponseType::Event`.
|
||||||
|
///
|
||||||
/// TODO(b/149720783): Make this an async function.
|
/// TODO(b/149720783): Make this an async function.
|
||||||
fn process_event(
|
fn process_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
|
@ -105,9 +105,9 @@ pub struct FfmpegEncoderSession {
|
||||||
output_queue: VecDeque<(OutputBufferId, MemoryMappingArena)>,
|
output_queue: VecDeque<(OutputBufferId, MemoryMappingArena)>,
|
||||||
/// `true` if a flush is pending. While a pending flush exist, input buffers are temporarily
|
/// `true` if a flush is pending. While a pending flush exist, input buffers are temporarily
|
||||||
/// held on and not sent to the encoder. An actual flush call will be issued when we run out of
|
/// held on and not sent to the encoder. An actual flush call will be issued when we run out of
|
||||||
/// output buffers (to defend against FFmpeg bugs), and we'll try to receive outputs again until
|
/// output buffers (to defend against FFmpeg bugs), and we'll try to receive outputs again
|
||||||
/// we receive another code indicating the flush has completed, at which point this flag will
|
/// until we receive another code indicating the flush has completed, at which point this
|
||||||
/// be reset.
|
/// flag will be reset.
|
||||||
is_flushing: bool,
|
is_flushing: bool,
|
||||||
|
|
||||||
/// The libav context for this session.
|
/// The libav context for this session.
|
||||||
|
|
|
@ -121,7 +121,8 @@ impl LibvdaEncoder {
|
||||||
let mut parsed_formats: BTreeMap<Format, ParsedFormat> = BTreeMap::new();
|
let mut parsed_formats: BTreeMap<Format, ParsedFormat> = BTreeMap::new();
|
||||||
|
|
||||||
for output_format in output_formats.iter() {
|
for output_format in output_formats.iter() {
|
||||||
// TODO(alexlau): Consider using `max_framerate_numerator` and `max_framerate_denominator`.
|
// TODO(alexlau): Consider using `max_framerate_numerator` and
|
||||||
|
// `max_framerate_denominator`.
|
||||||
let libvda::encode::OutputProfile {
|
let libvda::encode::OutputProfile {
|
||||||
profile: libvda_profile,
|
profile: libvda_profile,
|
||||||
max_width,
|
max_width,
|
||||||
|
|
|
@ -671,8 +671,8 @@ impl<T: Encoder> EncoderDevice<T> {
|
||||||
GuestResource::from_virtio_object_entry(
|
GuestResource::from_virtio_object_entry(
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// Safe because we confirmed the correct type for the resource.
|
// Safe because we confirmed the correct type for the resource.
|
||||||
// unwrap() is also safe here because we just tested above that `entries` had
|
// unwrap() is also safe here because we just tested above that
|
||||||
// exactly one element.
|
// `entries` had exactly one element.
|
||||||
unsafe { entries.get(0).unwrap().object },
|
unsafe { entries.get(0).unwrap().object },
|
||||||
&self.resource_bridge,
|
&self.resource_bridge,
|
||||||
&stream.src_params,
|
&stream.src_params,
|
||||||
|
@ -721,8 +721,8 @@ impl<T: Encoder> EncoderDevice<T> {
|
||||||
GuestResource::from_virtio_object_entry(
|
GuestResource::from_virtio_object_entry(
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// Safe because we confirmed the correct type for the resource.
|
// Safe because we confirmed the correct type for the resource.
|
||||||
// unwrap() is also safe here because we just tested above that `entries` had
|
// unwrap() is also safe here because we just tested above that
|
||||||
// exactly one element.
|
// `entries` had exactly one element.
|
||||||
unsafe { entries.get(0).unwrap().object },
|
unsafe { entries.get(0).unwrap().object },
|
||||||
&self.resource_bridge,
|
&self.resource_bridge,
|
||||||
&stream.dst_params,
|
&stream.dst_params,
|
||||||
|
@ -866,9 +866,9 @@ impl<T: Encoder> EncoderDevice<T> {
|
||||||
let buffer_size = dst_resource.resource.planes[0].size as u32;
|
let buffer_size = dst_resource.resource.planes[0].size as u32;
|
||||||
|
|
||||||
// Stores an output buffer to notify EOS.
|
// Stores an output buffer to notify EOS.
|
||||||
// This is necessary because libvda is unable to indicate EOS along with returned buffers.
|
// This is necessary because libvda is unable to indicate EOS along with returned
|
||||||
// For now, when a `Flush()` completes, this saved resource will be returned as a zero-sized
|
// buffers. For now, when a `Flush()` completes, this saved resource
|
||||||
// buffer with the EOS flag.
|
// will be returned as a zero-sized buffer with the EOS flag.
|
||||||
if stream.eos_manager.try_reserve_eos_buffer(resource_id) {
|
if stream.eos_manager.try_reserve_eos_buffer(resource_id) {
|
||||||
return Ok(VideoCmdResponseType::Async(AsyncCmdTag::Queue {
|
return Ok(VideoCmdResponseType::Async(AsyncCmdTag::Queue {
|
||||||
stream_id,
|
stream_id,
|
||||||
|
|
|
@ -186,8 +186,8 @@ impl PlaneFormat {
|
||||||
},
|
},
|
||||||
// UV plane, 1 sample per group of 4 pixels for U and V.
|
// UV plane, 1 sample per group of 4 pixels for U and V.
|
||||||
PlaneFormat {
|
PlaneFormat {
|
||||||
// Add one vertical line so odd resolutions result in an extra UV line to cover all the
|
// Add one vertical line so odd resolutions result in an extra UV line to cover
|
||||||
// Y samples.
|
// all the Y samples.
|
||||||
plane_size: width * half_height,
|
plane_size: width * half_height,
|
||||||
stride: width,
|
stride: width,
|
||||||
},
|
},
|
||||||
|
|
|
@ -21,8 +21,8 @@
|
||||||
//! * Derive implementations of AsBytes and FromBytes for each struct as needed.
|
//! * Derive implementations of AsBytes and FromBytes for each struct as needed.
|
||||||
//! * Added GET_PARAMS_EXT and SET_PARAMS_EXT to allow querying and changing the resource type
|
//! * Added GET_PARAMS_EXT and SET_PARAMS_EXT to allow querying and changing the resource type
|
||||||
//! dynamically.
|
//! dynamically.
|
||||||
//! * Moved some definitions such as virtio_video_config to device_constants to make them visible
|
//! * Moved some definitions such as virtio_video_config to device_constants to make them visible to
|
||||||
//! to vhost-user modules, and also pub-use them.
|
//! vhost-user modules, and also pub-use them.
|
||||||
|
|
||||||
#![allow(dead_code, non_snake_case, non_camel_case_types)]
|
#![allow(dead_code, non_snake_case, non_camel_case_types)]
|
||||||
|
|
||||||
|
|
|
@ -181,8 +181,8 @@ impl Worker {
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `device` - Instance of backend device
|
/// * `device` - Instance of backend device
|
||||||
/// * `wait_ctx` - `device` may register a new `Token::Event` for a new stream session
|
/// * `wait_ctx` - `device` may register a new `Token::Event` for a new stream session to
|
||||||
/// to `wait_ctx`
|
/// `wait_ctx`
|
||||||
/// * `desc` - `DescriptorChain` to handle
|
/// * `desc` - `DescriptorChain` to handle
|
||||||
fn handle_command_desc(
|
fn handle_command_desc(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -266,8 +266,8 @@ impl Worker {
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `device` - Instance of backend device
|
/// * `device` - Instance of backend device
|
||||||
/// * `wait_ctx` - `device` may register a new `Token::Event` for a new stream session
|
/// * `wait_ctx` - `device` may register a new `Token::Event` for a new stream session to
|
||||||
/// to `wait_ctx`
|
/// `wait_ctx`
|
||||||
fn handle_command_queue(
|
fn handle_command_queue(
|
||||||
&mut self,
|
&mut self,
|
||||||
device: &mut dyn Device,
|
device: &mut dyn Device,
|
||||||
|
@ -286,8 +286,8 @@ impl Worker {
|
||||||
///
|
///
|
||||||
/// * `device` - Instance of backend device
|
/// * `device` - Instance of backend device
|
||||||
/// * `stream_id` - Stream session ID of the event
|
/// * `stream_id` - Stream session ID of the event
|
||||||
/// * `wait_ctx` - `device` may register a new `Token::Buffer` for a new stream session
|
/// * `wait_ctx` - `device` may register a new `Token::Buffer` for a new stream session to
|
||||||
/// to `wait_ctx`
|
/// `wait_ctx`
|
||||||
fn handle_event(
|
fn handle_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
device: &mut dyn Device,
|
device: &mut dyn Device,
|
||||||
|
|
|
@ -398,9 +398,9 @@ struct VsockConnection {
|
||||||
prev_recv_cnt: usize,
|
prev_recv_cnt: usize,
|
||||||
|
|
||||||
// Total auxiliary buffer space available to receive packets from the driver, not including
|
// Total auxiliary buffer space available to receive packets from the driver, not including
|
||||||
// the virtqueue itself. For us, this is tx buffer on the named pipe into which we drain packets
|
// the virtqueue itself. For us, this is tx buffer on the named pipe into which we drain
|
||||||
// for the connection. Note that if the named pipe has a grow on demand TX buffer, we use
|
// packets for the connection. Note that if the named pipe has a grow on demand TX buffer,
|
||||||
// DEFAULT_BUF_ALLOC instead.
|
// we use DEFAULT_BUF_ALLOC instead.
|
||||||
buf_alloc: usize,
|
buf_alloc: usize,
|
||||||
|
|
||||||
// Peer (driver) total free-running count of received bytes.
|
// Peer (driver) total free-running count of received bytes.
|
||||||
|
@ -1155,7 +1155,8 @@ impl Worker {
|
||||||
fwd_cnt: 0.into(),
|
fwd_cnt: 0.into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
// Safe because virtio_vsock_hdr is a simple data struct and converts cleanly to bytes
|
// Safe because virtio_vsock_hdr is a simple data struct and converts cleanly to
|
||||||
|
// bytes
|
||||||
self.write_bytes_to_queue(
|
self.write_bytes_to_queue(
|
||||||
&mut *send_queue.lock().await,
|
&mut *send_queue.lock().await,
|
||||||
rx_queue_evt,
|
rx_queue_evt,
|
||||||
|
|
|
@ -59,17 +59,17 @@ const MAJOR_VERSION: u16 = 1;
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug, AsBytes, FromZeroes, FromBytes)]
|
#[derive(Clone, Copy, Debug, AsBytes, FromZeroes, FromBytes)]
|
||||||
struct SparseHeader {
|
struct SparseHeader {
|
||||||
magic: Le32, /* SPARSE_HEADER_MAGIC */
|
magic: Le32, // SPARSE_HEADER_MAGIC
|
||||||
major_version: Le16, /* (0x1) - reject images with higher major versions */
|
major_version: Le16, // (0x1) - reject images with higher major versions
|
||||||
minor_version: Le16, /* (0x0) - allow images with higer minor versions */
|
minor_version: Le16, // (0x0) - allow images with higer minor versions
|
||||||
file_hdr_sz: Le16, /* 28 bytes for first revision of the file format */
|
file_hdr_sz: Le16, // 28 bytes for first revision of the file format
|
||||||
chunk_hdr_size: Le16, /* 12 bytes for first revision of the file format */
|
chunk_hdr_size: Le16, // 12 bytes for first revision of the file format
|
||||||
blk_sz: Le32, /* block size in bytes, must be a multiple of 4 (4096) */
|
blk_sz: Le32, // block size in bytes, must be a multiple of 4 (4096)
|
||||||
total_blks: Le32, /* total blocks in the non-sparse output image */
|
total_blks: Le32, // total blocks in the non-sparse output image
|
||||||
total_chunks: Le32, /* total chunks in the sparse input image */
|
total_chunks: Le32, // total chunks in the sparse input image
|
||||||
image_checksum: Le32, /* CRC32 checksum of the original data, counting "don't care" */
|
// CRC32 checksum of the original data, counting "don't care" as 0. Standard 802.3 polynomial,
|
||||||
/* as 0. Standard 802.3 polynomial, use a Public Domain */
|
// use a Public Domain table implementation
|
||||||
/* table implementation */
|
image_checksum: Le32,
|
||||||
}
|
}
|
||||||
|
|
||||||
const CHUNK_TYPE_RAW: u16 = 0xCAC1;
|
const CHUNK_TYPE_RAW: u16 = 0xCAC1;
|
||||||
|
|
|
@ -219,7 +219,8 @@ impl CompositeDiskFile {
|
||||||
};
|
};
|
||||||
let comp_file = open_file_or_duplicate(
|
let comp_file = open_file_or_duplicate(
|
||||||
&path,
|
&path,
|
||||||
OpenOptions::new().read(true).write(writable), // TODO(b/190435784): add support for O_DIRECT.
|
OpenOptions::new().read(true).write(writable), /* TODO(b/190435784): add
|
||||||
|
* support for O_DIRECT. */
|
||||||
)
|
)
|
||||||
.map_err(|e| Error::OpenFile(e.into(), disk.file_path.to_string()))?;
|
.map_err(|e| Error::OpenFile(e.into(), disk.file_path.to_string()))?;
|
||||||
|
|
||||||
|
@ -1302,8 +1303,8 @@ mod tests {
|
||||||
// Write to the RW part so that some fsync operation will occur.
|
// Write to the RW part so that some fsync operation will occur.
|
||||||
composite.write_zeroes_at(0, 20).await.unwrap();
|
composite.write_zeroes_at(0, 20).await.unwrap();
|
||||||
|
|
||||||
// This is the test's assert. fsyncing should NOT touch a read-only disk part. On Windows,
|
// This is the test's assert. fsyncing should NOT touch a read-only disk part. On
|
||||||
// this would be an error.
|
// Windows, this would be an error.
|
||||||
composite.fsync().await.expect(
|
composite.fsync().await.expect(
|
||||||
"Failed to fsync composite disk. \
|
"Failed to fsync composite disk. \
|
||||||
This can happen if the disk writable state is wrong.",
|
This can happen if the disk writable state is wrong.",
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue