| # Copyright (c) 2016 Cisco and/or its affiliates. |
| # 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. |
| |
| from __future__ import print_function |
| |
| """VPP PCI Utility libraries""" |
| |
| import re |
| import logging |
| |
| from vpplib.VPPUtil import VPPUtil |
| |
| DPDK_SCRIPT = "/vpp/vpp-config/scripts/dpdk-devbind.py" |
| |
| # PCI Device id regular expresssion |
| PCI_DEV_ID_REGEX = "[0-9A-Fa-f]+:[0-9A-Fa-f]+:[0-9A-Fa-f]+.[0-9A-Fa-f]+" |
| |
| |
| class VppPCIUtil(object): |
| """ |
| PCI Utilities |
| |
| """ |
| |
| @staticmethod |
| def _create_device_list(device_string): |
| """ |
| Returns a list of PCI devices |
| |
| :param device_string: The devices string from dpdk_devbind |
| :returns: The device list |
| :rtype: dictionary |
| """ |
| |
| devices = {} |
| |
| ids = re.findall(PCI_DEV_ID_REGEX, device_string) |
| descriptions = re.findall(r"\'([\s\S]*?)\'", device_string) |
| unused = re.findall(r"unused=\w+|unused=", device_string) |
| |
| for i, j in enumerate(ids): |
| device = {"description": descriptions[i]} |
| if unused: |
| device["unused"] = unused[i].split("=")[1].split(",") |
| |
| cmd = "ls /sys/bus/pci/devices/{}/driver/module/drivers".format(ids[i]) |
| (ret, stdout, stderr) = VPPUtil.exec_command(cmd) |
| if ret == 0: |
| device["driver"] = stdout.split(":")[1].rstrip("\n") |
| |
| cmd = "cat /sys/bus/pci/devices/{}/numa_node".format(ids[i]) |
| (ret, stdout, stderr) = VPPUtil.exec_command(cmd) |
| if ret != 0: |
| raise RuntimeError("{} failed {} {}".format(cmd, stderr, stdout)) |
| numa_node = stdout.rstrip("\n") |
| if numa_node == "-1": |
| device["numa_node"] = "0" |
| else: |
| device["numa_node"] = numa_node |
| |
| interfaces = [] |
| device["interfaces"] = [] |
| cmd = "ls /sys/bus/pci/devices/{}/net".format(ids[i]) |
| (ret, stdout, stderr) = VPPUtil.exec_command(cmd) |
| if ret == 0: |
| interfaces = stdout.rstrip("\n").split() |
| device["interfaces"] = interfaces |
| |
| l2_addrs = [] |
| for intf in interfaces: |
| cmd = "cat /sys/bus/pci/devices/{}/net/{}/address".format(ids[i], intf) |
| (ret, stdout, stderr) = VPPUtil.exec_command(cmd) |
| if ret != 0: |
| raise RuntimeError("{} failed {} {}".format(cmd, stderr, stdout)) |
| |
| l2_addrs.append(stdout.rstrip("\n")) |
| |
| device["l2addr"] = l2_addrs |
| |
| devices[ids[i]] = device |
| |
| return devices |
| |
| def __init__(self, node): |
| self._node = node |
| self._dpdk_devices = {} |
| self._kernel_devices = {} |
| self._other_devices = {} |
| self._crypto_dpdk_devices = {} |
| self._crypto_kernel_devices = {} |
| self._crypto_other_devices = {} |
| self._link_up_devices = {} |
| |
| def get_all_devices(self): |
| """ |
| Returns a list of all the devices |
| |
| """ |
| |
| node = self._node |
| rootdir = node["rootdir"] |
| dpdk_script = rootdir + DPDK_SCRIPT |
| cmd = dpdk_script + " --status" |
| (ret, stdout, stderr) = VPPUtil.exec_command(cmd) |
| if ret != 0: |
| raise RuntimeError( |
| "{} failed on node {} {}".format(cmd, node["host"], stderr) |
| ) |
| |
| # Get the network devices using the DPDK |
| # First get everything after using DPDK |
| stda = stdout.split("Network devices using DPDK-compatible driver")[1] |
| # Then get everything before using kernel driver |
| using_dpdk = stda.split("Network devices using kernel driver")[0] |
| self._dpdk_devices = self._create_device_list(using_dpdk) |
| |
| # Get the network devices using the kernel |
| stda = stdout.split("Network devices using kernel driver")[1] |
| using_kernel = stda.split("Other network devices")[0] |
| self._kernel_devices = self._create_device_list(using_kernel) |
| |
| # Get the other network devices |
| stda = stdout.split("Other network devices")[1] |
| other = stda.split("Crypto devices using DPDK-compatible driver")[0] |
| self._other_devices = self._create_device_list(other) |
| |
| # Get the crypto devices using the DPDK |
| stda = stdout.split("Crypto devices using DPDK-compatible driver")[1] |
| crypto_using_dpdk = stda.split("Crypto devices using kernel driver")[0] |
| self._crypto_dpdk_devices = self._create_device_list(crypto_using_dpdk) |
| |
| # Get the network devices using the kernel |
| stda = stdout.split("Crypto devices using kernel driver")[1] |
| crypto_using_kernel = stda.split("Other crypto devices")[0] |
| self._crypto_kernel_devices = self._create_device_list(crypto_using_kernel) |
| |
| # Get the other network devices |
| crypto_other = stdout.split("Other crypto devices")[1] |
| self._crypto_other_devices = self._create_device_list(crypto_other) |
| |
| # Get the devices used by the kernel |
| for devk in self._kernel_devices.items(): |
| dvid = devk[0] |
| device = devk[1] |
| for i in device["interfaces"]: |
| cmd = "ip addr show " + i |
| (ret, stdout, stderr) = VPPUtil.exec_command(cmd) |
| if ret != 0: |
| raise RuntimeError( |
| "{} failed on node {} {}".format(cmd, node["host"], stderr) |
| ) |
| lstate = re.findall(r"state \w+", stdout)[0].split(" ")[1] |
| |
| # Take care of the links that are UP |
| if lstate == "UP": |
| device["linkup"] = True |
| self._link_up_devices[dvid] = device |
| |
| for devl in self._link_up_devices.items(): |
| dvid = devl[0] |
| del self._kernel_devices[dvid] |
| |
| def get_dpdk_devices(self): |
| """ |
| Returns a list the dpdk devices |
| |
| """ |
| return self._dpdk_devices |
| |
| def get_kernel_devices(self): |
| """ |
| Returns a list the kernel devices |
| |
| """ |
| return self._kernel_devices |
| |
| def get_other_devices(self): |
| """ |
| Returns a list the other devices |
| |
| """ |
| return self._other_devices |
| |
| def get_crypto_dpdk_devices(self): |
| """ |
| Returns a list the crypto dpdk devices |
| |
| """ |
| return self._crypto_dpdk_devices |
| |
| def get_crypto_kernel_devices(self): |
| """ |
| Returns a list the crypto kernel devices |
| |
| """ |
| return self._crypto_kernel_devices |
| |
| def get_crypto_other_devices(self): |
| """ |
| Returns a list the crypto other devices |
| |
| """ |
| return self._crypto_other_devices |
| |
| def get_link_up_devices(self): |
| """ |
| Returns a list the link up devices |
| |
| """ |
| return self._link_up_devices |
| |
| @staticmethod |
| def vpp_create_interface(interfaces, device_id, device): |
| """ |
| Create an interface using the device is and device |
| |
| """ |
| |
| name = "port" + str(len(interfaces)) |
| interfaces[name] = {} |
| interfaces[name]["pci_address"] = device_id |
| interfaces[name]["numa_node"] = device["numa_node"] |
| if "l2addr" in device: |
| l2_addrs = device["l2addr"] |
| for i, j in enumerate(l2_addrs): |
| if i > 0: |
| mname = "mac_address" + str(i + 1) |
| interfaces[name][mname] = l2_addrs[i] |
| else: |
| interfaces[name]["mac_address"] = l2_addrs[i] |
| |
| @staticmethod |
| def show_vpp_devices(devices, show_interfaces=True, show_header=True): |
| """ |
| show the vpp devices specified in the argument |
| |
| :param devices: A list of devices |
| :param show_interfaces: show the kernel information |
| :param show_header: Display the header if true |
| :type devices: dict |
| :type show_interfaces: bool |
| :type show_header: bool |
| """ |
| |
| if show_interfaces: |
| header = "{:15} {:25} {:50}".format( |
| "PCI ID", "Kernel Interface(s)", "Description" |
| ) |
| else: |
| header = "{:15} {:50}".format("PCI ID", "Description") |
| dashseparator = "-" * (len(header) - 2) |
| |
| if show_header is True: |
| print(header) |
| print(dashseparator) |
| for dit in devices.items(): |
| dvid = dit[0] |
| device = dit[1] |
| if show_interfaces: |
| interfaces = device["interfaces"] |
| interface = "" |
| for i, j in enumerate(interfaces): |
| if i > 0: |
| interface += "," + interfaces[i] |
| else: |
| interface = interfaces[i] |
| |
| print( |
| "{:15} {:25} {:50}".format(dvid, interface, device["description"]) |
| ) |
| else: |
| print("{:15} {:50}".format(dvid, device["description"])) |
| |
| @staticmethod |
| def unbind_vpp_device(node, device_id): |
| """ |
| unbind the device specified |
| |
| :param node: Node dictionary with cpuinfo. |
| :param device_id: The device id |
| :type node: dict |
| :type device_id: string |
| """ |
| |
| rootdir = node["rootdir"] |
| dpdk_script = rootdir + DPDK_SCRIPT |
| cmd = dpdk_script + " -u " + " " + device_id |
| (ret, stdout, stderr) = VPPUtil.exec_command(cmd) |
| if ret != 0: |
| raise RuntimeError( |
| "{} failed on node {} {} {}".format(cmd, node["host"], stdout, stderr) |
| ) |
| |
| @staticmethod |
| def bind_vpp_device(node, driver, device_id): |
| """ |
| bind the device specified |
| |
| :param node: Node dictionary with cpuinfo. |
| :param driver: The driver |
| :param device_id: The device id |
| :type node: dict |
| :type driver: string |
| :type device_id: string |
| :returns ret: Command return code |
| """ |
| |
| rootdir = node["rootdir"] |
| dpdk_script = rootdir + DPDK_SCRIPT |
| cmd = dpdk_script + " -b " + driver + " " + device_id |
| (ret, stdout, stderr) = VPPUtil.exec_command(cmd) |
| if ret != 0: |
| logging.error( |
| "{} failed on node {}".format(cmd, node["host"], stdout, stderr) |
| ) |
| logging.error("{} {}".format(stdout, stderr)) |
| |
| return ret |