From f9f3bdf8453a4e62205c73f6fd721946375853e8 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 17 Nov 2021 20:11:13 +0530 Subject: [PATCH] [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 --- src/i2c/Cargo.toml | 1 + src/i2c/src/vhu_i2c.rs | 264 ++++++++++++++++++++++------------------- 2 files changed, 142 insertions(+), 123 deletions(-) diff --git a/src/i2c/Cargo.toml b/src/i2c/Cargo.toml index 2738dda..6998c5a 100644 --- a/src/i2c/Cargo.toml +++ b/src/i2c/Cargo.toml @@ -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" diff --git a/src/i2c/src/vhu_i2c.rs b/src/i2c/src/vhu_i2c.rs index f5ff3ea..bfa74a2 100644 --- a/src/i2c/src/vhu_i2c.rs +++ b/src/i2c/src/vhu_i2c.rs @@ -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 { pub exit_event: EventFd, } +type I2cDescriptorChain = DescriptorChain>>; + +/// Process the requests in the vring and dispatch replies +fn process_requests( + i2c_map: &Arc>, + requests: Vec, + vring: Option<&VringRwLock>, +) -> Result { + let mut reqs: Vec = 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::() { + return Err(Error::UnexpectedDescriptorSize); + } + + let out_hdr = desc_chain + .memory() + .read_obj::(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::() { + 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::::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::() 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::(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 VhostUserI2cBackend { pub fn new(i2c_map: Arc>) -> Result { Ok(VhostUserI2cBackend { @@ -107,8 +243,6 @@ impl VhostUserI2cBackend { /// Process the requests in the vring and dispatch replies fn process_queue(&self, vring: &VringRwLock) -> Result { - let mut reqs: Vec = Vec::new(); - let requests: Vec<_> = vring .get_mut() .get_queue_mut() @@ -116,129 +250,13 @@ impl VhostUserI2cBackend { .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::() { - return Err(Error::UnexpectedDescriptorSize); - } - - let out_hdr = desc_chain - .memory() - .read_obj::(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::() { - 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::::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::() 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::(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) } }