From 4cbcc26f5c34d8254a9f98e065a923abb194ba8b Mon Sep 17 00:00:00 2001 From: Xiong Zhang Date: Thu, 13 Feb 2020 19:36:49 +0800 Subject: Virtio-net: Add multi queues in multi threads In order to support 2 vq pairs for virtio-net, this patch create 2 sockets in one tap interface, 2 vq pairs(rx/tx) and 2 threads in device model, So one vq pair has one thread and one socket in tap corresponding. On my SkyLake desktop with crosvm and ubuntu 18.04 guest: If run one iperf server on host and one iperf client in guest, 2 vq pairs has the same netwrok bandwidth as 1 vq pair, the bandwidth is 5.99Gbits/sec If run two iperf server on host and two iperf client in guest, In 1 vq pair mode, two iperf cliens bandwidth are 3.19Gbits/sec and 3.18Gbits/sec. In 2 vq pairs mode, two iperf clients bandwidth are 4.87Gbits/sec and 4.86Gbits/sec. So 2 vq pairs improve net bandwidth 52.7% compared with 1 vq pair in this case. BUG=chromium:1064482 TEST=Run iperf test in guest Change-Id: I1fa14d7e24085552dc828a89a883d4a2ada34789 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2099754 Tested-by: kokoro Commit-Queue: Xiong Zhang Reviewed-by: Stephen Barber --- net_util/src/lib.rs | 111 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 74 insertions(+), 37 deletions(-) (limited to 'net_util') diff --git a/net_util/src/lib.rs b/net_util/src/lib.rs index 0243552..a91a116 100644 --- a/net_util/src/lib.rs +++ b/net_util/src/lib.rs @@ -188,13 +188,54 @@ impl Tap { if_flags: ifreq.ifr_ifru.ifru_flags, }) } + + fn create_tap_with_ifreq(ifreq: &mut net_sys::ifreq) -> Result { + // Open calls are safe because we give a constant nul-terminated + // string and verify the result. + let fd = unsafe { + libc::open( + b"/dev/net/tun\0".as_ptr() as *const c_char, + libc::O_RDWR | libc::O_NONBLOCK | libc::O_CLOEXEC, + ) + }; + if fd < 0 { + return Err(Error::OpenTun(SysError::last())); + } + + // We just checked that the fd is valid. + let tuntap = unsafe { File::from_raw_fd(fd) }; + // ioctl is safe since we call it with a valid tap fd and check the return + // value. + let ret = unsafe { ioctl_with_mut_ref(&tuntap, net_sys::TUNSETIFF(), ifreq) }; + + if ret < 0 { + let error = SysError::last(); + + // In a non-root, test environment, we won't have permission to call this; allow + if !(cfg!(test) && error.errno() == EPERM) { + return Err(Error::CreateTap(error)); + } + } + + // Safe since only the name is accessed, and it's copied out. + Ok(Tap { + tap_file: tuntap, + if_name: unsafe { ifreq.ifr_ifrn.ifrn_name }, + if_flags: unsafe { ifreq.ifr_ifru.ifru_flags }, + }) + } } pub trait TapT: FileReadWriteVolatile + Read + Write + AsRawFd + Send + Sized { /// Create a new tap interface. Set the `vnet_hdr` flag to true to allow offloading on this tap, /// which will add an extra 12 byte virtio net header to incoming frames. Offloading cannot /// be used if `vnet_hdr` is false. - fn new(vnet_hdr: bool) -> Result; + /// set 'multi_vq' to ture, if tap have multi virt queue pairs + fn new(vnet_hdr: bool, multi_vq: bool) -> Result; + + /// Change the origin tap into multiqueue taps, this means create other taps based on the + /// origin tap. + fn into_mq_taps(self, vq_pairs: u16) -> Result>; /// Get the host-side IP address for the tap interface. fn ip_addr(&self) -> Result; @@ -230,22 +271,7 @@ pub trait TapT: FileReadWriteVolatile + Read + Write + AsRawFd + Send + Sized { } impl TapT for Tap { - fn new(vnet_hdr: bool) -> Result { - // Open calls are safe because we give a constant nul-terminated - // string and verify the result. - let fd = unsafe { - libc::open( - b"/dev/net/tun\0".as_ptr() as *const c_char, - libc::O_RDWR | libc::O_NONBLOCK | libc::O_CLOEXEC, - ) - }; - if fd < 0 { - return Err(Error::OpenTun(SysError::last())); - } - - // We just checked that the fd is valid. - let tuntap = unsafe { File::from_raw_fd(fd) }; - + fn new(vnet_hdr: bool, multi_vq: bool) -> Result { const TUNTAP_DEV_FORMAT: &[u8; 8usize] = b"vmtap%d\0"; // This is pretty messy because of the unions used by ifreq. Since we @@ -262,27 +288,34 @@ impl TapT for Tap { | net_sys::IFF_NO_PI | if vnet_hdr { net_sys::IFF_VNET_HDR } else { 0 }) as c_short; + if multi_vq { + ifreq.ifr_ifru.ifru_flags |= net_sys::IFF_MULTI_QUEUE as c_short; + } } - // ioctl is safe since we call it with a valid tap fd and check the return - // value. - let ret = unsafe { ioctl_with_mut_ref(&tuntap, net_sys::TUNSETIFF(), &mut ifreq) }; + Tap::create_tap_with_ifreq(&mut ifreq) + } - if ret < 0 { - let error = SysError::last(); + fn into_mq_taps(self, vq_pairs: u16) -> Result> { + let mut taps: Vec = Vec::new(); - // In a non-root, test environment, we won't have permission to call this; allow - if !(cfg!(test) && error.errno() == EPERM) { - return Err(Error::CreateTap(error)); - } + if vq_pairs <= 1 { + taps.push(self); + return Ok(taps); } - // Safe since only the name is accessed, and it's copied out. - Ok(Tap { - tap_file: tuntap, - if_name: unsafe { ifreq.ifr_ifrn.ifrn_name }, - if_flags: unsafe { ifreq.ifr_ifru.ifru_flags }, - }) + // Add other socket into the origin tap interface + for _ in 0..vq_pairs - 1 { + let mut ifreq = self.get_ifreq(); + let tap = Tap::create_tap_with_ifreq(&mut ifreq)?; + + tap.enable()?; + + taps.push(tap); + } + + taps.insert(0, self); + Ok(taps) } fn ip_addr(&self) -> Result { @@ -499,7 +532,7 @@ pub mod fakes { } impl TapT for FakeTap { - fn new(_: bool) -> Result { + fn new(_: bool, _: bool) -> Result { Ok(FakeTap { tap_file: OpenOptions::new() .read(true) @@ -510,6 +543,10 @@ pub mod fakes { }) } + fn into_mq_taps(self, _vq_pairs: u16) -> Result> { + Ok(Vec::new()) + } + fn ip_addr(&self) -> Result { Ok(net::Ipv4Addr::new(1, 2, 3, 4)) } @@ -600,12 +637,12 @@ mod tests { #[test] fn tap_create() { - Tap::new(true).unwrap(); + Tap::new(true, false).unwrap(); } #[test] fn tap_configure() { - let tap = Tap::new(true).unwrap(); + let tap = Tap::new(true, false).unwrap(); let ip_addr: net::Ipv4Addr = "100.115.92.5".parse().unwrap(); let netmask: net::Ipv4Addr = "255.255.255.252".parse().unwrap(); let mac_addr: MacAddress = "a2:06:b9:3d:68:4d".parse().unwrap(); @@ -625,14 +662,14 @@ mod tests { #[ignore] fn root_only_tests() { // This line will fail to provide an initialized FD if the test is not run as root. - let tap = Tap::new(true).unwrap(); + let tap = Tap::new(true, false).unwrap(); tap.set_vnet_hdr_size(16).unwrap(); tap.set_offload(0).unwrap(); } #[test] fn tap_enable() { - let tap = Tap::new(true).unwrap(); + let tap = Tap::new(true, false).unwrap(); let ret = tap.enable(); assert_ok_or_perm_denied(ret); -- cgit 1.4.1