[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:
Viresh Kumar 2021-11-17 20:11:13 +05:30
parent 0cc0fe4f7a
commit f9f3bdf845
2 changed files with 142 additions and 123 deletions

View File

@ -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"

View File

@ -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)
}
}