summaryrefslogtreecommitdiffstats
path: root/tools/networkcard.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/networkcard.py')
-rw-r--r--tools/networkcard.py266
1 files changed, 266 insertions, 0 deletions
diff --git a/tools/networkcard.py b/tools/networkcard.py
new file mode 100644
index 00000000..c31be691
--- /dev/null
+++ b/tools/networkcard.py
@@ -0,0 +1,266 @@
+# Copyright 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.
+# 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.
+
+"""Tools for network card manipulation
+"""
+
+import os
+import subprocess
+import logging
+import glob
+from conf import settings
+
+_LOGGER = logging.getLogger('tools.networkcard')
+
+_PCI_DIR = '/sys/bus/pci/devices/{}/'
+_SRIOV_NUMVFS = os.path.join(_PCI_DIR, 'sriov_numvfs')
+_SRIOV_TOTALVFS = os.path.join(_PCI_DIR, 'sriov_totalvfs')
+_SRIOV_VF_PREFIX = 'virtfn'
+_SRIOV_PF = 'physfn'
+_PCI_NET = 'net'
+_PCI_DRIVER = 'driver'
+
+
+def check_pci(pci_handle):
+ """ Checks if given extended PCI handle has correct length and fixes
+ it if possible.
+
+ :param pci_handle: PCI slot identifier. It can contain vsperf specific
+ suffix after '|' with VF indication. e.g. '0000:05:00.0|vf1'
+
+ :returns: PCI handle
+ """
+ pci = pci_handle.split('|')
+ pci_len = len(pci[0])
+ if pci_len == 12:
+ return pci_handle
+ elif pci_len == 7:
+ pci[0] = '0000:' + pci[0][-7:]
+ _LOGGER.debug('Adding domain part to PCI slot %s', pci[0])
+ return '|'.join(pci)
+ elif pci_len > 12:
+ pci[0] = pci[0][-12:]
+ _LOGGER.warning('PCI slot is too long, it will be shortened to %s', pci[0])
+ return '|'.join(pci)
+ else:
+ # pci_handle has a strange length, but let us try to use it
+ _LOGGER.error('Unknown format of PCI slot %s', pci_handle)
+ return pci_handle
+
+def is_sriov_supported(pci_handle):
+ """ Checks if sriov is supported by given NIC
+
+ :param pci_handle: PCI slot identifier with domain part.
+
+ :returns: True on success, False otherwise
+ """
+ return os.path.isfile(_SRIOV_TOTALVFS.format(pci_handle))
+
+def is_sriov_nic(pci_handle):
+ """ Checks if given extended PCI ID refers to the VF
+
+ :param pci_handle: PCI slot identifier with domain part. It can contain
+ vsperf specific suffix after '|' with VF indication.
+ e.g. '0000:05:00.0|vf1'
+
+ :returns: True on success, False otherwise
+ """
+ for item in pci_handle.split('|'):
+ if item.lower().startswith('vf'):
+ return True
+ return False
+
+def set_sriov_numvfs(pci_handle, numvfs):
+ """ Checks if sriov is supported and configures given number of VFs
+
+ :param pci_handle: PCI slot identifier with domain part.
+ :param numvfs: Number of VFs to be configured at given NIC.
+
+ :returns: True on success, False otherwise
+ """
+ if not is_sriov_supported(pci_handle):
+ return False
+
+ if get_sriov_numvfs(pci_handle) == numvfs:
+ return True
+
+ if numvfs and get_sriov_numvfs(pci_handle) != 0:
+ if not set_sriov_numvfs(pci_handle, 0):
+ return False
+
+ try:
+ subprocess.call('sudo bash -c "echo {} > {}"'.format(numvfs, _SRIOV_NUMVFS.format(pci_handle)), shell=True)
+ return get_sriov_numvfs(pci_handle) == numvfs
+ except OSError:
+ _LOGGER.debug('Number of VFs cant be changed to %s for PF %s', numvfs, pci_handle)
+ return False
+
+def get_sriov_numvfs(pci_handle):
+ """ Returns the number of configured VFs
+
+ :param pci_handle: PCI slot identifier with domain part
+ :returns: the number of configured VFs
+ """
+ if is_sriov_supported(pci_handle):
+ with open(_SRIOV_NUMVFS.format(pci_handle), 'r') as numvfs:
+ return int(numvfs.readline().rstrip('\n'))
+
+ return None
+
+def get_sriov_totalvfs(pci_handle):
+ """ Checks if sriov is supported and returns max number of supported VFs
+
+ :param pci_handle: PCI slot identifier with domain part
+ :returns: the max number of supported VFs by given NIC
+ """
+ if is_sriov_supported(pci_handle):
+ with open(_SRIOV_TOTALVFS.format(pci_handle), 'r') as total:
+ return int(total.readline().rstrip('\n'))
+
+ return None
+
+def get_sriov_vfs_list(pf_pci_handle):
+ """ Returns list of PCI handles of VFs configured at given NIC/PF
+
+ :param pf_pci_handle: PCI slot identifier of PF with domain part.
+ :returns: list
+ """
+ vfs = []
+ if is_sriov_supported(pf_pci_handle):
+ for vf_name in glob.glob(os.path.join(_PCI_DIR, _SRIOV_VF_PREFIX + '*').format(pf_pci_handle)):
+ vfs.append(os.path.basename(os.path.realpath(vf_name)))
+
+ return vfs
+
+def get_sriov_pf(vf_pci_handle):
+ """ Get PCI handle of PF which belongs to given VF
+
+ :param vf_pci_handle: PCI slot identifier of VF with domain part.
+ :returns: PCI handle of parent PF
+ """
+ pf_path = os.path.join(_PCI_DIR, _SRIOV_PF).format(vf_pci_handle)
+ if os.path.isdir(pf_path):
+ return os.path.basename(os.path.realpath(pf_path))
+
+ return None
+
+def get_driver(pci_handle):
+ """ Returns name of kernel driver assigned to given NIC
+
+ :param pci_handle: PCI slot identifier with domain part.
+ :returns: string with assigned kernel driver, None otherwise
+ """
+ driver_path = os.path.join(_PCI_DIR, _PCI_DRIVER).format(pci_handle)
+ if os.path.isdir(driver_path):
+ return os.path.basename(os.path.realpath(driver_path))
+
+ return None
+
+def get_device_name(pci_handle):
+ """ Returns name of network card device name
+
+ :param pci_handle: PCI slot identifier with domain part.
+ :returns: string with assigned NIC device name, None otherwise
+ """
+ net_path = os.path.join(_PCI_DIR, _PCI_NET).format(pci_handle)
+ try:
+ return os.listdir(net_path)[0]
+ except FileNotFoundError:
+ return None
+ except IndexError:
+ return None
+
+ return None
+
+def get_mac(pci_handle):
+ """ Returns MAC address of given NIC
+
+ :param pci_handle: PCI slot identifier with domain part.
+ :returns: string with assigned MAC address, None otherwise
+ """
+ mac_path = glob.glob(os.path.join(_PCI_DIR, _PCI_NET, '*', 'address').format(pci_handle))
+ # kernel driver is loaded and MAC can be read
+ if len(mac_path) and os.path.isfile(mac_path[0]):
+ with open(mac_path[0], 'r') as _file:
+ return _file.readline().rstrip('\n')
+
+ # MAC address is unknown, e.g. NIC is assigned to DPDK
+ return None
+
+def get_nic_info(full_pci_handle):
+ """ Parse given pci handle with additional info and returns
+ requested NIC info.
+
+ :param full_pci_handle: A string with extended network card PCI ID.
+ extended PCI ID syntax: PCI_ID[|vfx][|(mac|dev)]
+ examples:
+ 0000:06:00.0 - returns the same value
+ 0000:06:00.0|vf0 - returns PCI ID of 1st virtual function of given NIC
+ 0000:06:00.0|mac - returns MAC address of given NIC
+ 0000:06:00.0|vf0|mac - returns MAC address of 1st virtual function of given NIC
+
+ :returns: A string with requested NIC data or None if data cannot be read.
+ """
+ parsed_handle = full_pci_handle.split('|')
+ if len(parsed_handle) not in (1, 2, 3):
+ _LOGGER.error("Invalid PCI device name: '%s'", full_pci_handle)
+ return None
+
+ pci_handle = parsed_handle[0]
+
+ for action in parsed_handle[1:]:
+ # in case of SRIOV get PCI handle of given virtual function
+ if action.lower().startswith('vf'):
+ try:
+ vf_num = int(action[2:])
+ pci_handle = get_sriov_vfs_list(pci_handle)[vf_num]
+ except ValueError:
+ _LOGGER.error("Pci device '%s', does not have VF with index '%s'", pci_handle, action[2:])
+ return None
+ except IndexError:
+ _LOGGER.error("Pci device '%s', does not have VF with index '%s'", pci_handle, vf_num)
+ return None
+ continue
+
+ # return requested info for given PCI handle
+ if action.lower() == 'mac':
+ return get_mac(pci_handle)
+ elif action.lower() == 'dev':
+ return get_device_name(pci_handle)
+ else:
+ _LOGGER.error("Invalid item '%s' in PCI handle '%s'", action, full_pci_handle)
+ return None
+
+ return pci_handle
+
+def reinit_vfs(pf_pci_handle):
+ """ Reinitializates all VFs, which belong to given PF
+
+ :param pf_pci_handle: PCI slot identifier of PF with domain part.
+ """
+ rte_pci_tool = os.path.join(settings.getValue('RTE_SDK'), 'tools', 'dpdk_nic_bind.py')
+
+ for vf_nic in get_sriov_vfs_list(pf_pci_handle):
+ nic_driver = get_driver(vf_nic)
+ if nic_driver:
+ try:
+ subprocess.call(['sudo', rte_pci_tool, '--unbind', vf_nic],
+ stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+ subprocess.call(['sudo', rte_pci_tool, '--bind=' + nic_driver, vf_nic],
+ stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+ except subprocess.CalledProcessError:
+ _LOGGER.warning('Error during reinitialization of VF %s', vf_nic)
+ else:
+ _LOGGER.warning("Can't detect driver for VF %s", vf_nic)
+