Merge "hugepage_doc: Add hugepage configuration info to installation doc"
[vswitchperf.git] / src / dpdk / dpdk.py
index 01e1fb6..30f228f 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright 2015 Intel Corporation.
+# Copyright 2015-2016 Intel Corporation.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 
 """Automation of system configuration for DPDK use.
 
-Parts of this based on ``tools/pci_unbind.py`` script from Intel(R) DPDK.
+Parts of this based on ``tools/dpdk_nic_bind.py`` script from Intel(R)
+DPDK.
 """
 
 from sys import platform as _platform
 
 import os
-import re
 import subprocess
 import logging
-import locale
 
 from tools import tasks
 from conf import settings
-from tools.module_manager import ModuleManager, KernelModuleInsertMode
+from tools.module_manager import ModuleManager
 
 _LOGGER = logging.getLogger(__name__)
 RTE_PCI_TOOL = os.path.join(
-    settings.getValue('RTE_SDK'), 'tools', 'dpdk_nic_bind.py')
+    settings.getValue('RTE_SDK_USER'), 'tools', 'dpdk_nic_bind.py')
 
-_DPDK_MODULE_MANAGER = ModuleManager(KernelModuleInsertMode.MODPROBE)
+_DPDK_MODULE_MANAGER = ModuleManager()
+
+# declare global NIC variables only as their content might not be known yet
+_NICS = []
+_NICS_PCI = []
 
 #
 # system management
 #
 
-
 def init():
     """Setup system for DPDK.
     """
+    global _NICS
+    global _NICS_PCI
+    _NICS = settings.getValue('NICS')
+    _NICS_PCI = list(nic['pci'] for nic in _NICS)
     if not _is_linux():
         _LOGGER.error('Not running on a compatible Linux version. Exiting...')
         return
-
-    _mount_hugepages()
     _insert_modules()
     _remove_vhost_net()
     _bind_nics()
-    _copy_dpdk_for_guest()
 
 
 def cleanup():
@@ -63,34 +66,12 @@ def cleanup():
 
     _unbind_nics()
     _remove_modules()
-    _umount_hugepages()
     _vhost_user_cleanup()
 
-
-#
-# vhost specific modules management
-#
-
-
-def insert_vhost_modules():
-    """Inserts VHOST related kernel modules
-    """
-    mod_path_prefix = os.path.join(settings.getValue('RTE_SDK'),
-                                   'lib',
-                                   'librte_vhost')
-    _insert_module_group('VHOST_MODULE', mod_path_prefix)
-
-
-def remove_vhost_modules():
-    """Removes all VHOST related kernel modules
-    """
-    _remove_module_group('VHOST_MODULE')
-
 #
 # basic compatibility test
 #
 
-
 def _is_linux():
     """Check if running on Linux.
 
@@ -101,97 +82,10 @@ def _is_linux():
     """
     return _platform.startswith('linux') and os.path.isdir('/proc')
 
-#
-# hugepage management
-#
-
-
-def _is_hugepage_available():
-    """Check if hugepages are available on the system.
-    """
-    hugepage_re = re.compile(r'^HugePages_Free:\s+(?P<num_hp>\d+)$')
-
-    # read in meminfo
-    with open('/proc/meminfo') as mem_file:
-        mem_info = mem_file.readlines()
-
-    # first check if module is loaded
-    for line in mem_info:
-        result = hugepage_re.match(line)
-        if not result:
-            continue
-
-        num_huge = result.group('num_hp')
-        if not num_huge:
-            _LOGGER.info('No free hugepages.')
-        else:
-            _LOGGER.info('Found \'%s\' free hugepage(s).', num_huge)
-        return True
-
-    return False
-
-
-def _is_hugepage_mounted():
-    """Check if hugepages are mounted.
-    """
-    output = subprocess.check_output(['mount'], shell=True)
-    my_encoding = locale.getdefaultlocale()[1]
-    for line in output.decode(my_encoding).split('\n'):
-        if 'hugetlbfs' in line:
-            return True
-
-    return False
-
-
-def _mount_hugepages():
-    """Ensure hugepages are mounted.
-    """
-    if not _is_hugepage_available():
-        return
-
-    if _is_hugepage_mounted():
-        return
-
-    if not os.path.exists(settings.getValue('HUGEPAGE_DIR')):
-        os.makedirs(settings.getValue('HUGEPAGE_DIR'))
-    try:
-        tasks.run_task(['sudo', 'mount', '-t', 'hugetlbfs', 'nodev',
-                        settings.getValue('HUGEPAGE_DIR')],
-                       _LOGGER, 'Mounting hugepages...', True)
-    except subprocess.CalledProcessError:
-        _LOGGER.error('Unable to mount hugepages.')
-
-
-def _umount_hugepages():
-    """Ensure hugepages are unmounted.
-    """
-    if not _is_hugepage_mounted():
-        return
-
-    try:
-        tasks.run_task(['sudo', 'umount', settings.getValue('HUGEPAGE_DIR')],
-                       _LOGGER, 'Unmounting hugepages...', True)
-    except subprocess.CalledProcessError:
-        _LOGGER.error('Unable to umount hugepages.')
-
 #
 # module management
 #
 
-
-def _is_module_inserted(module):
-    """Check if a module is inserted on system.
-    """
-    with open('/proc/modules') as mod_file:
-        loaded_mods = mod_file.readlines()
-
-    # first check if module is loaded
-    for line in loaded_mods:
-        if line.startswith(module):
-            return True
-    return False
-
-
 def _insert_modules():
     """Ensure required modules are inserted on system.
     """
@@ -199,133 +93,55 @@ def _insert_modules():
     _DPDK_MODULE_MANAGER.insert_modules(settings.getValue('SYS_MODULES'))
 
     mod_path_prefix = settings.getValue('OVS_DIR')
-    _insert_module_group('OVS_MODULES', mod_path_prefix)
-    mod_path_prefix = os.path.join(settings.getValue('RTE_SDK'),
-                                   settings.getValue('RTE_TARGET'))
-    _insert_module_group('DPDK_MODULES', mod_path_prefix)
-
-
-def _insert_module_group(module_group, group_path_prefix):
-    """Ensure all modules in a group are inserted into the system.
-
-    :param module_group: A name of configuration item containing a list
-    of module names
-    """
-    for module in settings.getValue(module_group):
-        # first check if module is loaded
-        if _is_module_inserted(module[1]):
-            continue
-
-        try:
-            mod_path = os.path.join(group_path_prefix, module[0],
-                                    '%s.ko' % module[1])
-            tasks.run_task(['sudo', 'insmod', mod_path], _LOGGER,
-                           'Inserting module \'%s\'...' % module[1], True)
-        except subprocess.CalledProcessError:
-            _LOGGER.error('Unable to insert module \'%s\'.', module[1])
-            raise  # fail catastrophically
-
+    _DPDK_MODULE_MANAGER.insert_module_group(settings.getValue('OVS_MODULES'),
+                                             mod_path_prefix)
+    if 'vfio-pci' not in settings.getValue('DPDK_MODULES'):
+        mod_path_prefix = os.path.join(settings.getValue('RTE_SDK'),
+                                       settings.getValue('RTE_TARGET'))
+        _DPDK_MODULE_MANAGER.insert_module_group(settings.getValue('DPDK_MODULES'),
+                                                 mod_path_prefix)
+    else:
+        _DPDK_MODULE_MANAGER.insert_modules(settings.getValue('DPDK_MODULES'))
 
 def _remove_modules():
     """Ensure required modules are removed from system.
     """
-    _remove_module_group('OVS_MODULES')
-    _remove_module_group('DPDK_MODULES')
-
     _DPDK_MODULE_MANAGER.remove_modules()
 
-def _remove_module_group(module_group):
-    """Ensure all modules in a group are removed from the system.
+#
+# vhost specific modules management
+#
 
-    :param module_group: A name of configuration item containing a list
-    of module names
+def insert_vhost_modules():
+    """Inserts VHOST related kernel modules
     """
-    for module in settings.getValue(module_group):
-        # first check if module is loaded
-        if not _is_module_inserted(module[1]):
-            continue
+    mod_path_prefix = os.path.join(settings.getValue('RTE_SDK'),
+                                   'lib',
+                                   'librte_vhost')
+    _DPDK_MODULE_MANAGER.insert_module_group(settings.getValue('VHOST_MODULE'), mod_path_prefix)
 
-        try:
-            tasks.run_task(['sudo', 'rmmod', module[1]], _LOGGER,
-                           'Removing module \'%s\'...' % module[1], True)
-        except subprocess.CalledProcessError:
-            _LOGGER.error('Unable to remove module \'%s\'.', module[1])
-            continue
+
+def remove_vhost_modules():
+    """Removes all VHOST related kernel modules
+    """
+    # all modules are removed automatically by _remove_modules() method
+    pass
 
 
 #
-# 'vhost-net' module management
+# 'vhost-net' module cleanup
 #
 
 def _remove_vhost_net():
     """Remove vhost-net driver and file.
     """
-    if _is_module_inserted('vhost_net'):
-        try:
-            tasks.run_task(['sudo', 'rmmod', 'vhost_net'], _LOGGER,
-                           'Removing \'/dev/vhost-net\' directory...', True)
-        except subprocess.CalledProcessError:
-            _LOGGER.error('Unable to remove module \'vhost_net\'.')
-
+    _DPDK_MODULE_MANAGER.remove_module('vhost-net')
     try:
         tasks.run_task(['sudo', 'rm', '-f', '/dev/vhost-net'], _LOGGER,
                        'Removing \'/dev/vhost-net\' directory...', True)
     except subprocess.CalledProcessError:
         _LOGGER.error('Unable to remove directory \'/dev/vhost-net\'.')
 
-#
-# NIC management
-#
-
-
-def _bind_nics():
-    """Bind NICs using the Intel DPDK ``pci_unbind.py`` tool.
-    """
-    try:
-        tasks.run_task(['sudo', RTE_PCI_TOOL, '--bind', 'igb_uio'] +
-                       settings.getValue('WHITELIST_NICS'), _LOGGER,
-                       'Binding NICs %s...' %
-                       settings.getValue('WHITELIST_NICS'),
-                       True)
-    except subprocess.CalledProcessError:
-        _LOGGER.error('Unable to bind NICs %s',
-                      str(settings.getValue('WHITELIST_NICS')))
-
-
-def _unbind_nics():
-    """Unbind NICs using the Intel DPDK ``pci_unbind.py`` tool.
-    """
-    try:
-        tasks.run_task(['sudo', RTE_PCI_TOOL, '--unbind'] +
-                       settings.getValue('WHITELIST_NICS'), _LOGGER,
-                       'Unbinding NICs %s...' %
-                       str(settings.getValue('WHITELIST_NICS')),
-                       True)
-    except subprocess.CalledProcessError:
-        _LOGGER.error('Unable to unbind NICs %s',
-                      str(settings.getValue('WHITELIST_NICS')))
-
-
-def _copy_dpdk_for_guest():
-    """Copy dpdk code to GUEST_SHARE_DIR for use by guests.
-    """
-    guest_share_dir = os.path.join(
-        settings.getValue('GUEST_SHARE_DIR'), 'DPDK')
-
-    if not os.path.exists(guest_share_dir):
-        os.makedirs(guest_share_dir)
-
-    try:
-        tasks.run_task(['rsync', '-a', '-r', '-l', r'--exclude="\.git"',
-                        os.path.join(settings.getValue('RTE_SDK'), ''),
-                        guest_share_dir],
-                       _LOGGER,
-                       'Copying DPDK to shared directory...',
-                       True)
-    except subprocess.CalledProcessError:
-        _LOGGER.error('Unable to copy DPDK to shared directory')
-
-
 #
 # Vhost-user cleanup
 #
@@ -346,8 +162,56 @@ def _vhost_user_cleanup():
                 _LOGGER.error('Unable to delete vhost-user socket \'%s\'.',
                               sock)
                 continue
+#
+# NIC management
+#
 
 
+def _bind_nics():
+    """Bind NICs using the Intel DPDK ``dpdk_nic_bind.py`` tool.
+    """
+    try:
+        _driver = 'igb_uio'
+        if 'vfio-pci' in settings.getValue('DPDK_MODULES'):
+            _driver = 'vfio-pci'
+            tasks.run_task(['sudo', 'chmod', 'a+x', '/dev/vfio'],
+                           _LOGGER, 'Setting VFIO permissions .. a+x',
+                           True)
+            tasks.run_task(['sudo', 'chmod', '-R', '666', '/dev/vfio/'],
+                           _LOGGER, 'Setting VFIO permissions .. 0666',
+                           True)
+
+        tasks.run_task(['sudo', RTE_PCI_TOOL, '--bind=' + _driver] +
+                       _NICS_PCI, _LOGGER,
+                       'Binding NICs %s...' % _NICS_PCI,
+                       True)
+    except subprocess.CalledProcessError:
+        _LOGGER.error('Unable to bind NICs %s', str(_NICS_PCI))
+
+def _unbind_nics():
+    """Unbind NICs using the Intel DPDK ``dpdk_nic_bind.py`` tool.
+    """
+    try:
+        tasks.run_task(['sudo', RTE_PCI_TOOL, '--unbind'] +
+                       _NICS_PCI, _LOGGER,
+                       'Unbinding NICs %s...' % str(_NICS_PCI),
+                       True)
+    except subprocess.CalledProcessError:
+        _LOGGER.error('Unable to unbind NICs %s', str(_NICS_PCI))
+    # Rebind NICs to their original drivers
+    # using the Intel DPDK ``dpdk_nic_bind.py`` tool.
+    for nic in _NICS:
+        try:
+            if nic['driver']:
+                tasks.run_task(['sudo', RTE_PCI_TOOL, '--bind',
+                                nic['driver'], nic['pci']],
+                               _LOGGER, 'Binding NIC %s to %s...' %
+                               (nic['pci'], nic['driver']),
+                               True)
+        except subprocess.CalledProcessError:
+            _LOGGER.error('Unable to bind NIC %s to driver %s',
+                          nic['pci'], nic['driver'])
+
 class Dpdk(object):
     """A context manager for the system init/cleanup.
     """