aboutsummaryrefslogtreecommitdiffstats
path: root/vnfs/qemu/qemu_pci_passthrough.py
blob: 951d6086c4863f838543373bf0c7d5cf457a2a10 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# 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 QEMU hypervisor with direct access to host NICs via
   PCI passthrough.
"""

import logging
import subprocess
import os
import glob

from conf import settings as S
from vnfs.qemu.qemu import IVnfQemu
from tools import tasks
from tools.module_manager import ModuleManager

_MODULE_MANAGER = ModuleManager()
_RTE_PCI_TOOL = glob.glob(os.path.join(S.getValue('RTE_SDK'), 'tools', 'dpdk*bind.py'))[0]

class QemuPciPassthrough(IVnfQemu):
    """
    Control an instance of QEMU with direct access to the host network devices
    """
    def __init__(self):
        """
        Initialization function.
        """
        super(QemuPciPassthrough, self).__init__()
        self._logger = logging.getLogger(__name__)
        self._nics = S.getValue('NICS')

        # in case of SRIOV and PCI passthrough we must ensure, that MAC addresses are swapped
        if S.getValue('SRIOV_ENABLED') and not self._testpmd_fwd_mode.startswith('mac'):
            self._logger.info("SRIOV detected, forwarding mode of testpmd was changed from '%s' to '%s'",
                              self._testpmd_fwd_mode, 'mac')
            self._testpmd_fwd_mode = 'mac'

        for nic in self._nics:
            self._cmd += ['-device', 'vfio-pci,host=' + nic['pci']]

    def start(self):
        """
        Start QEMU instance, bind host NICs to vfio-pci driver
        """
        # load vfio-pci
        _MODULE_MANAGER.insert_modules(['vfio-pci'])

        # bind every interface to vfio-pci driver
        try:
            nics_list = list(tmp_nic['pci'] for tmp_nic in self._nics)
            tasks.run_task(['sudo', _RTE_PCI_TOOL, '--bind=vfio-pci'] + nics_list,
                           self._logger, 'Binding NICs %s...' % nics_list, True)

        except subprocess.CalledProcessError:
            self._logger.error('Unable to bind NICs %s', self._nics)

        super(QemuPciPassthrough, self).start()

    def stop(self):
        """
        Stop QEMU instance, bind host NICs to the original driver
        """
        super(QemuPciPassthrough, self).stop()

        # bind original driver to every interface
        for nic in self._nics:
            if nic['driver']:
                try:
                    tasks.run_task(['sudo', _RTE_PCI_TOOL, '--bind=' + nic['driver'], nic['pci']],
                                   self._logger, 'Binding NIC %s...' % nic['pci'], True)

                except subprocess.CalledProcessError:
                    self._logger.error('Unable to bind NIC %s to driver %s', nic['pci'], nic['driver'])

        # unload vfio-pci driver
        _MODULE_MANAGER.remove_modules()