mirror of
https://github.com/rust-vmm/vhost-device.git
synced 2025-12-26 22:48:17 +00:00
commit
91d84b481e
43
Cargo.lock
generated
43
Cargo.lock
generated
@ -122,9 +122,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.103"
|
||||
version = "0.2.105"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
|
||||
checksum = "869d572136620d55835903746bcb5cdc54cb2851fd0aeec53220b4bb65ef3013"
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
@ -224,6 +224,26 @@ dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.8.0"
|
||||
@ -263,7 +283,7 @@ dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
"vm-memory",
|
||||
"vmm-sys-util 0.8.0",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -274,11 +294,12 @@ dependencies = [
|
||||
"epoll",
|
||||
"libc",
|
||||
"log",
|
||||
"thiserror",
|
||||
"vhost",
|
||||
"vhost-user-backend",
|
||||
"virtio-bindings",
|
||||
"vm-memory",
|
||||
"vmm-sys-util 0.9.0",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -293,7 +314,7 @@ dependencies = [
|
||||
"virtio-bindings",
|
||||
"virtio-queue",
|
||||
"vm-memory",
|
||||
"vmm-sys-util 0.9.0",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -309,7 +330,7 @@ source = "git+https://github.com/rust-vmm/vm-virtio?rev=6013dd9#6013dd91b2e6eb77
|
||||
dependencies = [
|
||||
"log",
|
||||
"vm-memory",
|
||||
"vmm-sys-util 0.8.0",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -323,16 +344,6 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vmm-sys-util"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01cf11afbc4ebc0d5c7a7748a77d19e2042677fc15faa2f4ccccb27c18a60605"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vmm-sys-util"
|
||||
version = "0.9.0"
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"coverage_score": 48.8,
|
||||
"coverage_score": 46.8,
|
||||
"exclude_path": "",
|
||||
"crate_features": ""
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ clap = { version = "=3.0.0-beta.2", features = ["yaml"] }
|
||||
epoll = "4.3"
|
||||
libc = ">=0.2.95"
|
||||
log = ">=0.4.6"
|
||||
thiserror = "1.0"
|
||||
vhost = { version = "0.2", features = ["vhost-user-slave"] }
|
||||
vhost-user-backend = { git = "https://github.com/rust-vmm/vhost-user-backend", rev = "bd6b53348f06055abcb2b7254168d716b742f383" }
|
||||
virtio-bindings = ">=0.1"
|
||||
|
||||
@ -5,11 +5,13 @@
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use log::info;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
use libc::{c_ulong, ioctl, EINVAL};
|
||||
use vmm_sys_util::errno::{errno_result, Error, Result};
|
||||
use libc::{c_ulong, ioctl};
|
||||
use thiserror::Error as ThisError;
|
||||
use vmm_sys_util::errno::Error as IoError;
|
||||
|
||||
use super::AdapterConfig;
|
||||
|
||||
@ -20,6 +22,27 @@ type IoctlRequest = libc::c_int;
|
||||
#[cfg(not(target_env = "musl"))]
|
||||
type IoctlRequest = c_ulong;
|
||||
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, ThisError)]
|
||||
/// Errors related to low level i2c helpers
|
||||
pub enum Error {
|
||||
#[error("Incorrect message length for {0} operation: {1}")]
|
||||
MessageLengthInvalid(&'static str, usize),
|
||||
#[error("Invalid SMBUS command: {0}")]
|
||||
SMBusCommandInvalid(u32),
|
||||
#[error("Invalid SMBus transfer, request-count: {0}, req[0].len: {1}, req[1].len: {2}")]
|
||||
SMBusTransferInvalid(usize, u16, u16),
|
||||
#[error("Failed to open adapter at /dev/i2c-{0}")]
|
||||
DeviceOpenFailed(u32),
|
||||
#[error("Ioctl command failed for {0} operation: {1}")]
|
||||
IoctlFailure(&'static str, IoError),
|
||||
#[error("Invalid Adapter function: {0:x}")]
|
||||
AdapterFunctionInvalid(u64),
|
||||
#[error("Invalid Client Address")]
|
||||
ClientAddressInvalid,
|
||||
}
|
||||
|
||||
/// Linux I2C/SMBUS definitions
|
||||
/// IOCTL commands
|
||||
|
||||
@ -147,11 +170,7 @@ impl I2cSmbusIoctlData {
|
||||
if (reqs[0].flags & I2C_M_RD) != 0 {
|
||||
// Special Read requests, reqs[0].len can be 0 or 1 only.
|
||||
if reqs[0].len > 1 {
|
||||
println!(
|
||||
"Incorrect message length for read operation: {}",
|
||||
reqs[0].len
|
||||
);
|
||||
return Err(Error::new(EINVAL));
|
||||
return Err(Error::MessageLengthInvalid("read", reqs[0].len as usize));
|
||||
}
|
||||
read_write = I2C_SMBUS_READ;
|
||||
} else {
|
||||
@ -173,11 +192,7 @@ impl I2cSmbusIoctlData {
|
||||
I2C_SMBUS_WORD_DATA
|
||||
}
|
||||
_ => {
|
||||
println!(
|
||||
"Message length not supported for write operation: {}",
|
||||
reqs[0].len
|
||||
);
|
||||
return Err(Error::new(EINVAL));
|
||||
return Err(Error::MessageLengthInvalid("write", reqs[0].len as usize));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -195,11 +210,11 @@ impl I2cSmbusIoctlData {
|
||||
|| (reqs[0].len != 1)
|
||||
|| (reqs[1].len > 2)
|
||||
{
|
||||
println!(
|
||||
"Expecting a valid read smbus transfer: {:?}",
|
||||
(reqs.len(), reqs[0].len, reqs[1].len)
|
||||
);
|
||||
return Err(Error::new(EINVAL));
|
||||
return Err(Error::SMBusTransferInvalid(
|
||||
reqs.len(),
|
||||
reqs[0].len,
|
||||
reqs[1].len,
|
||||
));
|
||||
}
|
||||
read_write = I2C_SMBUS_READ;
|
||||
|
||||
@ -211,8 +226,11 @@ impl I2cSmbusIoctlData {
|
||||
}
|
||||
|
||||
_ => {
|
||||
println!("Invalid number of messages for smbus xfer: {}", reqs.len());
|
||||
return Err(Error::new(EINVAL));
|
||||
return Err(Error::SMBusTransferInvalid(
|
||||
reqs.len(),
|
||||
reqs[0].len,
|
||||
reqs[1].len,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,16 +264,16 @@ pub trait I2cDevice {
|
||||
Self: Sized;
|
||||
|
||||
// Corresponds to the I2C_FUNCS ioctl call.
|
||||
fn funcs(&mut self, func: u64) -> i32;
|
||||
fn funcs(&mut self, func: u64) -> Result<()>;
|
||||
|
||||
// Corresponds to the I2C_RDWR ioctl call.
|
||||
fn rdwr(&self, data: &I2cRdwrIoctlData) -> i32;
|
||||
fn rdwr(&self, data: &I2cRdwrIoctlData) -> Result<()>;
|
||||
|
||||
// Corresponds to the I2C_SMBUS ioctl call.
|
||||
fn smbus(&self, data: &I2cSmbusIoctlData) -> i32;
|
||||
fn smbus(&self, data: &I2cSmbusIoctlData) -> Result<()>;
|
||||
|
||||
// Corresponds to the I2C_SLAVE ioctl call.
|
||||
fn slave(&self, addr: u64) -> i32;
|
||||
fn slave(&self, addr: u64) -> Result<()>;
|
||||
|
||||
// Returns the adapter number corresponding to this device.
|
||||
fn adapter_no(&self) -> u32;
|
||||
@ -275,25 +293,50 @@ impl I2cDevice for PhysDevice {
|
||||
file: OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(device_path)?,
|
||||
.open(device_path)
|
||||
.map_err(|_| Error::DeviceOpenFailed(adapter_no))?,
|
||||
adapter_no,
|
||||
})
|
||||
}
|
||||
|
||||
fn funcs(&mut self, func: u64) -> i32 {
|
||||
unsafe { ioctl(self.file.as_raw_fd(), I2C_FUNCS, &func) }
|
||||
fn funcs(&mut self, func: u64) -> Result<()> {
|
||||
let ret = unsafe { ioctl(self.file.as_raw_fd(), I2C_FUNCS, &func) };
|
||||
|
||||
if ret == -1 {
|
||||
Err(Error::IoctlFailure("funcs", IoError::last()))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn rdwr(&self, data: &I2cRdwrIoctlData) -> i32 {
|
||||
unsafe { ioctl(self.file.as_raw_fd(), I2C_RDWR, data) }
|
||||
fn rdwr(&self, data: &I2cRdwrIoctlData) -> Result<()> {
|
||||
let ret = unsafe { ioctl(self.file.as_raw_fd(), I2C_RDWR, data) };
|
||||
|
||||
if ret == -1 {
|
||||
Err(Error::IoctlFailure("rdwr", IoError::last()))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn smbus(&self, data: &I2cSmbusIoctlData) -> i32 {
|
||||
unsafe { ioctl(self.file.as_raw_fd(), I2C_SMBUS, data) }
|
||||
fn smbus(&self, data: &I2cSmbusIoctlData) -> Result<()> {
|
||||
let ret = unsafe { ioctl(self.file.as_raw_fd(), I2C_SMBUS, data) };
|
||||
|
||||
if ret == -1 {
|
||||
Err(Error::IoctlFailure("smbus", IoError::last()))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn slave(&self, addr: u64) -> i32 {
|
||||
unsafe { ioctl(self.file.as_raw_fd(), I2C_SLAVE, addr as c_ulong) }
|
||||
fn slave(&self, addr: u64) -> Result<()> {
|
||||
let ret = unsafe { ioctl(self.file.as_raw_fd(), I2C_SLAVE, addr as c_ulong) };
|
||||
|
||||
if ret == -1 {
|
||||
Err(Error::IoctlFailure("slave", IoError::last()))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn adapter_no(&self) -> u32 {
|
||||
@ -311,11 +354,8 @@ impl<D: I2cDevice> I2cAdapter<D> {
|
||||
// Creates a new adapter corresponding to `device`.
|
||||
fn new(mut device: D) -> Result<I2cAdapter<D>> {
|
||||
let func: u64 = I2C_FUNC_SMBUS_ALL;
|
||||
let ret = device.funcs(func);
|
||||
if ret == -1 {
|
||||
println!("Failed to get I2C function");
|
||||
return errno_result();
|
||||
}
|
||||
|
||||
device.funcs(func)?;
|
||||
|
||||
let smbus;
|
||||
if (func & I2C_FUNC_I2C) != 0 {
|
||||
@ -323,8 +363,7 @@ impl<D: I2cDevice> I2cAdapter<D> {
|
||||
} else if (func & I2C_FUNC_SMBUS_ALL) != 0 {
|
||||
smbus = true;
|
||||
} else {
|
||||
println!("Invalid functionality {:x}", func);
|
||||
return Err(Error::new(EINVAL));
|
||||
return Err(Error::AdapterFunctionInvalid(func));
|
||||
}
|
||||
|
||||
Ok(I2cAdapter {
|
||||
@ -338,7 +377,6 @@ impl<D: I2cDevice> I2cAdapter<D> {
|
||||
fn i2c_transfer(&self, reqs: &mut [I2cReq]) -> Result<()> {
|
||||
let mut msgs: Vec<I2cMsg> = Vec::with_capacity(reqs.len());
|
||||
let len = reqs.len();
|
||||
let addr = reqs[0].addr;
|
||||
|
||||
for req in reqs {
|
||||
msgs.push(I2cMsg {
|
||||
@ -354,27 +392,14 @@ impl<D: I2cDevice> I2cAdapter<D> {
|
||||
nmsgs: len as u32,
|
||||
};
|
||||
|
||||
let ret = self.device.rdwr(&data);
|
||||
if ret == -1 {
|
||||
println!("Failed to transfer i2c data to device addr to {:x}", addr);
|
||||
errno_result()
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
self.device.rdwr(&data)
|
||||
}
|
||||
|
||||
/// Perform I2C_SMBUS transfer
|
||||
fn smbus_transfer(&self, reqs: &mut [I2cReq]) -> Result<()> {
|
||||
let smbus_data = I2cSmbusIoctlData::new(reqs)?;
|
||||
|
||||
let ret = self.device.smbus(&smbus_data);
|
||||
if ret == -1 {
|
||||
println!(
|
||||
"Failed to transfer smbus data to device addr to {:x}",
|
||||
reqs[0].addr
|
||||
);
|
||||
return errno_result();
|
||||
}
|
||||
self.device.smbus(&smbus_data)?;
|
||||
|
||||
if smbus_data.read_write == I2C_SMBUS_READ {
|
||||
unsafe {
|
||||
@ -387,8 +412,7 @@ impl<D: I2cDevice> I2cAdapter<D> {
|
||||
}
|
||||
|
||||
_ => {
|
||||
println!("Invalid SMBUS command: {}", smbus_data.size);
|
||||
return Err(Error::new(EINVAL));
|
||||
return Err(Error::SMBusCommandInvalid(smbus_data.size));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -406,14 +430,7 @@ impl<D: I2cDevice> I2cAdapter<D> {
|
||||
|
||||
/// Sets device's address for an I2C adapter.
|
||||
fn set_device_addr(&self, addr: usize) -> Result<()> {
|
||||
let ret = self.device.slave(addr as u64);
|
||||
|
||||
if ret == -1 {
|
||||
println!("Failed to set device addr to {:x}", addr);
|
||||
errno_result()
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
self.device.slave(addr as u64)
|
||||
}
|
||||
|
||||
fn transfer(&self, reqs: &mut [I2cReq]) -> Result<()> {
|
||||
@ -452,7 +469,7 @@ impl<D: I2cDevice> I2cMap<D> {
|
||||
device_map[*addr as usize] = i as u32;
|
||||
}
|
||||
|
||||
println!(
|
||||
info!(
|
||||
"Added I2C master with bus id: {:x} for devices",
|
||||
adapter.adapter_no(),
|
||||
);
|
||||
@ -474,7 +491,7 @@ impl<D: I2cDevice> I2cMap<D> {
|
||||
|
||||
// This can happen a lot while scanning the bus, don't print any errors.
|
||||
if index == I2C_INVALID_ADAPTER {
|
||||
return Err(Error::new(EINVAL));
|
||||
return Err(Error::ClientAddressInvalid);
|
||||
}
|
||||
|
||||
// get the corresponding adapter based on the device config.
|
||||
@ -491,12 +508,12 @@ pub mod tests {
|
||||
use super::*;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug)]
|
||||
pub struct DummyDevice {
|
||||
funcs_result: i32,
|
||||
rdwr_result: i32,
|
||||
smbus_result: i32,
|
||||
slave_result: i32,
|
||||
funcs_result: Result<()>,
|
||||
rdwr_result: Result<()>,
|
||||
smbus_result: Result<()>,
|
||||
slave_result: Result<()>,
|
||||
adapter_no: u32,
|
||||
}
|
||||
|
||||
@ -507,23 +524,26 @@ pub mod tests {
|
||||
{
|
||||
Ok(DummyDevice {
|
||||
adapter_no,
|
||||
..Default::default()
|
||||
funcs_result: Ok(()),
|
||||
rdwr_result: Ok(()),
|
||||
smbus_result: Ok(()),
|
||||
slave_result: Ok(()),
|
||||
})
|
||||
}
|
||||
|
||||
fn funcs(&mut self, _func: u64) -> i32 {
|
||||
fn funcs(&mut self, _func: u64) -> Result<()> {
|
||||
self.funcs_result
|
||||
}
|
||||
|
||||
fn rdwr(&self, _data: &I2cRdwrIoctlData) -> i32 {
|
||||
fn rdwr(&self, _data: &I2cRdwrIoctlData) -> Result<()> {
|
||||
self.rdwr_result
|
||||
}
|
||||
|
||||
fn smbus(&self, _data: &I2cSmbusIoctlData) -> i32 {
|
||||
fn smbus(&self, _data: &I2cSmbusIoctlData) -> Result<()> {
|
||||
self.smbus_result
|
||||
}
|
||||
|
||||
fn slave(&self, _addr: u64) -> i32 {
|
||||
fn slave(&self, _addr: u64) -> Result<()> {
|
||||
self.slave_result
|
||||
}
|
||||
|
||||
@ -532,16 +552,6 @@ pub mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i2c_map_duplicate_device4() {
|
||||
assert!(AdapterConfig::try_from("1:4,2:32:21,5:4:23").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_duplicated_adapter_no() {
|
||||
assert!(AdapterConfig::try_from("1:4,1:32:21,5:10:23").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i2c_map() {
|
||||
let adapter_config = AdapterConfig::try_from("1:4,2:32:21,5:10:23").unwrap();
|
||||
@ -617,6 +627,7 @@ pub mod tests {
|
||||
let mut i2c_map: I2cMap<DummyDevice> = I2cMap::new(&adapter_config).unwrap();
|
||||
i2c_map.adapters[0].smbus = true;
|
||||
|
||||
// I2C_SMBUS_READ (I2C_SMBUS_WORD_DATA) failure operation
|
||||
let mut reqs: Vec<I2cReq> = vec![
|
||||
I2cReq {
|
||||
addr: 0x3,
|
||||
@ -632,12 +643,10 @@ pub mod tests {
|
||||
buf: [3, 4].to_vec(),
|
||||
},
|
||||
];
|
||||
|
||||
// I2C_SMBUS_READ (I2C_SMBUS_WORD_DATA) failure operation
|
||||
// TODO: check the actual error once we have an error type defined.
|
||||
// TODO-continued: otherwise this test is unreliable because it might
|
||||
// fail for another reason than the expected one.
|
||||
assert!(i2c_map.transfer(&mut reqs).is_err());
|
||||
assert_eq!(
|
||||
i2c_map.transfer(&mut reqs).unwrap_err(),
|
||||
Error::SMBusTransferInvalid(2, 1, 2)
|
||||
);
|
||||
|
||||
// I2C_SMBUS_READ (I2C_SMBUS_WORD_DATA) failure operation
|
||||
let mut reqs = vec![
|
||||
@ -655,7 +664,10 @@ pub mod tests {
|
||||
buf: [3, 4].to_vec(),
|
||||
},
|
||||
];
|
||||
assert!(i2c_map.transfer(&mut reqs).is_err());
|
||||
assert_eq!(
|
||||
i2c_map.transfer(&mut reqs).unwrap_err(),
|
||||
Error::SMBusTransferInvalid(2, 1, 2)
|
||||
);
|
||||
|
||||
// I2C_SMBUS_READ (I2C_SMBUS_WORD_DATA) failure operation
|
||||
let mut reqs = vec![
|
||||
@ -673,7 +685,10 @@ pub mod tests {
|
||||
buf: [3, 4].to_vec(),
|
||||
},
|
||||
];
|
||||
assert!(i2c_map.transfer(&mut reqs).is_err());
|
||||
assert_eq!(
|
||||
i2c_map.transfer(&mut reqs).unwrap_err(),
|
||||
Error::SMBusTransferInvalid(2, 2, 2)
|
||||
);
|
||||
|
||||
// I2C_SMBUS_READ (I2C_SMBUS_WORD_DATA) failure operation
|
||||
let mut reqs = vec![
|
||||
@ -691,6 +706,9 @@ pub mod tests {
|
||||
buf: [3, 4, 5].to_vec(),
|
||||
},
|
||||
];
|
||||
assert!(i2c_map.transfer(&mut reqs).is_err());
|
||||
assert_eq!(
|
||||
i2c_map.transfer(&mut reqs).unwrap_err(),
|
||||
Error::SMBusTransferInvalid(2, 1, 3)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,11 +8,14 @@
|
||||
mod i2c;
|
||||
mod vhu_i2c;
|
||||
|
||||
use log::{info, warn};
|
||||
use std::convert::TryFrom;
|
||||
use std::num::ParseIntError;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::thread::spawn;
|
||||
|
||||
use clap::{load_yaml, App, ArgMatches};
|
||||
use thiserror::Error as ThisError;
|
||||
use vhost::{vhost_user, vhost_user::Listener};
|
||||
use vhost_user_backend::VhostUserDaemon;
|
||||
use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap};
|
||||
@ -20,6 +23,31 @@ use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap};
|
||||
use i2c::{I2cDevice, I2cMap, PhysDevice, MAX_I2C_VDEV};
|
||||
use vhu_i2c::VhostUserI2cBackend;
|
||||
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug, PartialEq, ThisError)]
|
||||
/// Errors related to low level i2c helpers
|
||||
pub enum Error {
|
||||
#[error("Invalid socket path")]
|
||||
SocketPathInvalid,
|
||||
#[error("Invalid socket count: {0}")]
|
||||
SocketCountInvalid(usize),
|
||||
#[error("Invalid device list")]
|
||||
DeviceListInvalid,
|
||||
#[error("Duplicate adapter detected: {0}")]
|
||||
AdapterDuplicate(u32),
|
||||
#[error("Invalid client address: {0}")]
|
||||
ClientAddressInvalid(u16),
|
||||
#[error("Duplicate client address detected: {0}")]
|
||||
ClientAddressDuplicate(u16),
|
||||
#[error("Low level I2c failure: {0:?}")]
|
||||
I2cFailure(i2c::Error),
|
||||
#[error("Failed while parsing to integer: {0:?}")]
|
||||
ParseFailure(ParseIntError),
|
||||
#[error("Failed to join threads")]
|
||||
FailedJoiningThreads,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct DeviceConfig {
|
||||
adapter_no: u32,
|
||||
@ -34,13 +62,13 @@ impl DeviceConfig {
|
||||
}
|
||||
}
|
||||
|
||||
fn push(&mut self, addr: u16) -> std::result::Result<(), String> {
|
||||
fn push(&mut self, addr: u16) -> Result<()> {
|
||||
if addr as usize > MAX_I2C_VDEV {
|
||||
return Err(format!("Invalid address: {} (> maximum allowed)", addr));
|
||||
return Err(Error::ClientAddressInvalid(addr));
|
||||
}
|
||||
|
||||
if self.addr.contains(&addr) {
|
||||
return Err(format!("Address already in use: {}", addr));
|
||||
return Err(Error::ClientAddressDuplicate(addr));
|
||||
}
|
||||
|
||||
self.addr.push(addr);
|
||||
@ -66,14 +94,14 @@ impl AdapterConfig {
|
||||
self.inner.iter().any(|elem| elem.addr.contains(&addr))
|
||||
}
|
||||
|
||||
fn push(&mut self, device: DeviceConfig) -> std::result::Result<(), String> {
|
||||
fn push(&mut self, device: DeviceConfig) -> Result<()> {
|
||||
if self.contains_adapter_no(device.adapter_no) {
|
||||
return Err("Duplicated adapter number".to_string());
|
||||
return Err(Error::AdapterDuplicate(device.adapter_no));
|
||||
}
|
||||
|
||||
for addr in device.addr.iter() {
|
||||
if self.contains_addr(*addr) {
|
||||
return Err(format!("Address already in use: {}", addr));
|
||||
return Err(Error::ClientAddressDuplicate(*addr));
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,21 +111,19 @@ impl AdapterConfig {
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for AdapterConfig {
|
||||
type Error = String;
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(list: &str) -> Result<Self, Self::Error> {
|
||||
fn try_from(list: &str) -> Result<Self> {
|
||||
let busses: Vec<&str> = list.split(',').collect();
|
||||
let mut devices = AdapterConfig::new();
|
||||
|
||||
for businfo in busses.iter() {
|
||||
let list: Vec<&str> = businfo.split(':').collect();
|
||||
let bus_addr = list[0].parse::<u32>().map_err(|_| "Invalid bus address")?;
|
||||
let bus_addr = list[0].parse::<u32>().map_err(Error::ParseFailure)?;
|
||||
let mut adapter = DeviceConfig::new(bus_addr);
|
||||
|
||||
for device_str in list[1..].iter() {
|
||||
let addr = device_str
|
||||
.parse::<u16>()
|
||||
.map_err(|_| "Invalid device addr")?;
|
||||
let addr = device_str.parse::<u16>().map_err(Error::ParseFailure)?;
|
||||
adapter.push(addr)?;
|
||||
}
|
||||
|
||||
@ -115,25 +141,27 @@ struct I2cConfiguration {
|
||||
}
|
||||
|
||||
impl TryFrom<ArgMatches> for I2cConfiguration {
|
||||
type Error = String;
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(cmd_args: ArgMatches) -> Result<Self, Self::Error> {
|
||||
fn try_from(cmd_args: ArgMatches) -> Result<Self> {
|
||||
let socket_path = cmd_args
|
||||
.value_of("socket_path")
|
||||
.ok_or("Invalid socket path")?
|
||||
.ok_or(Error::SocketPathInvalid)?
|
||||
.to_string();
|
||||
|
||||
let socket_count = cmd_args
|
||||
.value_of("socket_count")
|
||||
.unwrap_or("1")
|
||||
.parse::<usize>()
|
||||
.map_err(|_| "Invalid socket_count")?;
|
||||
.map_err(Error::ParseFailure)?;
|
||||
|
||||
if socket_count == 0 {
|
||||
return Err("Socket count can't be 0".to_string());
|
||||
return Err(Error::SocketCountInvalid(0));
|
||||
}
|
||||
|
||||
let list = cmd_args.value_of("devices").ok_or("Invalid devices list")?;
|
||||
let list = cmd_args
|
||||
.value_of("devices")
|
||||
.ok_or(Error::DeviceListInvalid)?;
|
||||
let devices = AdapterConfig::try_from(list)?;
|
||||
Ok(I2cConfiguration {
|
||||
socket_path,
|
||||
@ -143,14 +171,9 @@ impl TryFrom<ArgMatches> for I2cConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
fn start_backend<D: 'static + I2cDevice + Send + Sync>(
|
||||
config: I2cConfiguration,
|
||||
) -> Result<(), String> {
|
||||
fn start_backend<D: 'static + I2cDevice + Send + Sync>(config: I2cConfiguration) -> Result<()> {
|
||||
// The same i2c_map structure instance is shared between all the guests
|
||||
let i2c_map = Arc::new(
|
||||
I2cMap::<D>::new(&config.devices)
|
||||
.map_err(|e| format!("Failed to create i2c_map ({})", e))?,
|
||||
);
|
||||
let i2c_map = Arc::new(I2cMap::<D>::new(&config.devices).map_err(Error::I2cFailure)?);
|
||||
|
||||
let mut handles = Vec::new();
|
||||
|
||||
@ -159,6 +182,14 @@ fn start_backend<D: 'static + I2cDevice + Send + Sync>(
|
||||
let i2c_map = i2c_map.clone();
|
||||
|
||||
let handle = spawn(move || loop {
|
||||
// A separate thread is spawned for each socket and can connect to a separate guest.
|
||||
// These are run in an infinite loop to not require the daemon to be restarted once a
|
||||
// guest exits.
|
||||
//
|
||||
// There isn't much value in complicating code here to return an error from the
|
||||
// threads, and so the code uses unwrap() instead. The panic on a thread won't cause
|
||||
// trouble to other threads/guests or the main() function and should be safe for the
|
||||
// daemon.
|
||||
let backend = Arc::new(RwLock::new(
|
||||
VhostUserI2cBackend::new(i2c_map.clone()).unwrap(),
|
||||
));
|
||||
@ -175,38 +206,33 @@ fn start_backend<D: 'static + I2cDevice + Send + Sync>(
|
||||
|
||||
match daemon.wait() {
|
||||
Ok(()) => {
|
||||
println!("Stopping cleanly.");
|
||||
info!("Stopping cleanly.");
|
||||
}
|
||||
Err(vhost_user_backend::Error::HandleRequest(
|
||||
vhost_user::Error::PartialMessage,
|
||||
)) => {
|
||||
println!("vhost-user connection closed with partial message. If the VM is shutting down, this is expected behavior; otherwise, it might be a bug.");
|
||||
info!("vhost-user connection closed with partial message. If the VM is shutting down, this is expected behavior; otherwise, it might be a bug.");
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Error running daemon: {:?}", e);
|
||||
warn!("Error running daemon: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
// No matter the result, we need to shut down the worker thread.
|
||||
backend
|
||||
.read()
|
||||
.unwrap()
|
||||
.exit_event
|
||||
.write(1)
|
||||
.expect("Shutting down worker thread");
|
||||
backend.read().unwrap().exit_event.write(1).unwrap();
|
||||
});
|
||||
|
||||
handles.push(handle);
|
||||
}
|
||||
|
||||
for handle in handles {
|
||||
handle.join().map_err(|_| "Failed to join threads")?;
|
||||
handle.join().map_err(|_| Error::FailedJoiningThreads)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> Result<(), String> {
|
||||
fn main() -> Result<()> {
|
||||
let yaml = load_yaml!("cli.yaml");
|
||||
let cmd_args = App::from(yaml).get_matches();
|
||||
|
||||
@ -251,21 +277,21 @@ mod tests {
|
||||
let cmd_args = get_cmd_args(socket_name, "1:4d", Some(5));
|
||||
assert_eq!(
|
||||
I2cConfiguration::try_from(cmd_args).unwrap_err(),
|
||||
"Invalid device addr"
|
||||
Error::ParseFailure("4d".parse::<u32>().unwrap_err())
|
||||
);
|
||||
|
||||
// Invalid socket count
|
||||
let cmd_args = get_cmd_args(socket_name, "1:4", Some(0));
|
||||
assert_eq!(
|
||||
I2cConfiguration::try_from(cmd_args).unwrap_err(),
|
||||
"Socket count can't be 0"
|
||||
Error::SocketCountInvalid(0)
|
||||
);
|
||||
|
||||
// Duplicate client address: 4
|
||||
let cmd_args = get_cmd_args(socket_name, "1:4,2:32:21,5:4:23", Some(5));
|
||||
assert_eq!(
|
||||
I2cConfiguration::try_from(cmd_args).unwrap_err(),
|
||||
"Address already in use: 4"
|
||||
Error::ClientAddressDuplicate(4)
|
||||
);
|
||||
}
|
||||
|
||||
@ -295,4 +321,20 @@ mod tests {
|
||||
|
||||
assert_eq!(config, expected_config);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_i2c_map_duplicate_device4() {
|
||||
assert_eq!(
|
||||
AdapterConfig::try_from("1:4,2:32:21,5:4:23").unwrap_err(),
|
||||
Error::ClientAddressDuplicate(4)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_duplicated_adapter_no() {
|
||||
assert_eq!(
|
||||
AdapterConfig::try_from("1:4,1:32:21,5:10:23").unwrap_err(),
|
||||
Error::AdapterDuplicate(1)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,10 +5,12 @@
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use log::warn;
|
||||
use std::mem::size_of;
|
||||
use std::sync::Arc;
|
||||
use std::{convert, error, fmt, io};
|
||||
use std::{convert, io};
|
||||
|
||||
use thiserror::Error as ThisError;
|
||||
use vhost::vhost_user::message::{VhostUserProtocolFeatures, VhostUserVirtioFeatures};
|
||||
use vhost_user_backend::{VhostUserBackendMut, VringRwLock, VringT};
|
||||
use virtio_bindings::bindings::virtio_net::{VIRTIO_F_NOTIFY_ON_EMPTY, VIRTIO_F_VERSION_1};
|
||||
@ -26,36 +28,31 @@ const NUM_QUEUES: usize = 1;
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
type VhostUserBackendResult<T> = std::result::Result<T, std::io::Error>;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, ThisError)]
|
||||
/// Errors related to vhost-device-i2c daemon.
|
||||
pub enum Error {
|
||||
/// Failed to handle event other than input event.
|
||||
#[error("Failed to handle event, didn't match EPOLLIN")]
|
||||
HandleEventNotEpollIn,
|
||||
/// Failed to handle unknown event.
|
||||
HandleEventUnknownEvent,
|
||||
/// Guest gave us a write only descriptor that protocol says to read from.
|
||||
#[error("Failed to handle unknown event")]
|
||||
HandleEventUnknown,
|
||||
#[error("Received unexpected write only descriptor")]
|
||||
UnexpectedWriteOnlyDescriptor,
|
||||
/// Guest gave us a readable descriptor that protocol says to only write to.
|
||||
UnexpectedReadDescriptor,
|
||||
/// Invalid descriptor count
|
||||
#[error("Received unexpected readable descriptor")]
|
||||
UnexpectedReadableDescriptor,
|
||||
#[error("Invalid descriptor count")]
|
||||
UnexpectedDescriptorCount,
|
||||
/// Invalid descriptor
|
||||
#[error("Invalid descriptor size")]
|
||||
UnexpectedDescriptorSize,
|
||||
/// Descriptor not found
|
||||
#[error("Descriptor not found")]
|
||||
DescriptorNotFound,
|
||||
/// Descriptor read failed
|
||||
#[error("Descriptor read failed")]
|
||||
DescriptorReadFailed,
|
||||
/// Descriptor write failed
|
||||
#[error("Descriptor write failed")]
|
||||
DescriptorWriteFailed,
|
||||
/// Descriptor send failed
|
||||
DescriptorSendFailed,
|
||||
}
|
||||
impl error::Error for Error {}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "vhost-device-i2c error: {:?}", self)
|
||||
}
|
||||
#[error("Failed to send notification")]
|
||||
NotificationFailed,
|
||||
#[error("Failed to create new EventFd: {0:?}")]
|
||||
EventFdFailed(std::io::Error),
|
||||
}
|
||||
|
||||
impl convert::From<Error> for io::Error {
|
||||
@ -99,7 +96,7 @@ impl<D: I2cDevice> VhostUserI2cBackend<D> {
|
||||
i2c_map,
|
||||
event_idx: false,
|
||||
mem: None,
|
||||
exit_event: EventFd::new(EFD_NONBLOCK).expect("Creating exit eventfd"),
|
||||
exit_event: EventFd::new(EFD_NONBLOCK).map_err(Error::EventFdFailed)?,
|
||||
})
|
||||
}
|
||||
|
||||
@ -142,7 +139,7 @@ impl<D: I2cDevice> VhostUserI2cBackend<D> {
|
||||
|
||||
let desc_in_hdr = descriptors[2];
|
||||
if !desc_in_hdr.is_write_only() {
|
||||
return Err(Error::UnexpectedReadDescriptor);
|
||||
return Err(Error::UnexpectedReadableDescriptor);
|
||||
}
|
||||
|
||||
if desc_in_hdr.len() as usize != size_of::<u8>() {
|
||||
@ -209,14 +206,14 @@ impl<D: I2cDevice> VhostUserI2cBackend<D> {
|
||||
}
|
||||
|
||||
if vring.add_used(desc_chain.head_index(), len).is_err() {
|
||||
println!("Couldn't return used descriptors to the ring");
|
||||
warn!("Couldn't return used descriptors to the ring");
|
||||
}
|
||||
}
|
||||
|
||||
// Send notification once all the requests are processed
|
||||
vring
|
||||
.signal_used_queue()
|
||||
.map_err(|_| Error::DescriptorSendFailed)?;
|
||||
.map_err(|_| Error::NotificationFailed)?;
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
@ -292,15 +289,15 @@ impl<D: 'static + I2cDevice + Sync + Send> VhostUserBackendMut<VringRwLock, ()>
|
||||
}
|
||||
|
||||
_ => {
|
||||
dbg!("unhandled device_event:", device_event);
|
||||
return Err(Error::HandleEventUnknownEvent.into());
|
||||
warn!("unhandled device_event: {}", device_event);
|
||||
return Err(Error::HandleEventUnknown.into());
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn exit_event(&self, _thread_index: usize) -> Option<EventFd> {
|
||||
Some(self.exit_event.try_clone().expect("Cloning exit eventfd"))
|
||||
self.exit_event.try_clone().ok()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user