net_util, virtio-devices, vmm: Accept multiple TAP fds

This patch enables multi-queue support for creating virtio-net devices by
accepting multiple TAP fds, e.g. '--net fds=3:7'.

Fixes: #2164

Signed-off-by: Bo Chen <chen.bo@intel.com>
This commit is contained in:
Bo Chen 2021-01-26 20:52:50 -08:00 committed by Rob Bradford
parent 5209026f52
commit 6664e5a6e7
4 changed files with 43 additions and 17 deletions

View File

@ -153,7 +153,7 @@ impl Tap {
Self::open_named("vmtap%d", num_queue_pairs, None) Self::open_named("vmtap%d", num_queue_pairs, None)
} }
pub fn from_tap_fd(fd: RawFd) -> Result<Tap> { pub fn from_tap_fd(fd: RawFd, num_queue_pairs: usize) -> Result<Tap> {
// Ensure that the file is opened non-blocking, this is particularly // Ensure that the file is opened non-blocking, this is particularly
// needed when opened via the shell for macvtap. // needed when opened via the shell for macvtap.
let ret = unsafe { let ret = unsafe {
@ -181,6 +181,9 @@ impl Tap {
let ifru_flags = ifreq.ifr_ifru.ifru_flags.as_mut(); let ifru_flags = ifreq.ifr_ifru.ifru_flags.as_mut();
*ifru_flags = *ifru_flags =
(net_gen::IFF_TAP | net_gen::IFF_NO_PI | net_gen::IFF_VNET_HDR) as c_short; (net_gen::IFF_TAP | net_gen::IFF_NO_PI | net_gen::IFF_VNET_HDR) as c_short;
if num_queue_pairs > 1 {
*ifru_flags |= net_gen::IFF_MULTI_QUEUE as c_short;
}
} }
let ret = unsafe { ioctl_with_mut_ref(&tap_file, net_gen::TUNSETIFF(), &mut ifreq) }; let ret = unsafe { ioctl_with_mut_ref(&tap_file, net_gen::TUNSETIFF(), &mut ifreq) };
if ret < 0 && IoError::last_os_error().raw_os_error().unwrap() != libc::EEXIST { if ret < 0 && IoError::last_os_error().raw_os_error().unwrap() != libc::EEXIST {
@ -591,7 +594,7 @@ mod tests {
let orig_tap = Tap::new(1).unwrap(); let orig_tap = Tap::new(1).unwrap();
let fd = orig_tap.as_raw_fd(); let fd = orig_tap.as_raw_fd();
let _new_tap = Tap::from_tap_fd(fd).unwrap(); let _new_tap = Tap::from_tap_fd(fd, 1).unwrap();
} }
#[test] #[test]

View File

@ -297,21 +297,28 @@ impl Net {
) )
} }
pub fn from_tap_fd( pub fn from_tap_fds(
id: String, id: String,
fd: RawFd, fds: &[RawFd],
guest_mac: Option<MacAddr>, guest_mac: Option<MacAddr>,
iommu: bool, iommu: bool,
queue_size: u16, queue_size: u16,
seccomp_action: SeccompAction, seccomp_action: SeccompAction,
) -> Result<Self> { ) -> Result<Self> {
let tap = Tap::from_tap_fd(fd).map_err(Error::TapError)?; let mut taps: Vec<Tap> = Vec::new();
let num_queue_pairs = fds.len();
for fd in fds.iter() {
let tap = Tap::from_tap_fd(*fd, num_queue_pairs).map_err(Error::TapError)?;
taps.push(tap);
}
Self::new_with_tap( Self::new_with_tap(
id, id,
vec![tap], taps,
guest_mac, guest_mac,
iommu, iommu,
2, num_queue_pairs * 2,
queue_size, queue_size,
seccomp_action, seccomp_action,
) )

View File

@ -107,6 +107,8 @@ pub enum ValidationError {
CpuTopologyZeroPart, CpuTopologyZeroPart,
/// Virtio needs a min of 2 queues /// Virtio needs a min of 2 queues
VnetQueueLowerThan2, VnetQueueLowerThan2,
/// The input queue number for virtio_net must match the number of input fds
VnetQueueFdMismatch,
} }
type ValidationResult<T> = std::result::Result<T, ValidationError>; type ValidationResult<T> = std::result::Result<T, ValidationError>;
@ -132,6 +134,10 @@ impl fmt::Display for ValidationError {
"Product of CPU topology parts does not match maximum vCPUs" "Product of CPU topology parts does not match maximum vCPUs"
), ),
VnetQueueLowerThan2 => write!(f, "Number of queues to virtio_net less than 2"), VnetQueueLowerThan2 => write!(f, "Number of queues to virtio_net less than 2"),
VnetQueueFdMismatch => write!(
f,
"Number of queues to virtio_net does not match the number of input FDs"
),
} }
} }
} }
@ -765,7 +771,7 @@ pub struct NetConfig {
#[serde(default)] #[serde(default)]
pub id: Option<String>, pub id: Option<String>,
#[serde(default)] #[serde(default)]
pub fd: Option<i32>, pub fds: Option<Vec<i32>>,
} }
fn default_netconfig_tap() -> Option<String> { fn default_netconfig_tap() -> Option<String> {
@ -806,14 +812,14 @@ impl Default for NetConfig {
vhost_user: false, vhost_user: false,
vhost_socket: None, vhost_socket: None,
id: None, id: None,
fd: None, fds: None,
} }
} }
} }
impl NetConfig { impl NetConfig {
pub const SYNTAX: &'static str = "Network parameters \ pub const SYNTAX: &'static str = "Network parameters \
\"tap=<if_name>,ip=<ip_addr>,mask=<net_mask>,mac=<mac_addr>,fd=<fd>,iommu=on|off,\ \"tap=<if_name>,ip=<ip_addr>,mask=<net_mask>,mac=<mac_addr>,fd=<fd1:fd2...>,iommu=on|off,\
num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,\ num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,\
vhost_user=<vhost_user_enable>,socket=<vhost_user_socket_path>,id=<device_id>\""; vhost_user=<vhost_user_enable>,socket=<vhost_user_socket_path>,id=<device_id>\"";
@ -869,7 +875,11 @@ impl NetConfig {
.0; .0;
let vhost_socket = parser.get("socket"); let vhost_socket = parser.get("socket");
let id = parser.get("id"); let id = parser.get("id");
let fd = parser.convert("fd").map_err(Error::ParseNetwork)?; let fds = parser
.convert::<IntegerList>("fd")
.map_err(Error::ParseNetwork)?
.map(|v| v.0.iter().map(|e| *e as i32).collect());
let config = NetConfig { let config = NetConfig {
tap, tap,
ip, ip,
@ -882,7 +892,7 @@ impl NetConfig {
vhost_user, vhost_user,
vhost_socket, vhost_socket,
id, id,
fd, fds,
}; };
config.validate().map_err(Error::Validation)?; config.validate().map_err(Error::Validation)?;
Ok(config) Ok(config)
@ -891,6 +901,11 @@ impl NetConfig {
if self.num_queues < 2 { if self.num_queues < 2 {
return Err(ValidationError::VnetQueueLowerThan2); return Err(ValidationError::VnetQueueLowerThan2);
} }
if self.fds.is_some() && self.fds.as_ref().unwrap().len() * 2 != self.num_queues {
return Err(ValidationError::VnetQueueFdMismatch);
}
Ok(()) Ok(())
} }
} }
@ -1949,10 +1964,11 @@ mod tests {
); );
assert_eq!( assert_eq!(
NetConfig::parse("mac=de:ad:be:ef:12:34,fd=3")?, NetConfig::parse("mac=de:ad:be:ef:12:34,fd=3:7,num_queues=4")?,
NetConfig { NetConfig {
mac: MacAddr::parse_str("de:ad:be:ef:12:34").unwrap(), mac: MacAddr::parse_str("de:ad:be:ef:12:34").unwrap(),
fd: Some(3), fds: Some(vec![3, 7]),
num_queues: 4,
..Default::default() ..Default::default()
} }
); );

View File

@ -1790,11 +1790,11 @@ impl DeviceManager {
) )
.map_err(DeviceManagerError::CreateVirtioNet)?, .map_err(DeviceManagerError::CreateVirtioNet)?,
)) ))
} else if let Some(fd) = net_cfg.fd { } else if let Some(fds) = &net_cfg.fds {
Arc::new(Mutex::new( Arc::new(Mutex::new(
virtio_devices::Net::from_tap_fd( virtio_devices::Net::from_tap_fds(
id.clone(), id.clone(),
fd, fds,
Some(net_cfg.mac), Some(net_cfg.mac),
net_cfg.iommu, net_cfg.iommu,
net_cfg.queue_size, net_cfg.queue_size,