254 lines
8.0 KiB
Python
254 lines
8.0 KiB
Python
# Copyright 2020 Intel, Inc.
|
|
#
|
|
# 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.
|
|
|
|
|
|
"""
|
|
Cyborg Intel NIC driver implementation.
|
|
"""
|
|
|
|
|
|
import glob
|
|
import os
|
|
import socket
|
|
|
|
from oslo_config import cfg
|
|
from oslo_log import log as logging
|
|
from oslo_serialization import jsonutils
|
|
|
|
from cyborg.accelerator.common import utils
|
|
import cyborg.conf
|
|
|
|
from cyborg.objects.driver_objects import driver_attach_handle
|
|
from cyborg.objects.driver_objects import driver_attribute
|
|
from cyborg.objects.driver_objects import driver_controlpath_id
|
|
from cyborg.objects.driver_objects import driver_deployable
|
|
from cyborg.objects.driver_objects import driver_device
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
PCI_DEVICES_PATH_PATTERN = "/sys/bus/pci/devices/*"
|
|
KNOWN_NICS = [("0x8086", "0x158b"), ("0x8086", "0x1572")]
|
|
DRIVER_NAME = "intel"
|
|
|
|
VF = "virtfn*"
|
|
_SRIOV_TOTALVFS = "sriov_totalvfs"
|
|
CONF = cyborg.conf.CONF
|
|
|
|
|
|
def _parse_config():
|
|
# parse nic config.
|
|
cyborg.conf.devices.register_dynamic_opts(CONF)
|
|
try:
|
|
pdm = utils.parse_mappings(CONF.x710_static.physical_device_mappings)
|
|
fdm = utils.parse_mappings(CONF.x710_static.function_device_mappings)
|
|
except cfg.NoSuchOptError:
|
|
return None, None
|
|
else:
|
|
return pdm, fdm
|
|
|
|
|
|
def get_physical_network_and_traits(pci_info, physnet_device_mappings,
|
|
function_device_mappings, pf_nic=None):
|
|
traits = []
|
|
physnet = None
|
|
func_name = None
|
|
|
|
if pf_nic:
|
|
pf_addr = pf_nic["device"]
|
|
traits.append("CUSTOM_VF")
|
|
else:
|
|
pf_addr = pci_info["PCI_SLOT_NAME"]
|
|
traits.append("CUSTOM_PF")
|
|
if not pf_addr:
|
|
LOG.error("Incorrect report data received. Missing PF info.")
|
|
|
|
pf_ifname = utils.get_ifname_by_pci_address(pf_addr)
|
|
|
|
if physnet_device_mappings:
|
|
for key, devices in physnet_device_mappings.items():
|
|
if pf_ifname in devices:
|
|
physnet = key
|
|
break
|
|
if function_device_mappings:
|
|
for key, devices in function_device_mappings.items():
|
|
if pf_ifname in devices:
|
|
func_name = key
|
|
break
|
|
if func_name:
|
|
traits.append("CUSTOM_" + func_name.upper())
|
|
if physnet:
|
|
traits.append("CUSTOM_" + physnet.upper())
|
|
|
|
return {"traits": traits, "physical_network": physnet}
|
|
|
|
|
|
def read_line(filename):
|
|
with open(filename) as f:
|
|
return f.readline().strip()
|
|
|
|
|
|
def find_nics_by_know_list():
|
|
return set(filter(
|
|
lambda p: (
|
|
read_line(os.path.join(p, "vendor")),
|
|
read_line(os.path.join(p, "device"))
|
|
) in KNOWN_NICS,
|
|
glob.glob(PCI_DEVICES_PATH_PATTERN)))
|
|
|
|
|
|
def pci_attributes(path):
|
|
with open(os.path.join(path, "uevent")) as f:
|
|
attributes = dict(map(
|
|
lambda p: p.strip().split("="),
|
|
f.readlines()
|
|
))
|
|
|
|
with open(os.path.join(path, "vendor")) as f:
|
|
attributes["VENDOR"] = f.readline().strip()
|
|
|
|
with open(os.path.join(path, "device")) as f:
|
|
attributes["PRODUCT_ID"] = f.readline().strip()
|
|
|
|
return attributes
|
|
|
|
|
|
def nic_gen(path, physnet_device_mappings=None, function_device_mappings=None,
|
|
pf_nic=None):
|
|
pci_info = pci_attributes(path)
|
|
nic = {
|
|
"name": "_".join((socket.gethostname(), pci_info["PCI_SLOT_NAME"])),
|
|
"device": pci_info["PCI_SLOT_NAME"],
|
|
"type": "NIC",
|
|
"vendor": pci_info["VENDOR"],
|
|
"product_id": pci_info["PRODUCT_ID"],
|
|
"rc": "CUSTOM_NIC",
|
|
"stub": False,
|
|
}
|
|
# TODO(Xinran): need check device id and call get_traits differently.
|
|
updates = get_physical_network_and_traits(pci_info,
|
|
physnet_device_mappings,
|
|
function_device_mappings,
|
|
pf_nic)
|
|
|
|
nic.update(updates)
|
|
return nic
|
|
|
|
|
|
def all_pfs_with_vf():
|
|
return set(filter(
|
|
lambda p: glob.glob(os.path.join(p, VF)),
|
|
find_nics_by_know_list()))
|
|
|
|
|
|
def all_vfs_in_pf(pf_path):
|
|
return map(
|
|
lambda p:
|
|
os.path.join(
|
|
os.path.dirname(os.path.dirname(p)),
|
|
os.path.basename(os.readlink(p))),
|
|
glob.glob(os.path.join(pf_path, VF)))
|
|
|
|
|
|
def nic_tree():
|
|
physnet_device_mappings, function_device_mappings = _parse_config()
|
|
nics = []
|
|
pfs_has_vf = all_pfs_with_vf()
|
|
for n in find_nics_by_know_list():
|
|
nic = nic_gen(n, physnet_device_mappings, function_device_mappings)
|
|
if n in pfs_has_vf:
|
|
vfs = []
|
|
for vf in all_vfs_in_pf(n):
|
|
vf_nic = nic_gen(vf, physnet_device_mappings,
|
|
function_device_mappings, nic)
|
|
vfs.append(vf_nic)
|
|
nic["vfs"] = vfs
|
|
nics.append(_generate_driver_device(nic))
|
|
return nics
|
|
|
|
|
|
def _generate_driver_device(nic):
|
|
driver_device_obj = driver_device.DriverDevice()
|
|
driver_device_obj.vendor = nic["vendor"]
|
|
driver_device_obj.stub = nic["stub"]
|
|
driver_device_obj.model = nic.get("model", "miss_model_info")
|
|
driver_device_obj.vendor_board_info = nic.get(
|
|
"vendor_board_info",
|
|
"miss_vb_info")
|
|
std_board_info = {"product_id": nic.get("product_id", None)}
|
|
driver_device_obj.std_board_info = jsonutils.dumps(std_board_info)
|
|
driver_device_obj.type = nic["type"]
|
|
driver_device_obj.controlpath_id = _generate_controlpath_id(nic)
|
|
driver_device_obj.deployable_list = _generate_dep_list(nic)
|
|
return driver_device_obj
|
|
|
|
|
|
def _generate_controlpath_id(nic):
|
|
driver_cpid = driver_controlpath_id.DriverControlPathID()
|
|
driver_cpid.cpid_type = "PCI"
|
|
driver_cpid.cpid_info = utils.pci_str_to_json(nic["device"])
|
|
return driver_cpid
|
|
|
|
|
|
def _generate_dep_list(nic):
|
|
dep_list = []
|
|
# pf without sriov enabled.
|
|
if "vfs" not in nic:
|
|
driver_dep = driver_deployable.DriverDeployable()
|
|
driver_dep.num_accelerators = 1
|
|
driver_dep.attach_handle_list = [
|
|
_generate_attach_handle(nic)]
|
|
driver_dep.name = nic["name"]
|
|
driver_dep.driver_name = DRIVER_NAME
|
|
driver_dep.attribute_list = _generate_attribute_list(nic)
|
|
dep_list = [driver_dep]
|
|
# pf with sriov enabled, may have several vfs.
|
|
else:
|
|
for vf in nic["vfs"]:
|
|
driver_dep = driver_deployable.DriverDeployable()
|
|
driver_dep.num_accelerators = 1
|
|
driver_dep.attach_handle_list = [
|
|
_generate_attach_handle(vf)]
|
|
driver_dep.name = vf["name"]
|
|
driver_dep.driver_name = DRIVER_NAME
|
|
driver_dep.attribute_list = _generate_attribute_list(vf)
|
|
dep_list.append(driver_dep)
|
|
return dep_list
|
|
|
|
|
|
def _generate_attach_handle(nic):
|
|
driver_ah = driver_attach_handle.DriverAttachHandle()
|
|
driver_ah.attach_type = "PCI"
|
|
driver_ah.attach_info = utils.pci_str_to_json(nic["device"],
|
|
nic["physical_network"])
|
|
driver_ah.in_use = False
|
|
return driver_ah
|
|
|
|
|
|
def _generate_attribute_list(nic):
|
|
attr_list = []
|
|
for k, v in nic.items():
|
|
if k == "rc":
|
|
driver_attr = driver_attribute.DriverAttribute()
|
|
driver_attr.key, driver_attr.value = k, v
|
|
attr_list.append(driver_attr)
|
|
if k == "traits":
|
|
values = nic.get(k, [])
|
|
for idx, val in enumerate(values):
|
|
driver_attr = driver_attribute.DriverAttribute()
|
|
driver_attr.key = "trait" + str(idx)
|
|
driver_attr.value = val
|
|
attr_list.append(driver_attr)
|
|
|
|
return attr_list
|