aboutsummaryrefslogtreecommitdiffstats
path: root/src/dpdk
diff options
context:
space:
mode:
Diffstat (limited to 'src/dpdk')
-rw-r--r--src/dpdk/__init__.py21
-rw-r--r--src/dpdk/dpdk.py377
2 files changed, 398 insertions, 0 deletions
diff --git a/src/dpdk/__init__.py b/src/dpdk/__init__.py
new file mode 100644
index 00000000..4be1e215
--- /dev/null
+++ b/src/dpdk/__init__.py
@@ -0,0 +1,21 @@
+# Copyright 2015 Intel Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""A collection of functions for automating the DPDK setup and teardown.
+
+These automation tasks include mounting/unmounting hugepages, inserting
+and removing drivers and binding/unbinding NICs.
+"""
+
+from src.dpdk.dpdk import *
diff --git a/src/dpdk/dpdk.py b/src/dpdk/dpdk.py
new file mode 100644
index 00000000..9b3d1385
--- /dev/null
+++ b/src/dpdk/dpdk.py
@@ -0,0 +1,377 @@
+# Copyright 2015 Intel Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Automation of system configuration for DPDK use.
+
+Parts of this based on ``tools/pci_unbind.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
+
+_LOGGER = logging.getLogger(__name__)
+RTE_PCI_TOOL = os.path.join(
+ settings.getValue('RTE_SDK'), 'tools', 'dpdk_nic_bind.py')
+
+#
+# system management
+#
+
+
+def init():
+ """Setup system for DPDK.
+ """
+ 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():
+ """Setup system for DPDK.
+ """
+ if not _is_linux():
+ _LOGGER.error('Not running on a compatible Linux version. Exiting...')
+ return
+
+ _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.
+
+ Many of the functions in this file rely on features commonly found
+ only on Linux (i.e. ``/proc`` is not present on FreeBSD). Hence, this
+ check is important to ensure someone doesn't run this on an incompatible
+ OS or distro.
+ """
+ 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.
+ """
+ for module in settings.getValue('SYS_MODULES'):
+ if _is_module_inserted(module):
+ continue
+
+ try:
+ tasks.run_task(['sudo', 'modprobe', module], _LOGGER,
+ 'Inserting module \'%s\'...' % module, True)
+ except subprocess.CalledProcessError:
+ _LOGGER.error('Unable to insert module \'%s\'.', module)
+ raise # fail catastrophically
+
+ 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
+
+
+def _remove_modules():
+ """Ensure required modules are removed from system.
+ """
+ _remove_module_group('OVS_MODULES')
+ _remove_module_group('DPDK_MODULES')
+
+ for module in settings.getValue('SYS_MODULES'):
+ # first check if module is loaded
+ if not _is_module_inserted(module):
+ continue
+
+ try:
+ tasks.run_task(['sudo', 'rmmod', module], _LOGGER,
+ 'Removing module \'%s\'...' % module, True)
+ except subprocess.CalledProcessError:
+ _LOGGER.error('Unable to remove module \'%s\'.', module)
+ continue
+
+
+def _remove_module_group(module_group):
+ """Ensure all modules in a group are removed from 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 not _is_module_inserted(module[1]):
+ continue
+
+ 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
+
+
+#
+# 'vhost-net' module management
+#
+
+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\'.')
+
+ 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
+#
+
+def _vhost_user_cleanup():
+ """Remove files created by vhost-user tests.
+ """
+ for sock in settings.getValue('VHOST_USER_SOCKS'):
+ if os.path.exists(sock):
+ try:
+ tasks.run_task(['sudo', 'rm', sock],
+ _LOGGER,
+ 'Deleting vhost-user socket \'%s\'...' %
+ sock,
+ True)
+
+ except subprocess.CalledProcessError:
+ _LOGGER.error('Unable to delete vhost-user socket \'%s\'.',
+ sock)
+ continue
+
+
+class Dpdk(object):
+ """A context manager for the system init/cleanup.
+ """
+ def __enter__(self):
+ _LOGGER.info('Setting up DPDK')
+ init()
+ return self
+
+ def __exit__(self, type_, value, traceback):
+ _LOGGER.info('Cleaning up DPDK')
+ cleanup()