[ovs-dev] [PATCH v4] ovs-tcpdump: Improve performance with dummy interface

Mike Pattrick mkp at redhat.com
Fri Nov 5 19:38:26 UTC 2021


Currently the ovs-tcpdump utility creates a virtual tunnel to send
packets to. This method functions perfectly fine, however, it can
greatly impact performance of the monitored port.

It has been reported to reduce packet throughput significantly. I was
able to reproduce a reduction in throughput of up 70 percent in some
tests with a simple setup of two hosts communicating through a single
bridge on Linux with the kernel module datapath. Another more complex
test was configured for the usermode datapath both with and without
DPDK. This test involved a data path going from a VM, through a port
into one OVS bridge, out through a network card which could be DPDK
enabled for the relevant tests, in to a different network interface,
then into a different OVS bridge, through another port, and then into
a virtual machine.

Using the dummy driver resulted in the following impact to performance
compared to no ovs-tcpdump. Due to intra-test variance and fluctuations
during the first few seconds after installing a tap; multiple samples
were taken over multiple test runs. The first few seconds worth of
results were discarded and then results were averaged out.

If the dummy driver isn't present, falls back on the existing tap code.

Original Script
===============
Category             Impact on Throughput
Kernel datapath    - 65%
Usermode (no DPDK) - 26%
DPDK ports in use  - 37%

New Script
==========
Category             Impact on Throughput
Kernel datapath    - 5%
Usermode (no DPDK) - 16%
DPDK ports in use  - 29%

Signed-off-by: Mike Pattrick <mkp at redhat.com>
---
 utilities/ovs-tcpdump.in | 54 ++++++++++++++++++++++++++--------------
 1 file changed, 36 insertions(+), 18 deletions(-)

diff --git a/utilities/ovs-tcpdump.in b/utilities/ovs-tcpdump.in
index 5ec02383c..82d1bedfa 100755
--- a/utilities/ovs-tcpdump.in
+++ b/utilities/ovs-tcpdump.in
@@ -14,15 +14,14 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import fcntl
-
 import os
 import pwd
 from random import randint
-import struct
 import subprocess
 import sys
 import time
+import struct
+import fcntl
 
 try:
     from netifaces import interfaces
@@ -52,8 +51,8 @@ except Exception:
     print("       the correct location.")
     sys.exit(1)
 
-tapdev_fd = None
 _make_taps = {}
+_del_taps = {}
 _make_mirror_name = {}
 IFNAMSIZ_LINUX = 15      # this is the max name size, excluding the null byte.
 
@@ -67,21 +66,25 @@ def _doexec(*args, **kwargs):
     return proc
 
 
-def _install_tap_linux(tap_name, mtu_value=None):
-    """Uses /dev/net/tun to create a tap device"""
-    global tapdev_fd
+def _install_dst_if_linux(tap_name, mtu_value=None):
+    ret_code = _doexec(
+        *['ip', 'link', 'add', str(tap_name), 'type', 'dummy']
+        ).wait()
 
-    IFF_TAP = 0x0002
-    IFF_NO_PI = 0x1000
-    TUNSETIFF = 0x400454CA  # This is derived by printf() of TUNSETIFF
-    TUNSETOWNER = TUNSETIFF + 2
+    if ret_code != 0:
+        """If dummy driver is not present, fall back on /dev/net/tun"""
+        IFF_TAP = 0x0002
+        IFF_NO_PI = 0x1000
+        TUNSETIFF = 0x400454CA  # This is derived by printf() of TUNSETIFF
+        TUNSETOWNER = TUNSETIFF + 2
 
-    tapdev_fd = os.open('/dev/net/tun', os.O_RDWR)
-    ifr = struct.pack('16sH', tap_name.encode('utf8'), IFF_TAP | IFF_NO_PI)
-    fcntl.ioctl(tapdev_fd, TUNSETIFF, ifr)
-    fcntl.ioctl(tapdev_fd, TUNSETOWNER, os.getegid())
+        tapdev_fd = os.open('/dev/net/tun', os.O_RDWR)
+        ifr = struct.pack('16sH', tap_name.encode('utf8'), IFF_TAP | IFF_NO_PI)
+        fcntl.ioctl(tapdev_fd, TUNSETIFF, ifr)
+        fcntl.ioctl(tapdev_fd, TUNSETOWNER, os.getegid())
+
+        time.sleep(1)  # required to give the new device settling time
 
-    time.sleep(1)  # required to give the new device settling time
     if mtu_value is not None:
         pipe = _doexec(
             *(['ip', 'link', 'set', 'dev', str(tap_name), 'mtu',
@@ -93,14 +96,22 @@ def _install_tap_linux(tap_name, mtu_value=None):
     pipe.wait()
 
 
+def _remove_dst_if_linux(tap_name):
+    _doexec(
+        *['ip', 'link', 'del', str(tap_name)]
+        ).wait()
+
+
 def _make_linux_mirror_name(interface_name):
     if len(interface_name) > IFNAMSIZ_LINUX - 2:
         return "ovsmi%06d" % randint(1, 999999)
     return "mi%s" % interface_name
 
 
-_make_taps['linux'] = _install_tap_linux
-_make_taps['linux2'] = _install_tap_linux
+_make_taps['linux'] = _install_dst_if_linux
+_make_taps['linux2'] = _install_dst_if_linux
+_del_taps['linux'] = _remove_dst_if_linux
+_del_taps['linux2'] = _remove_dst_if_linux
 _make_mirror_name['linux'] = _make_linux_mirror_name
 _make_mirror_name['linux2'] = _make_linux_mirror_name
 
@@ -455,6 +466,9 @@ def main():
        mirror_interface not in interfaces():
         _make_taps[sys.platform](mirror_interface,
                                  ovsdb.interface_mtu(interface))
+        tap_created = True
+    else:
+        tap_created = False
 
     if mirror_interface not in interfaces():
         print("ERROR: Please create an interface called `%s`" %
@@ -480,6 +494,8 @@ def main():
         print("ERROR: Unable to properly setup the mirror: %s." % str(oe))
         try:
             ovsdb.destroy_port(mirror_interface, ovsdb.port_bridge(interface))
+            if tap_created is True:
+                _del_taps[sys.platform](mirror_interface)
         except Exception:
             pass
         sys.exit(1)
@@ -498,6 +514,8 @@ def main():
 
         ovsdb.destroy_mirror(interface, ovsdb.port_bridge(interface))
         ovsdb.destroy_port(mirror_interface, ovsdb.port_bridge(interface))
+        if tap_created is True:
+            _del_taps[sys.platform](mirror_interface)
     except Exception:
         print("Unable to tear down the ports and mirrors.")
         print("Please use ovs-vsctl to remove the ports and mirrors created.")
-- 
2.30.2




More information about the dev mailing list