Add Inspur FPGA driver

Please see the test report in https://wiki.openstack.org/wiki/Cyborg/TestReport/InspurFPGA
This patch implemented Inspur FPGA driver in Cyborg. Currently we don't
support program with this card by Cyborg, the operator can bind the card
to guest and use it in guest, so BSP data reported is ignored now.

The spec is already merged. Please see:
https://specs.openstack.org/openstack/cyborg-specs/specs/victoria/approved/inspur-fpga-driver-proposal.html

Story: 2007772
Task: 39997

Change-Id: I7cc3482a4a6ac6fb0ca6965b7ce18bc37b122f63
This commit is contained in:
songwenping 2020-05-21 15:04:57 +08:00
parent 40259a25ec
commit 3108d28eb3
10 changed files with 314 additions and 3 deletions

View File

@ -20,7 +20,8 @@ Cyborg FPGA driver implementation.
from cyborg.accelerator.drivers.fpga import utils
VENDOR_MAPS = {"0x8086": "intel"}
VENDOR_MAPS = {"0x8086": "intel",
"1bd4": 'inspur'}
class FPGADriver(object):

View File

@ -0,0 +1,40 @@
# Copyright 2020 Inspur, 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 Inspur FPGA driver implementation.
"""
from oslo_log import log as logging
from cyborg.accelerator.drivers.fpga.base import FPGADriver
from cyborg.accelerator.drivers.fpga.inspur import sysinfo
LOG = logging.getLogger(__name__)
class InspurFPGADriver(FPGADriver):
"""Base class for FPGA drivers.
This is just a virtual FPGA drivers interface.
Vendor should implement their specific drivers.
"""
VENDOR = "inspur"
def __init__(self, *args, **kwargs):
pass
def discover(self):
return sysinfo.fpga_tree()

View File

@ -0,0 +1,154 @@
# Copyright 2020 Inspur, 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 Inspur FPGA driver implementation.
"""
import re
from oslo_concurrency import processutils
from oslo_serialization import jsonutils
from cyborg.accelerator.common import utils
from cyborg.common import constants
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
import cyborg.privsep
INSPUR_FPGA_FLAGS = ["Inspur Electronic Information Industry Co., Ltd.",
"Processing accelerators"]
INSPUR_FPGA_INFO_PATTERN = re.compile(
r"(?P<devices>[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:"
r"[0-9a-fA-F]{2}\.[0-9a-fA-F]) "
r"(?P<controller>.*) [\[].*]: (?P<name>.*) .*"
r"[\[](?P<vendor_id>[0-9a-fA-F]"
r"{4}):(?P<product_id>[0-9a-fA-F]{4})].*")
VENDOR_MAPS = {"1bd4": "inspur"}
@cyborg.privsep.sys_admin_pctxt.entrypoint
def lspci_privileged():
cmd = ['lspci', '-nnn', '-D']
return processutils.execute(*cmd)
def get_pci_devices(pci_flags, vendor_id=None):
device_for_vendor_out = []
all_device_out = []
lspci_out = lspci_privileged()[0].split('\n')
for i in range(len(lspci_out)):
if any(x in lspci_out[i] for x in pci_flags):
all_device_out.append(lspci_out[i])
if vendor_id and vendor_id in lspci_out[i]:
device_for_vendor_out.append(lspci_out[i])
return device_for_vendor_out if vendor_id else all_device_out
def get_traits(vendor_id, product_id):
"""Generate traits for FPGAs.
: param vendor_id: vendor_id of FPGA, eg."1bd4"
: param product_id: product_id of FPGA, eg."a115".
Example FPGA traits:
{traits:["CUSTOM_FPGA_INSPUR", "CUSTOM_FPGA_PRODUCT_ID_A115"]}
"""
traits = []
traits.append("CUSTOM_FPGA_" + VENDOR_MAPS.get(vendor_id, "").upper())
traits.append("CUSTOM_FPGA_PRODUCT_ID_" + product_id.upper())
# TODO(wenping) Currently we don't support program by Cyborg. The operator
# can bind Inspur FPGA card to guest and then use it in guest. So the BSP
# data reported is ignored here now.
return {"traits": traits}
def fpga_tree():
fpga_list = []
fpgas = get_pci_devices(INSPUR_FPGA_FLAGS)
for fpga in fpgas:
m = INSPUR_FPGA_INFO_PATTERN.match(fpga)
if m:
fpga_dict = m.groupdict()
# generate traits info
traits = get_traits(
fpga_dict["vendor_id"], fpga_dict["product_id"])
fpga_dict["rc"] = constants.RESOURCES["FPGA"]
fpga_dict.update(traits)
fpga_list.append(_generate_driver_device(fpga_dict))
return fpga_list
def _generate_driver_device(fpga):
driver_device_obj = driver_device.DriverDevice()
driver_device_obj.vendor = fpga["vendor_id"]
driver_device_obj.model = fpga.get('model', 'miss model info')
std_board_info = {'product_id': fpga.get('product_id', None),
'controller': fpga.get('controller', None)}
vendor_board_info = {
'vendor_info': fpga.get('vendor_info', 'fpga_vb_info')}
driver_device_obj.std_board_info = jsonutils.dumps(std_board_info)
driver_device_obj.vendor_board_info = jsonutils.dumps(vendor_board_info)
driver_device_obj.type = constants.DEVICE_FPGA
driver_device_obj.stub = fpga.get('stub', False)
driver_device_obj.controlpath_id = _generate_controlpath_id(fpga)
driver_device_obj.deployable_list = _generate_dep_list(fpga)
return driver_device_obj
def _generate_controlpath_id(fpga):
driver_cpid = driver_controlpath_id.DriverControlPathID()
driver_cpid.cpid_type = "PCI"
driver_cpid.cpid_info = utils.pci_str_to_json(fpga["devices"])
return driver_cpid
def _generate_dep_list(fpga):
dep_list = []
driver_dep = driver_deployable.DriverDeployable()
driver_dep.attribute_list = _generate_attribute_list(fpga)
driver_dep.attach_handle_list = []
driver_dep.name = fpga.get('name', '') + '_' + fpga["devices"]
driver_dep.driver_name = VENDOR_MAPS.get(fpga["vendor_id"]).upper()
driver_dep.num_accelerators = 1
driver_dep.attach_handle_list = [_generate_attach_handle(fpga)]
dep_list.append(driver_dep)
return dep_list
def _generate_attribute_list(fpga):
attr_list = []
for k, v in fpga.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 = fpga.get(k, [])
for index, val in enumerate(values):
driver_attr = driver_attribute.DriverAttribute(
key="trait" + str(index), value=val)
attr_list.append(driver_attr)
return attr_list
def _generate_attach_handle(fpga):
driver_ah = driver_attach_handle.DriverAttachHandle()
driver_ah.attach_type = constants.AH_TYPE_PCI
driver_ah.attach_info = utils.pci_str_to_json(fpga["devices"])
driver_ah.in_use = False
return driver_ah

View File

@ -21,7 +21,7 @@ import glob
import re
VENDORS = ["intel"] # can extend, such as ["intel", "xilinx"]
VENDORS = ["intel", "inspur"] # can extend, such as ["intel", "xilinx"]
SYS_FPGA_PATH = "/sys/class/fpga"
VENDORS_PATTERN = re.compile("|".join(["(%s)" % v for v in VENDORS]))

View File

@ -21,7 +21,8 @@ opts = [
cfg.ListOpt('enabled_drivers',
default=[],
help=_('The accelerator drivers enabled on this agent. Such '
'as intel_fpga_driver, nvidia_gpu_driver, etc.')),
'as intel_fpga_driver, inspur_fpga_driver,'
'nvidia_gpu_driver, etc.')),
]
opt_group = cfg.OptGroup(name='agent',

View File

@ -0,0 +1,112 @@
# Copyright 2020 Inspur, 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.
from oslo_serialization import jsonutils
from unittest import mock
from cyborg.accelerator.drivers.fpga.inspur.driver import InspurFPGADriver
from cyborg.tests import base
INSPUR_FPGA_INFO = ("0000:86:00.0 Processing accelerators [1200]: "
"Inspur Electronic Information Industry Co., Ltd. "
"Device [1bd4:a115] (rev 04)")
class stdout(object):
def readlines(self):
return [INSPUR_FPGA_INFO]
class p(object):
def __init__(self):
self.stdout = stdout()
def wait(self):
pass
class TestInspurFPGADriver(base.TestCase):
def setUp(self):
super(TestInspurFPGADriver, self).setUp()
self.p = p()
@mock.patch('cyborg.accelerator.drivers.fpga.'
'inspur.sysinfo.lspci_privileged')
def test_discover(self, mock_devices_for_vendor):
mock_devices_for_vendor.return_value = self.p.stdout.readlines()
fpga_list = InspurFPGADriver().discover()
self.assertEqual(1, len(fpga_list))
attach_handle_list = [
{'attach_type': 'PCI',
'attach_info': '{"bus": "86", '
'"device": "00", '
'"domain": "0000", '
'"function": "0"}',
'in_use': False}
]
attribute_list = [
{'key': 'rc', 'value': 'FPGA'},
{'key': 'trait0', 'value': 'CUSTOM_FPGA_INSPUR'},
{'key': 'trait1', 'value': 'CUSTOM_FPGA_PRODUCT_ID_A115'},
]
expected = {
'vendor': '1bd4',
'type': 'FPGA',
'std_board_info': {"controller": "Processing accelerators",
"product_id": "a115"},
'vendor_board_info': {"vendor_info": "fpga_vb_info"},
'deployable_list':
[
{
'num_accelerators': 1,
'driver_name': 'INSPUR',
'name':
'Inspur Electronic Information Industry Co., Ltd.'
' Device_0000:86:00.0',
'attach_handle_list': attach_handle_list,
'attribute_list': attribute_list
},
],
'controlpath_id': {'cpid_info': '{"bus": "86", '
'"device": "00", '
'"domain": "0000", '
'"function": "0"}',
'cpid_type': 'PCI'}
}
fpga_obj = fpga_list[0]
fpga_dict = fpga_obj.as_dict()
fpga_dep_list = fpga_dict['deployable_list']
fpga_attach_handle_list = (
fpga_dep_list[0].as_dict()['attach_handle_list'])
fpga_attribute_list = fpga_dep_list[0].as_dict()['attribute_list']
attri_obj_data = []
[attri_obj_data.append(attr.as_dict()) for attr in fpga_attribute_list]
attribute_actual_data = sorted(attri_obj_data, key=lambda i: i['key'])
self.assertEqual(expected['vendor'], fpga_dict['vendor'])
self.assertEqual(expected['controlpath_id'],
fpga_dict['controlpath_id'])
self.assertEqual(expected['std_board_info'],
jsonutils.loads(fpga_dict['std_board_info']))
self.assertEqual(expected['vendor_board_info'],
jsonutils.loads(fpga_dict['vendor_board_info']))
self.assertEqual(expected['deployable_list'][0]['num_accelerators'],
fpga_dep_list[0].as_dict()['num_accelerators'])
self.assertEqual(expected['deployable_list'][0]['name'],
fpga_dep_list[0].as_dict()['name'])
self.assertEqual(expected['deployable_list'][0]['driver_name'],
fpga_dep_list[0].as_dict()['driver_name'])
self.assertEqual(attach_handle_list[0],
fpga_attach_handle_list[0].as_dict())
self.assertEqual(attribute_list, attribute_actual_data)

View File

@ -14,12 +14,14 @@
from cyborg.accelerator.drivers.fpga.base import FPGADriver
from cyborg.accelerator.drivers.fpga.intel.driver import IntelFPGADriver # noqa
from cyborg.accelerator.drivers.fpga.inspur.driver import InspurFPGADriver # noqa
from cyborg.tests import base
class TestFPGADriver(base.TestCase):
def test_create(self):
FPGADriver.create("intel")
FPGADriver.create("inspur")
self.assertRaises(LookupError, FPGADriver.create, "xilinx")
def test_discover(self):

View File

@ -47,6 +47,7 @@ cyborg.database.migration_backend =
cyborg.accelerator.driver =
intel_fpga_driver = cyborg.accelerator.drivers.fpga.intel.driver:IntelFPGADriver
inspur_fpga_driver = cyborg.accelerator.drivers.fpga.inspur.driver:InspurFPGADriver
nvmf_spdk_driver = cyborg.accelerator.drivers.spdk.nvmf.nvmf:NVMFDRIVER
nvidia_gpu_driver = cyborg.accelerator.drivers.gpu.nvidia.driver:NVIDIAGPUDriver
fake_driver = cyborg.accelerator.drivers.fake:FakeDriver