FPGA driver support
This patch is not a plugin implementation Cyborg agent can call driver as follow: from cyborg.accelerator.drivers.fpga import base fdriver = base.FPGADriver # There maybe more than one vendor fpga card on a host. venders = fdriver.discover_vendors() for v in venders: dr = fdriver.create(v) dr.discover() intel = base.FPGADriver.create("intel") intel.program("/fpga/device/path", "/local/path/image") When call IntelFPGADriver.discover(), we can get the result as follow: [{'assignable': False, 'devices': '0000:5e:00.0', 'function': 'pf', 'parent_devices': '', 'path': '/sys/class/fpga/intel-fpga-dev.0', 'pr_num': '1', 'product_id': '0xbcc0', 'regions': [{'assignable': True, 'devices': '0000:5e:00.1', 'function': 'vf', 'parent_devices': '0000:5e:00.0', 'path': '/sys/class/fpga/intel-fpga-dev.2', 'product_id': '0xbcc1', 'vendor_id': '0x8086'}], 'vendor_id': '0x8086'}, {'assignable': True, 'devices': '0000:be:00.0', 'function': 'pf', 'parent_devices': '', 'path': '/sys/class/fpga/intel-fpga-dev.1', 'pr_num': '0', 'product_id': '0xbcc0', 'vendor_id': '0x8086'}] Cyborg agent can use it to form it's DB. Co-Authored-By: Dolpher Du <Dolpher.Du@intel.com> Change-Id: I132be5ecdb90b385b68b0cdf306d1402ef4bfc04
This commit is contained in:
parent
b22761ab0d
commit
5b72422930
|
@ -0,0 +1,41 @@
|
|||
# Copyright 2018 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.
|
||||
|
||||
import os
|
||||
import glob
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
|
||||
__import__('pkg_resources').declare_namespace(__name__)
|
||||
__import__(".".join([__package__, 'base']))
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def load_fpga_vendor_driver():
|
||||
files = glob.glob(os.path.join(os.path.dirname(__file__), "*/driver*"))
|
||||
modules = set(map(lambda s: ".".join(s.rsplit(".")[0].rsplit("/", 2)[-2:]),
|
||||
files))
|
||||
for m in modules:
|
||||
try:
|
||||
__import__(".".join([__package__, m]))
|
||||
LOG.debug("Successfully loaded FPGA vendor driver: %s." % m)
|
||||
except ImportError as e:
|
||||
LOG.error("Failed to load FPGA vendor driver: %s. Details: %s"
|
||||
% (m, e))
|
||||
|
||||
|
||||
load_fpga_vendor_driver()
|
|
@ -0,0 +1,52 @@
|
|||
# Copyright 2018 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 FPGA driver implementation.
|
||||
"""
|
||||
|
||||
from cyborg.accelerator.drivers.fpga import utils
|
||||
|
||||
|
||||
VENDOR_MAPS = {"0x8086": "intel"}
|
||||
|
||||
|
||||
class FPGADriver(object):
|
||||
"""Base class for FPGA drivers.
|
||||
|
||||
This is just a virtual FPGA drivers interface.
|
||||
Vedor should implement their specific drivers.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def create(cls, vendor, *args, **kwargs):
|
||||
for sclass in cls.__subclasses__():
|
||||
vendor = VENDOR_MAPS.get(vendor, vendor)
|
||||
if vendor == sclass.VENDOR:
|
||||
return sclass(*args, **kwargs)
|
||||
raise LookupError("Not find the FPGA driver for vendor %s" % vendor)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def discover(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def program(self, device_path, image):
|
||||
raise NotImplementedError()
|
||||
|
||||
@classmethod
|
||||
def discover_vendors(cls):
|
||||
return utils.discover_vendors()
|
|
@ -0,0 +1,56 @@
|
|||
# Copyright 2018 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 FPGA driver implementation.
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
|
||||
from cyborg.accelerator.drivers.fpga.base import FPGADriver
|
||||
from cyborg.accelerator.drivers.fpga.intel import sysinfo
|
||||
|
||||
|
||||
class IntelFPGADriver(FPGADriver):
|
||||
"""Base class for FPGA drivers.
|
||||
|
||||
This is just a virtual FPGA drivers interface.
|
||||
Vedor should implement their specific drivers.
|
||||
"""
|
||||
VENDOR = "intel"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def discover(self):
|
||||
return sysinfo.fpga_tree()
|
||||
|
||||
def program(self, device_path, image):
|
||||
bdf = ""
|
||||
path = sysinfo.find_pf_by_vf(device_path) if sysinfo.is_vf(
|
||||
device_path) else device_path
|
||||
if sysinfo.is_bdf(device_path):
|
||||
bdf = sysinfo.get_pf_bdf(device_path)
|
||||
else:
|
||||
bdf = sysinfo.get_bdf_by_path(path)
|
||||
bdfs = sysinfo.split_bdf(bdf)
|
||||
cmd = ["sudo", "fpgaconf"]
|
||||
for i in zip(["-b", "-d", "-f"], bdfs):
|
||||
cmd.extend(i)
|
||||
cmd.append(image)
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
||||
# FIXME Should log p.communicate(), p.stderr
|
||||
p.wait()
|
||||
return p.returncode
|
|
@ -0,0 +1,162 @@
|
|||
# Copyright 2018 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 FPGA driver implementation.
|
||||
"""
|
||||
|
||||
# from cyborg.accelerator.drivers.fpga.base import FPGADriver
|
||||
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
SYS_FPGA = "/sys/class/fpga"
|
||||
DEVICE = "device"
|
||||
PF = "physfn"
|
||||
VF = "virtfn*"
|
||||
BDF_PATTERN = re.compile(
|
||||
"^[a-fA-F\d]{4}:[a-fA-F\d]{2}:[a-fA-F\d]{2}\.[a-fA-F\d]$")
|
||||
|
||||
|
||||
DEVICE_FILE_MAP = {"vendor": "vendor_id",
|
||||
"device": "product_id",
|
||||
"sriov_numvfs": "pr_num"}
|
||||
DEVICE_FILE_HANDLER = {}
|
||||
DEVICE_EXPOSED = ["vendor", "device", "sriov_numvfs"]
|
||||
|
||||
|
||||
def all_fpgas():
|
||||
# glob.glob1("/sys/class/fpga", "*")
|
||||
return glob.glob(os.path.join(SYS_FPGA, "*"))
|
||||
|
||||
|
||||
def all_vf_fpgas():
|
||||
return [dev.rsplit("/", 2)[0] for dev in
|
||||
glob.glob(os.path.join(SYS_FPGA, "*/device/physfn"))]
|
||||
|
||||
|
||||
def all_pure_pf_fpgas():
|
||||
return [dev.rsplit("/", 2)[0] for dev in
|
||||
glob.glob(os.path.join(SYS_FPGA, "*/device/virtfn0"))]
|
||||
|
||||
|
||||
def target_symbolic_map():
|
||||
maps = {}
|
||||
for f in glob.glob(os.path.join(SYS_FPGA, "*/device")):
|
||||
maps[os.path.realpath(f)] = os.path.dirname(f)
|
||||
return maps
|
||||
|
||||
|
||||
def bdf_path_map():
|
||||
maps = {}
|
||||
for f in glob.glob(os.path.join(SYS_FPGA, "*/device")):
|
||||
maps[os.path.basename(os.path.realpath(f))] = os.path.dirname(f)
|
||||
return maps
|
||||
|
||||
|
||||
def all_vfs_in_pf_fpgas(pf_path):
|
||||
maps = target_symbolic_map()
|
||||
vfs = glob.glob(os.path.join(pf_path, "device/virtfn*"))
|
||||
return [maps[os.path.realpath(vf)] for vf in vfs]
|
||||
|
||||
|
||||
def all_pf_fpgas():
|
||||
return [dev.rsplit("/", 2)[0] for dev in
|
||||
glob.glob(os.path.join(SYS_FPGA, "*/device/sriov_totalvfs"))]
|
||||
|
||||
|
||||
def is_vf(path):
|
||||
return True if glob.glob(os.path.join(path, "device/physfn")) else False
|
||||
|
||||
|
||||
def find_pf_by_vf(path):
|
||||
maps = target_symbolic_map()
|
||||
p = os.path.realpath(os.path.join(path, "device/physfn"))
|
||||
return maps[p]
|
||||
|
||||
|
||||
def is_bdf(bdf):
|
||||
return True if BDF_PATTERN.match(bdf) else False
|
||||
|
||||
|
||||
def get_bdf_by_path(path):
|
||||
return os.path.basename(os.readlink(os.path.join(path, "device")))
|
||||
|
||||
|
||||
def split_bdf(bdf):
|
||||
return ["0x" + v for v in bdf.replace(".", ":").rsplit(":")[1:]]
|
||||
|
||||
|
||||
def get_pf_bdf(bdf):
|
||||
path = bdf_path_map().get(bdf)
|
||||
if path:
|
||||
path = find_pf_by_vf(path) if is_vf(path) else path
|
||||
return get_bdf_by_path(path)
|
||||
return bdf
|
||||
|
||||
|
||||
def fpga_device(path):
|
||||
infos = {}
|
||||
|
||||
def read_line(filename):
|
||||
with open(filename) as f:
|
||||
return f.readline().strip()
|
||||
|
||||
# NOTE "In 3.x, os.path.walk is removed in favor of os.walk."
|
||||
for (dirpath, dirnames, filenames) in os.walk(path):
|
||||
for filename in filenames:
|
||||
if filename in DEVICE_EXPOSED:
|
||||
key = DEVICE_FILE_MAP.get(filename) or filename
|
||||
if key in DEVICE_FILE_HANDLER and callable(
|
||||
DEVICE_FILE_HANDLER(key)):
|
||||
infos[key] = DEVICE_FILE_HANDLER(key)(
|
||||
os.path.join(dirpath, filename))
|
||||
else:
|
||||
infos[key] = read_line(os.path.join(dirpath, filename))
|
||||
return infos
|
||||
|
||||
|
||||
def fpga_tree():
|
||||
|
||||
def gen_fpga_infos(path, vf=True):
|
||||
name = os.path.basename(path)
|
||||
dpath = os.path.realpath(os.path.join(path, DEVICE))
|
||||
bdf = os.path.basename(dpath)
|
||||
func = "vf" if vf else "pf"
|
||||
pf_bdf = os.path.basename(
|
||||
os.path.realpath(os.path.join(dpath, PF))) if vf else ""
|
||||
fpga = {"path": path, "function": func,
|
||||
"devices": bdf, "assignable": True,
|
||||
"parent_devices": pf_bdf,
|
||||
"name": name}
|
||||
d_info = fpga_device(dpath)
|
||||
fpga.update(d_info)
|
||||
return fpga
|
||||
|
||||
devs = []
|
||||
pure_pfs = all_pure_pf_fpgas()
|
||||
for pf in all_pf_fpgas():
|
||||
fpga = gen_fpga_infos(pf, False)
|
||||
if pf in pure_pfs:
|
||||
fpga["assignable"] = False
|
||||
fpga["regions"] = []
|
||||
vfs = all_vfs_in_pf_fpgas(pf)
|
||||
for vf in vfs:
|
||||
vf_fpga = gen_fpga_infos(vf, True)
|
||||
fpga["regions"].append(vf_fpga)
|
||||
devs.append(fpga)
|
||||
return devs
|
|
@ -0,0 +1,36 @@
|
|||
# Copyright 2018 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.
|
||||
|
||||
|
||||
"""
|
||||
Utils for FPGA driver.
|
||||
"""
|
||||
|
||||
import glob
|
||||
import re
|
||||
|
||||
|
||||
VENDORS = ["intel"] # can extend, such as ["intel", "xilinx"]
|
||||
|
||||
SYS_FPGA_PATH = "/sys/class/fpga"
|
||||
VENDORS_PATTERN = re.compile("|".join(["(%s)" % v for v in VENDORS]))
|
||||
|
||||
|
||||
def discover_vendors():
|
||||
vendors = set()
|
||||
for p in glob.glob1(SYS_FPGA_PATH, "*"):
|
||||
m = VENDORS_PATTERN.match(p)
|
||||
if m:
|
||||
vendors.add(m.group())
|
||||
return list(vendors)
|
|
@ -0,0 +1,105 @@
|
|||
# Copyright 2018 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.
|
||||
|
||||
import mock
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import fixtures
|
||||
|
||||
from cyborg.accelerator.drivers.fpga.base import FPGADriver
|
||||
from cyborg.accelerator.drivers.fpga.intel import sysinfo
|
||||
from cyborg.tests import base
|
||||
from cyborg.tests.unit.accelerator.drivers.fpga.intel import prepare_test_data
|
||||
|
||||
|
||||
class TestFPGADriver(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestFPGADriver, self).setUp()
|
||||
self.syspath = sysinfo.SYS_FPGA
|
||||
sysinfo.SYS_FPGA = "/sys/class/fpga"
|
||||
tmp_sys_dir = self.useFixture(fixtures.TempDir())
|
||||
prepare_test_data.create_fake_sysfs(tmp_sys_dir.path)
|
||||
sysinfo.SYS_FPGA = os.path.join(
|
||||
tmp_sys_dir.path, sysinfo.SYS_FPGA.split("/", 1)[-1])
|
||||
|
||||
def tearDown(self):
|
||||
super(TestFPGADriver, self).tearDown()
|
||||
sysinfo.SYS_FPGA = self.syspath
|
||||
|
||||
def test_create(self):
|
||||
FPGADriver.create("intel")
|
||||
self.assertRaises(LookupError, FPGADriver.create, "xilinx")
|
||||
|
||||
def test_discover(self):
|
||||
d = FPGADriver()
|
||||
self.assertRaises(NotImplementedError, d.discover)
|
||||
|
||||
def test_program(self):
|
||||
d = FPGADriver()
|
||||
self.assertRaises(NotImplementedError, d.program, "path", "image")
|
||||
|
||||
def test_intel_discover(self):
|
||||
expect = [{'function': 'pf', 'assignable': False, 'pr_num': '1',
|
||||
'vendor_id': '0x8086', 'devices': '0000:5e:00.0',
|
||||
'regions': [{
|
||||
'function': 'vf', 'assignable': True,
|
||||
'product_id': '0xbcc1',
|
||||
'name': 'intel-fpga-dev.2',
|
||||
'parent_devices': '0000:5e:00.0',
|
||||
'path': '%s/intel-fpga-dev.2' % sysinfo.SYS_FPGA,
|
||||
'vendor_id': '0x8086',
|
||||
'devices': '0000:5e:00.1'}],
|
||||
'name': 'intel-fpga-dev.0',
|
||||
'parent_devices': '',
|
||||
'path': '%s/intel-fpga-dev.0' % sysinfo.SYS_FPGA,
|
||||
'product_id': '0xbcc0'},
|
||||
{'function': 'pf', 'assignable': True, 'pr_num': '0',
|
||||
'vendor_id': '0x8086', 'devices': '0000:be:00.0',
|
||||
'name': 'intel-fpga-dev.1',
|
||||
'parent_devices': '',
|
||||
'path': '%s/intel-fpga-dev.1' % sysinfo.SYS_FPGA,
|
||||
'product_id': '0xbcc0'}]
|
||||
expect.sort()
|
||||
|
||||
intel = FPGADriver.create("intel")
|
||||
fpgas = intel.discover()
|
||||
fpgas.sort()
|
||||
self.assertEqual(2, len(fpgas))
|
||||
self.assertEqual(fpgas, expect)
|
||||
|
||||
@mock.patch.object(subprocess, 'Popen', autospec=True)
|
||||
def test_intel_program(self, mock_popen):
|
||||
|
||||
class p(object):
|
||||
returncode = 0
|
||||
|
||||
def wait(self):
|
||||
pass
|
||||
|
||||
b = "0x5e"
|
||||
d = "0x00"
|
||||
f = "0x0"
|
||||
expect_cmd = ['sudo', 'fpgaconf', '-b', b,
|
||||
'-d', d, '-f', f, '/path/image']
|
||||
mock_popen.return_value = p()
|
||||
intel = FPGADriver.create("intel")
|
||||
# program VF
|
||||
intel.program("0000:5e:00.1", "/path/image")
|
||||
mock_popen.assert_called_with(expect_cmd, stdout=subprocess.PIPE)
|
||||
|
||||
# program PF
|
||||
intel.program("0000:5e:00.0", "/path/image")
|
||||
mock_popen.assert_called_with(expect_cmd, stdout=subprocess.PIPE)
|
|
@ -0,0 +1,93 @@
|
|||
# Copyright 2018 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.
|
||||
|
||||
import mock
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import fixtures
|
||||
|
||||
from cyborg.accelerator.drivers.fpga.intel import sysinfo
|
||||
from cyborg.accelerator.drivers.fpga.intel.driver import IntelFPGADriver
|
||||
from cyborg.tests import base
|
||||
from cyborg.tests.unit.accelerator.drivers.fpga.intel import prepare_test_data
|
||||
|
||||
|
||||
class TestIntelFPGADriver(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestIntelFPGADriver, self).setUp()
|
||||
self.syspath = sysinfo.SYS_FPGA
|
||||
sysinfo.SYS_FPGA = "/sys/class/fpga"
|
||||
tmp_sys_dir = self.useFixture(fixtures.TempDir())
|
||||
prepare_test_data.create_fake_sysfs(tmp_sys_dir.path)
|
||||
sysinfo.SYS_FPGA = os.path.join(
|
||||
tmp_sys_dir.path, sysinfo.SYS_FPGA.split("/", 1)[-1])
|
||||
|
||||
def tearDown(self):
|
||||
super(TestIntelFPGADriver, self).tearDown()
|
||||
sysinfo.SYS_FPGA = self.syspath
|
||||
|
||||
def test_discover(self):
|
||||
expect = [{'function': 'pf', 'assignable': False, 'pr_num': '1',
|
||||
'vendor_id': '0x8086', 'devices': '0000:5e:00.0',
|
||||
'regions': [{
|
||||
'function': 'vf', 'assignable': True,
|
||||
'product_id': '0xbcc1',
|
||||
'name': 'intel-fpga-dev.2',
|
||||
'parent_devices': '0000:5e:00.0',
|
||||
'path': '%s/intel-fpga-dev.2' % sysinfo.SYS_FPGA,
|
||||
'vendor_id': '0x8086',
|
||||
'devices': '0000:5e:00.1'}],
|
||||
'name': 'intel-fpga-dev.0',
|
||||
'parent_devices': '',
|
||||
'path': '%s/intel-fpga-dev.0' % sysinfo.SYS_FPGA,
|
||||
'product_id': '0xbcc0'},
|
||||
{'function': 'pf', 'assignable': True, 'pr_num': '0',
|
||||
'vendor_id': '0x8086', 'devices': '0000:be:00.0',
|
||||
'parent_devices': '',
|
||||
'name': 'intel-fpga-dev.1',
|
||||
'path': '%s/intel-fpga-dev.1' % sysinfo.SYS_FPGA,
|
||||
'product_id': '0xbcc0'}]
|
||||
expect.sort()
|
||||
|
||||
intel = IntelFPGADriver()
|
||||
fpgas = intel.discover()
|
||||
fpgas.sort()
|
||||
self.assertEqual(2, len(fpgas))
|
||||
self.assertEqual(fpgas, expect)
|
||||
|
||||
@mock.patch.object(subprocess, 'Popen', autospec=True)
|
||||
def test_intel_program(self, mock_popen):
|
||||
|
||||
class p(object):
|
||||
returncode = 0
|
||||
|
||||
def wait(self):
|
||||
pass
|
||||
|
||||
b = "0x5e"
|
||||
d = "0x00"
|
||||
f = "0x0"
|
||||
expect_cmd = ['sudo', 'fpgaconf', '-b', b,
|
||||
'-d', d, '-f', f, '/path/image']
|
||||
mock_popen.return_value = p()
|
||||
intel = IntelFPGADriver()
|
||||
# program VF
|
||||
intel.program("0000:5e:00.1", "/path/image")
|
||||
mock_popen.assert_called_with(expect_cmd, stdout=subprocess.PIPE)
|
||||
|
||||
# program PF
|
||||
intel.program("0000:5e:00.0", "/path/image")
|
||||
mock_popen.assert_called_with(expect_cmd, stdout=subprocess.PIPE)
|
|
@ -0,0 +1,296 @@
|
|||
#!/usr/bin/python
|
||||
# Copyright 2018 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.
|
||||
|
||||
import argparse
|
||||
import copy
|
||||
import glob
|
||||
import os
|
||||
import shutil
|
||||
|
||||
|
||||
PF0_ADDR = "0000:5e:00.0"
|
||||
PF1_ADDR = "0000:be:00.0"
|
||||
VF0_ADDR = "0000:5e:00.1"
|
||||
FPGA_TREE = {
|
||||
"dev.0": {"bdf": PF0_ADDR,
|
||||
"regions": {"dev.2": {"bdf": VF0_ADDR}}},
|
||||
"dev.1": {"bdf": PF1_ADDR}}
|
||||
|
||||
SYS_DEVICES = "sys/devices"
|
||||
SYS_CLASS_FPGA = "sys/class/fpga"
|
||||
|
||||
DEV_PREFIX = "intel-fpga"
|
||||
|
||||
PGFA_DEVICE_COMMON_SUB_DIR = ["power"]
|
||||
|
||||
PGFA_DEVICE_COMMON_CONTENT = {
|
||||
"broken_parity_status": "0",
|
||||
"class": "0x120000",
|
||||
"config": "",
|
||||
"consistent_dma_mask_bits": "64",
|
||||
"d3cold_allowed": "1",
|
||||
"device": "0xbcc0",
|
||||
"dma_mask_bits": "64",
|
||||
"driver_override": "(null)",
|
||||
"enable": "1",
|
||||
"irq": "16",
|
||||
"local_cpulist": "0-111",
|
||||
"local_cpus": "00000000,00000000,00000000,00000000,00000000,"
|
||||
"00000000,00000000,00000000,00000000,00000000,"
|
||||
"0000ffff,ffffffff,ffffffff,ffffffff",
|
||||
"modalias": "pci:v00008086d0000BCC0sv00000000sd00000000bc12sc00i00",
|
||||
"msi_bus": "",
|
||||
"numa_node": "-1",
|
||||
"resource": [
|
||||
"0x00000000c6000000 0x00000000c607ffff 0x000000000014220c",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x00000000c6080000 0x00000000c60fffff 0x000000000014220c",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x00000000c6100000 0x00000000c617ffff 0x000000000014220c",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000"],
|
||||
"resource0": "",
|
||||
"resource0_wc": "",
|
||||
"subsystem_device": "0x0000",
|
||||
"subsystem_vendor": "0x0000",
|
||||
"uevent": [
|
||||
"DRIVER=intel-fpga-pci",
|
||||
"PCI_CLASS=120000",
|
||||
"PCI_ID=8086:BCC0",
|
||||
"PCI_SUBSYS_ID=0000:0000",
|
||||
"PCI_SLOT_NAME=0000:5e:00.0",
|
||||
"MODALIAS=pci:v00008086d0000BCC0sv00000000sd00000000bc12sc00i00"],
|
||||
"vendor": "0x8086"}
|
||||
|
||||
PGFA_DEVICES_SPECIAL_COMMON_CONTENT = {
|
||||
"dev.0": {
|
||||
"resource2": "",
|
||||
"resource2_wc": "",
|
||||
"sriov_numvfs": "1",
|
||||
"sriov_totalvfs": "1",
|
||||
},
|
||||
"dev.1": {
|
||||
"resource": [
|
||||
"0x00000000fbc00000 0x00000000fbc7ffff 0x000000000014220c",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x00000000fbc80000 0x00000000fbcfffff 0x000000000014220c",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x00000000fbd00000 0x00000000fbd7ffff 0x000000000014220c",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000"],
|
||||
"resource2": "",
|
||||
"resource2_wc": "",
|
||||
"sriov_numvfs": "0",
|
||||
"sriov_totalvfs": "1",
|
||||
"uevent": [
|
||||
"DRIVER=intel-fpga-pci",
|
||||
"PCI_CLASS=120000",
|
||||
"PCI_ID=8086:BCC0",
|
||||
"PCI_SUBSYS_ID=0000:0000",
|
||||
"PCI_SLOT_NAME=0000:be:00.0",
|
||||
"MODALIAS=pci:v00008086d0000BCC0sv00000000sd00000000bc12sc00i00"],
|
||||
},
|
||||
"dev.2": {
|
||||
"d3cold_allowed": "0",
|
||||
"device": "0xbcc1",
|
||||
"modalias": "pci:v00008086d0000BCC0sv00000000sd00000000bc12sc00i00",
|
||||
"irq": "0",
|
||||
"resource": [
|
||||
"0x00000000c6100000 0x00000000c617ffff 0x000000000014220c",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
|
||||
"0x0000000000000000 0x0000000000000000 0x0000000000000000"],
|
||||
"uevent": [
|
||||
"DRIVER=intel-fpga-pci",
|
||||
"PCI_CLASS=120000",
|
||||
"PCI_ID=8086:BCC1",
|
||||
"PCI_SUBSYS_ID=0000:0000",
|
||||
"PCI_SLOT_NAME=0000:5e:00.1",
|
||||
"MODALIAS=pci:v00008086d0000BCC1sv00000000sd00000000bc12sc00i00"],
|
||||
}
|
||||
}
|
||||
|
||||
PGFA_DEVICE_COMMON_SOFT_LINK = {
|
||||
"driver": "../../../bus/pci/drivers/intel-fpga-pci",
|
||||
"iommu": "../../virtual/iommu/dmar8",
|
||||
"iommu_group": "../../../kernel/iommu_groups/75",
|
||||
"subsystem": "../../../bus/pci"
|
||||
}
|
||||
|
||||
PGFA_DEVICES_SPECIAL_SOFT_LINK = {
|
||||
"dev.0": {
|
||||
"firmware_node": "../../LNXSYSTM:00/device:00/PNP0A08:18/device:1d4",
|
||||
},
|
||||
"dev.1": {
|
||||
"firmware_node": "../../LNXSYSTM:00/device:00/PNP0A08:19/device:1d5",
|
||||
"iommu": "../../virtual/iommu/dmar4",
|
||||
"iommu_group": "../../../kernel/iommu_groups/76",
|
||||
},
|
||||
"dev.2": {
|
||||
"iommu": "../../virtual/iommu/dmar9",
|
||||
"iommu_group": "../../../kernel/iommu_groups/81",
|
||||
}
|
||||
}
|
||||
PGFA_DEVICES_SPECIAL_SOFT_LINK = {
|
||||
"dev.0": {
|
||||
"firmware_node": "../../LNXSYSTM:00/device:00/PNP0A08:18/device:1d4",
|
||||
},
|
||||
"dev.1": {
|
||||
"firmware_node": "../../LNXSYSTM:00/device:00/PNP0A08:19/device:1d5",
|
||||
"iommu": "../../virtual/iommu/dmar4",
|
||||
"iommu_group": "../../../kernel/iommu_groups/76",
|
||||
},
|
||||
"dev.2": {
|
||||
"iommu": "../../virtual/iommu/dmar9",
|
||||
"iommu_group": "../../../kernel/iommu_groups/81",
|
||||
}
|
||||
}
|
||||
|
||||
PGFA_DEVICE_PF_SOFT_LINK = {
|
||||
"virtfn": lambda k, v: (k + str(int(v.rsplit(".", 1)[-1]) - 1),
|
||||
"/".join(["..", v]))
|
||||
}
|
||||
|
||||
PGFA_DEVICE_VF_SOFT_LINK = {
|
||||
"physfn": lambda k, v: (k, "/".join(["..", v]))
|
||||
}
|
||||
|
||||
|
||||
def gen_fpga_content(path, dev):
|
||||
content = copy.copy(PGFA_DEVICE_COMMON_CONTENT)
|
||||
content.update(PGFA_DEVICES_SPECIAL_COMMON_CONTENT[dev])
|
||||
for k, v in content.items():
|
||||
p = os.path.join(path, k)
|
||||
if not v:
|
||||
os.mknod(p)
|
||||
elif type(v) is str:
|
||||
with open(p, 'a') as f:
|
||||
f.write(v + "\n")
|
||||
elif type(v) is list:
|
||||
with open(p, 'a') as f:
|
||||
f.writelines([l + "\n" for l in v])
|
||||
|
||||
|
||||
def gen_fpga_sub_dir(path):
|
||||
for d in PGFA_DEVICE_COMMON_SUB_DIR:
|
||||
p = os.path.join(path, d)
|
||||
os.makedirs(p)
|
||||
|
||||
|
||||
def gen_fpga_pf_soft_link(path, bdf):
|
||||
for k, v in PGFA_DEVICE_PF_SOFT_LINK.items():
|
||||
if callable(v):
|
||||
k, v = v(k, bdf)
|
||||
os.symlink(v, os.path.join(path, k))
|
||||
|
||||
|
||||
def gen_fpga_common_soft_link(path, bdf):
|
||||
for k, v in PGFA_DEVICE_COMMON_SOFT_LINK.items():
|
||||
os.symlink(v, os.path.join(path, k))
|
||||
|
||||
|
||||
def gen_fpga_vf_soft_link(path, bdf):
|
||||
for k, v in PGFA_DEVICE_VF_SOFT_LINK.items():
|
||||
if callable(v):
|
||||
k, v = v(k, bdf)
|
||||
os.symlink(v, os.path.join(path, k))
|
||||
|
||||
|
||||
def create_devices_path_and_files(tree, device_path, class_fpga_path,
|
||||
vf=False, pfinfo={}):
|
||||
for k, v in tree.items():
|
||||
bdf = v["bdf"]
|
||||
pci_path = "pci" + bdf.rsplit(":", 1)[0]
|
||||
bdf_path = os.path.join(device_path, pci_path, bdf)
|
||||
ln = "-".join([DEV_PREFIX, k])
|
||||
dev_path = os.path.join(bdf_path, "fpga", ln)
|
||||
os.makedirs(dev_path)
|
||||
gen_fpga_content(bdf_path, k)
|
||||
gen_fpga_sub_dir(bdf_path)
|
||||
if vf:
|
||||
gen_fpga_pf_soft_link(pfinfo["path"], bdf)
|
||||
gen_fpga_vf_soft_link(bdf_path, pfinfo["bdf"])
|
||||
pfinfo = {"path": bdf_path, "bdf": bdf}
|
||||
if "regions" in v:
|
||||
create_devices_path_and_files(
|
||||
v["regions"], device_path, class_fpga_path, True, pfinfo)
|
||||
source = dev_path.split("sys")[-1]
|
||||
os.symlink("../.." + source, os.path.join(class_fpga_path, ln))
|
||||
os.symlink("../../../" + bdf, os.path.join(dev_path, "device"))
|
||||
|
||||
|
||||
def create_devices_soft_link(class_fpga_path):
|
||||
devs = glob.glob1(class_fpga_path, "*")
|
||||
for dev in devs:
|
||||
path = os.path.realpath("%s/%s/device" % (class_fpga_path, dev))
|
||||
softlinks = copy.copy(PGFA_DEVICE_COMMON_SOFT_LINK)
|
||||
softlinks.update(
|
||||
PGFA_DEVICES_SPECIAL_SOFT_LINK[dev.rsplit("-", 1)[-1]])
|
||||
for k, v in softlinks.items():
|
||||
source = os.path.normpath(os.path.join(path, v))
|
||||
if not os.path.exists(source):
|
||||
os.makedirs(source)
|
||||
os.symlink(v, os.path.join(path, k))
|
||||
|
||||
|
||||
def create_fake_sysfs(prefix=""):
|
||||
sys_device = os.path.join(prefix, SYS_DEVICES)
|
||||
sys_class_fpga = os.path.join(prefix, SYS_CLASS_FPGA)
|
||||
basedir = os.path.dirname(sys_device)
|
||||
if os.path.exists(basedir):
|
||||
shutil.rmtree(basedir, ignore_errors=False, onerror=None)
|
||||
os.makedirs(sys_class_fpga)
|
||||
create_devices_path_and_files(FPGA_TREE, sys_device, sys_class_fpga)
|
||||
create_devices_soft_link(sys_class_fpga)
|
||||
|
||||
|
||||
def main():
|
||||
create_fake_sysfs()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate a fake sysfs for intel FPGA.")
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
group.add_argument("-v", "--verbose", action="store_true")
|
||||
group.add_argument("-q", "--quiet", action="store_true")
|
||||
parser.add_argument("-p", "--prefix", type=str,
|
||||
default="/tmp", dest="p",
|
||||
help='Set the prefix path of the fake sysfs. '
|
||||
'default "/tmp"')
|
||||
args = parser.parse_args()
|
||||
|
||||
create_fake_sysfs(args.p)
|
Loading…
Reference in New Issue