summary refs log tree commit diff
path: root/net_util
diff options
context:
space:
mode:
authorXiong Zhang <xiong.y.zhang@intel.corp-partner.google.com>2020-02-13 19:36:49 +0800
committerCommit Bot <commit-bot@chromium.org>2020-04-04 05:57:05 +0000
commit4cbcc26f5c34d8254a9f98e065a923abb194ba8b (patch)
tree5f68084f0bbca71efbabb5996b79813d7eccd04b /net_util
parent664cc3ca49cb58d5bf7d936686fd211d6dd728bf (diff)
downloadcrosvm-4cbcc26f5c34d8254a9f98e065a923abb194ba8b.tar
crosvm-4cbcc26f5c34d8254a9f98e065a923abb194ba8b.tar.gz
crosvm-4cbcc26f5c34d8254a9f98e065a923abb194ba8b.tar.bz2
crosvm-4cbcc26f5c34d8254a9f98e065a923abb194ba8b.tar.lz
crosvm-4cbcc26f5c34d8254a9f98e065a923abb194ba8b.tar.xz
crosvm-4cbcc26f5c34d8254a9f98e065a923abb194ba8b.tar.zst
crosvm-4cbcc26f5c34d8254a9f98e065a923abb194ba8b.zip
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 <noreply+kokoro@google.com>
Commit-Queue: Xiong  Zhang <xiong.y.zhang@intel.corp-partner.google.com>
Reviewed-by: Stephen Barber <smbarber@chromium.org>
Diffstat (limited to 'net_util')
-rw-r--r--net_util/src/lib.rs111
1 files changed, 74 insertions, 37 deletions
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<Tap> {
+        // 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<Self>;
+    /// set 'multi_vq' to ture, if tap have multi virt queue pairs
+    fn new(vnet_hdr: bool, multi_vq: bool) -> Result<Self>;
+
+    /// 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<Vec<Self>>;
 
     /// Get the host-side IP address for the tap interface.
     fn ip_addr(&self) -> Result<net::Ipv4Addr>;
@@ -230,22 +271,7 @@ pub trait TapT: FileReadWriteVolatile + Read + Write + AsRawFd + Send + Sized {
 }
 
 impl TapT for Tap {
-    fn new(vnet_hdr: bool) -> Result<Tap> {
-        // 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<Tap> {
         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<Vec<Tap>> {
+        let mut taps: Vec<Tap> = 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<net::Ipv4Addr> {
@@ -499,7 +532,7 @@ pub mod fakes {
     }
 
     impl TapT for FakeTap {
-        fn new(_: bool) -> Result<FakeTap> {
+        fn new(_: bool, _: bool) -> Result<FakeTap> {
             Ok(FakeTap {
                 tap_file: OpenOptions::new()
                     .read(true)
@@ -510,6 +543,10 @@ pub mod fakes {
             })
         }
 
+        fn into_mq_taps(self, _vq_pairs: u16) -> Result<Vec<FakeTap>> {
+            Ok(Vec::new())
+        }
+
         fn ip_addr(&self) -> Result<net::Ipv4Addr> {
             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);