mirror of
https://github.com/rust-vmm/vhost-device.git
synced 2026-01-01 12:41:58 +00:00
[i2c] Break process_queue() into two parts
Break out most of the non vring specific stuff to process_requests(), this is required for testing request processing properly. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
This commit is contained in:
parent
0cc0fe4f7a
commit
f9f3bdf845
@ -19,5 +19,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 = "4047c697470cc6c37e8e1835025b091d2b59c2f7" }
|
||||
virtio-bindings = ">=0.1"
|
||||
virtio-queue = { git = "https://github.com/rust-vmm/vm-virtio", rev = "66cda80" }
|
||||
vm-memory = "0.7"
|
||||
vmm-sys-util = "=0.9.0"
|
||||
|
||||
@ -17,6 +17,7 @@ use virtio_bindings::bindings::virtio_net::{VIRTIO_F_NOTIFY_ON_EMPTY, VIRTIO_F_V
|
||||
use virtio_bindings::bindings::virtio_ring::{
|
||||
VIRTIO_RING_F_EVENT_IDX, VIRTIO_RING_F_INDIRECT_DESC,
|
||||
};
|
||||
use virtio_queue::DescriptorChain;
|
||||
use vm_memory::{ByteValued, Bytes, GuestMemoryAtomic, GuestMemoryMmap, Le16, Le32};
|
||||
use vmm_sys_util::epoll::EventSet;
|
||||
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
||||
@ -96,6 +97,141 @@ pub struct VhostUserI2cBackend<D: I2cDevice> {
|
||||
pub exit_event: EventFd,
|
||||
}
|
||||
|
||||
type I2cDescriptorChain = DescriptorChain<GuestMemoryAtomic<GuestMemoryMmap<()>>>;
|
||||
|
||||
/// Process the requests in the vring and dispatch replies
|
||||
fn process_requests<D: I2cDevice>(
|
||||
i2c_map: &Arc<I2cMap<D>>,
|
||||
requests: Vec<I2cDescriptorChain>,
|
||||
vring: Option<&VringRwLock>,
|
||||
) -> Result<bool> {
|
||||
let mut reqs: Vec<I2cReq> = Vec::new();
|
||||
|
||||
if requests.is_empty() {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
// Iterate over each I2C request and push it to "reqs" vector.
|
||||
for desc_chain in requests.clone() {
|
||||
let descriptors: Vec<_> = desc_chain.clone().collect();
|
||||
|
||||
if (descriptors.len() != 2) && (descriptors.len() != 3) {
|
||||
return Err(Error::UnexpectedDescriptorCount);
|
||||
}
|
||||
|
||||
let desc_out_hdr = descriptors[0];
|
||||
|
||||
if desc_out_hdr.is_write_only() {
|
||||
return Err(Error::UnexpectedWriteOnlyDescriptor);
|
||||
}
|
||||
|
||||
if desc_out_hdr.len() as usize != size_of::<VirtioI2cOutHdr>() {
|
||||
return Err(Error::UnexpectedDescriptorSize);
|
||||
}
|
||||
|
||||
let out_hdr = desc_chain
|
||||
.memory()
|
||||
.read_obj::<VirtioI2cOutHdr>(desc_out_hdr.addr())
|
||||
.map_err(|_| Error::DescriptorReadFailed)?;
|
||||
|
||||
let flags = match out_hdr.flags.to_native() & VIRTIO_I2C_FLAGS_M_RD {
|
||||
VIRTIO_I2C_FLAGS_M_RD => I2C_M_RD,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
let desc_in_hdr = descriptors[descriptors.len() - 1];
|
||||
if !desc_in_hdr.is_write_only() {
|
||||
return Err(Error::UnexpectedReadableDescriptor);
|
||||
}
|
||||
|
||||
if desc_in_hdr.len() as usize != size_of::<u8>() {
|
||||
return Err(Error::UnexpectedDescriptorSize);
|
||||
}
|
||||
|
||||
let (buf, len) = match descriptors.len() {
|
||||
// Buffer is available
|
||||
3 => {
|
||||
let desc_buf = descriptors[1];
|
||||
let len = desc_buf.len();
|
||||
|
||||
if len == 0 {
|
||||
return Err(Error::UnexpectedDescriptorSize);
|
||||
}
|
||||
let mut buf = vec![0; len as usize];
|
||||
|
||||
if flags != I2C_M_RD {
|
||||
if desc_buf.is_write_only() {
|
||||
return Err(Error::UnexpectedWriteOnlyDescriptor);
|
||||
}
|
||||
|
||||
desc_chain
|
||||
.memory()
|
||||
.read(&mut buf, desc_buf.addr())
|
||||
.map_err(|_| Error::DescriptorReadFailed)?;
|
||||
} else if !desc_buf.is_write_only() {
|
||||
return Err(Error::UnexpectedReadableDescriptor);
|
||||
}
|
||||
|
||||
(buf, len)
|
||||
}
|
||||
|
||||
_ => (Vec::<u8>::new(), 0),
|
||||
};
|
||||
|
||||
reqs.push(I2cReq {
|
||||
addr: out_hdr.addr.to_native() >> 1,
|
||||
flags,
|
||||
len: len as u16,
|
||||
buf,
|
||||
});
|
||||
}
|
||||
|
||||
let in_hdr = {
|
||||
VirtioI2cInHdr {
|
||||
status: match i2c_map.transfer(&mut reqs) {
|
||||
Ok(()) => VIRTIO_I2C_MSG_OK,
|
||||
Err(_) => VIRTIO_I2C_MSG_ERR,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
for (i, desc_chain) in requests.iter().enumerate() {
|
||||
let descriptors: Vec<_> = desc_chain.clone().collect();
|
||||
let desc_in_hdr = descriptors[descriptors.len() - 1];
|
||||
let mut len = size_of::<VirtioI2cInHdr>() as u32;
|
||||
|
||||
if descriptors.len() == 3 {
|
||||
let desc_buf = descriptors[1];
|
||||
|
||||
// Write the data read from the I2C device
|
||||
if reqs[i].flags == I2C_M_RD {
|
||||
desc_chain
|
||||
.memory()
|
||||
.write(&reqs[i].buf, desc_buf.addr())
|
||||
.map_err(|_| Error::DescriptorWriteFailed)?;
|
||||
}
|
||||
|
||||
if in_hdr.status == VIRTIO_I2C_MSG_OK {
|
||||
len += desc_buf.len();
|
||||
}
|
||||
}
|
||||
|
||||
// Write the transfer status
|
||||
desc_chain
|
||||
.memory()
|
||||
.write_obj::<VirtioI2cInHdr>(in_hdr, desc_in_hdr.addr())
|
||||
.map_err(|_| Error::DescriptorWriteFailed)?;
|
||||
|
||||
if let Some(vring) = vring {
|
||||
if vring.add_used(desc_chain.head_index(), len).is_err() {
|
||||
warn!("Couldn't return used descriptors to the ring");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
impl<D: I2cDevice> VhostUserI2cBackend<D> {
|
||||
pub fn new(i2c_map: Arc<I2cMap<D>>) -> Result<Self> {
|
||||
Ok(VhostUserI2cBackend {
|
||||
@ -107,8 +243,6 @@ impl<D: I2cDevice> VhostUserI2cBackend<D> {
|
||||
|
||||
/// Process the requests in the vring and dispatch replies
|
||||
fn process_queue(&self, vring: &VringRwLock) -> Result<bool> {
|
||||
let mut reqs: Vec<I2cReq> = Vec::new();
|
||||
|
||||
let requests: Vec<_> = vring
|
||||
.get_mut()
|
||||
.get_queue_mut()
|
||||
@ -116,129 +250,13 @@ impl<D: I2cDevice> VhostUserI2cBackend<D> {
|
||||
.map_err(|_| Error::DescriptorNotFound)?
|
||||
.collect();
|
||||
|
||||
if requests.is_empty() {
|
||||
return Ok(true);
|
||||
if process_requests(&self.i2c_map, requests, Some(vring))? {
|
||||
// Send notification once all the requests are processed
|
||||
vring
|
||||
.signal_used_queue()
|
||||
.map_err(|_| Error::NotificationFailed)?;
|
||||
}
|
||||
|
||||
// Iterate over each I2C request and push it to "reqs" vector.
|
||||
for desc_chain in requests.clone() {
|
||||
let descriptors: Vec<_> = desc_chain.clone().collect();
|
||||
|
||||
if (descriptors.len() != 2) && (descriptors.len() != 3) {
|
||||
return Err(Error::UnexpectedDescriptorCount);
|
||||
}
|
||||
|
||||
let desc_out_hdr = descriptors[0];
|
||||
if desc_out_hdr.is_write_only() {
|
||||
return Err(Error::UnexpectedWriteOnlyDescriptor);
|
||||
}
|
||||
|
||||
if desc_out_hdr.len() as usize != size_of::<VirtioI2cOutHdr>() {
|
||||
return Err(Error::UnexpectedDescriptorSize);
|
||||
}
|
||||
|
||||
let out_hdr = desc_chain
|
||||
.memory()
|
||||
.read_obj::<VirtioI2cOutHdr>(desc_out_hdr.addr())
|
||||
.map_err(|_| Error::DescriptorReadFailed)?;
|
||||
|
||||
let flags = match out_hdr.flags.to_native() & VIRTIO_I2C_FLAGS_M_RD {
|
||||
VIRTIO_I2C_FLAGS_M_RD => I2C_M_RD,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
let desc_in_hdr = descriptors[descriptors.len() - 1];
|
||||
if !desc_in_hdr.is_write_only() {
|
||||
return Err(Error::UnexpectedReadableDescriptor);
|
||||
}
|
||||
|
||||
if desc_in_hdr.len() as usize != size_of::<u8>() {
|
||||
return Err(Error::UnexpectedDescriptorSize);
|
||||
}
|
||||
|
||||
let (buf, len) = match descriptors.len() {
|
||||
// Buffer is available
|
||||
3 => {
|
||||
let desc_buf = descriptors[1];
|
||||
let len = desc_buf.len();
|
||||
|
||||
if len == 0 {
|
||||
return Err(Error::UnexpectedDescriptorSize);
|
||||
}
|
||||
let mut buf = vec![0; len as usize];
|
||||
|
||||
if flags != I2C_M_RD {
|
||||
if desc_buf.is_write_only() {
|
||||
return Err(Error::UnexpectedWriteOnlyDescriptor);
|
||||
}
|
||||
|
||||
desc_chain
|
||||
.memory()
|
||||
.read(&mut buf, desc_buf.addr())
|
||||
.map_err(|_| Error::DescriptorReadFailed)?;
|
||||
} else if !desc_buf.is_write_only() {
|
||||
return Err(Error::UnexpectedReadableDescriptor);
|
||||
}
|
||||
|
||||
(buf, len)
|
||||
}
|
||||
|
||||
_ => (Vec::<u8>::new(), 0),
|
||||
};
|
||||
|
||||
reqs.push(I2cReq {
|
||||
addr: out_hdr.addr.to_native() >> 1,
|
||||
flags,
|
||||
len: len as u16,
|
||||
buf,
|
||||
});
|
||||
}
|
||||
|
||||
let in_hdr = {
|
||||
VirtioI2cInHdr {
|
||||
status: match self.i2c_map.transfer(&mut reqs) {
|
||||
Ok(()) => VIRTIO_I2C_MSG_OK,
|
||||
Err(_) => VIRTIO_I2C_MSG_ERR,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
for (i, desc_chain) in requests.iter().enumerate() {
|
||||
let descriptors: Vec<_> = desc_chain.clone().collect();
|
||||
let desc_in_hdr = descriptors[descriptors.len() - 1];
|
||||
let mut len = size_of::<VirtioI2cInHdr>() as u32;
|
||||
|
||||
if descriptors.len() == 3 {
|
||||
let desc_buf = descriptors[1];
|
||||
|
||||
// Write the data read from the I2C device
|
||||
if reqs[i].flags == I2C_M_RD {
|
||||
desc_chain
|
||||
.memory()
|
||||
.write(&reqs[i].buf, desc_buf.addr())
|
||||
.map_err(|_| Error::DescriptorWriteFailed)?;
|
||||
}
|
||||
|
||||
if in_hdr.status == VIRTIO_I2C_MSG_OK {
|
||||
len += desc_buf.len();
|
||||
}
|
||||
}
|
||||
|
||||
// Write the transfer status
|
||||
desc_chain
|
||||
.memory()
|
||||
.write_obj::<VirtioI2cInHdr>(in_hdr, desc_in_hdr.addr())
|
||||
.map_err(|_| Error::DescriptorWriteFailed)?;
|
||||
|
||||
if vring.add_used(desc_chain.head_index(), len).is_err() {
|
||||
warn!("Couldn't return used descriptors to the ring");
|
||||
}
|
||||
}
|
||||
|
||||
// Send notification once all the requests are processed
|
||||
vring
|
||||
.signal_used_queue()
|
||||
.map_err(|_| Error::NotificationFailed)?;
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user