intel-device-plugins-for-ku.../pkg/fpga/device/device.go
Ed Bartosh de9df8373e fpga_plugin: support in-tree kernel driver
Extended fpga plugin to support both in-tree(DFL) and
out-of-tree (OPAE) kernel drivers.

- fpga_crihook: move JSON parsing to separate functions
- decreased cyclomatic complexity of the CRI hook main() function
- increased readability
- increased test coverage

Signed-off-by: Ed Bartosh <eduard.bartosh@intel.com>
2019-08-24 18:27:15 +03:00

190 lines
6.0 KiB
Go

// Copyright 2019 Intel Corporation. All Rights Reserved.
//
// 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.
package device
import (
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"regexp"
"strconv"
"strings"
"github.com/pkg/errors"
)
const (
sysfsDirectoryOPAE = "/sys/class/fpga"
interfaceIDTemplateOPAE = "/sys/class/fpga/%s/*/pr/interface_id"
afuIDTemplateOPAE = "/sys/class/fpga/intel-fpga-dev.*/%s/afu_id"
fpgaPortDevRegexOPAE = `^intel-fpga-port\.[0-9]+$`
devTemplateOPAE = "/sys/class/fpga/intel-fpga-dev.*/%s/dev"
sysfsDirectoryDFL = "/sys/class/fpga_region"
interfaceIDTemplateDFL = "/sys/class/fpga_region/%s/*/dfl-fme-region.*/fpga_region/region*/compat_id"
afuIDTemplateDFL = "/sys/class/fpga_region/region*/%s/afu_id"
devTemplateDFL = "/sys/class/fpga_region/region*/%s/dev"
fpgaPortDevRegexDFL = `^dfl-port\.[0-9]+$`
fpgaDevRegex = `.*/sys/class/fpga[^/]*/([^/]+)/.+$`
fpgaPortDevRegex = `.*/sys/class/fpga[^/]*/[^/]+/([^/]+)/.+$`
)
var (
fpgaPortDevRegDFL = regexp.MustCompile(fpgaPortDevRegexDFL)
fpgaPortDevRegOPAE = regexp.MustCompile(fpgaPortDevRegexOPAE)
fpgaDevReg = regexp.MustCompile(fpgaDevRegex)
fpgaPortDevReg = regexp.MustCompile(fpgaPortDevRegex)
)
// FPGADevice represents FME and port(AFU) devices
type FPGADevice struct {
ID string // Interface or AFU id
Name string // basename of DevNode
SysfsPath string // path to ID file
DevNode string
Minor uint32
Major uint32
}
// CanonizeID canonizes Interface and AFU ids
func CanonizeID(ID string) string {
return strings.ToLower(strings.Replace(strings.TrimSpace(ID), "-", "", -1))
}
// IsFPGADevice returns true if device is either DFL or OPAE device
func IsFPGADevice(deviceName string) bool {
return fpgaPortDevRegDFL.MatchString(deviceName) || fpgaPortDevRegOPAE.MatchString(deviceName)
}
func getTemplates(sysFsPrefix string) (string, string, string, error) {
var afuIDTemplate, interfaceIDTemplate, devTemplate string
if _, err := os.Stat(path.Join(sysFsPrefix, sysfsDirectoryOPAE)); err == nil {
afuIDTemplate = afuIDTemplateOPAE
interfaceIDTemplate = interfaceIDTemplateOPAE
devTemplate = devTemplateOPAE
} else if _, err := os.Stat(path.Join(sysFsPrefix, sysfsDirectoryDFL)); err == nil {
afuIDTemplate = afuIDTemplateDFL
interfaceIDTemplate = interfaceIDTemplateDFL
devTemplate = devTemplateDFL
} else {
return "", "", "", fmt.Errorf("kernel driver is not loaded: neither %s nor %s sysfs directory is accessible", sysfsDirectoryOPAE, sysfsDirectoryDFL)
}
return path.Join(sysFsPrefix, afuIDTemplate), path.Join(sysFsPrefix, interfaceIDTemplate), path.Join(sysFsPrefix, devTemplate), nil
}
// read file by filepath.Glob pattern
func readFileByPattern(pattern string) (string, []byte, error) {
matches, err := filepath.Glob(pattern)
if err != nil {
return "", nil, err
}
if len(matches) == 0 {
return "", nil, fmt.Errorf("no file found with pattern '%s'", pattern)
}
if len(matches) > 1 {
return "", nil, fmt.Errorf("path pattern '%s' matches multiple files", pattern)
}
data, err := ioutil.ReadFile(matches[0])
if err != nil {
return matches[0], nil, errors.WithStack(err)
}
return matches[0], data, nil
}
func parseDev(devData string) (uint32, uint32, error) {
devData = strings.TrimSpace(devData)
numbers := strings.SplitN(devData, ":", 2)
minor := numbers[0]
major := numbers[1]
minorInt, err := strconv.ParseInt(minor, 10, 32)
if err != nil {
return 0, 0, errors.Wrapf(err, "can't convert device minor %s to a number", minor)
}
majorInt, err := strconv.ParseInt(major, 10, 32)
if err != nil {
return 0, 0, errors.Wrapf(err, "can't convert device major %s to a number", major)
}
return uint32(minorInt), uint32(majorInt), nil
}
func getFPGADevice(afuIDPattern, devPattern string) (*FPGADevice, error) {
idPath, idData, err := readFileByPattern(afuIDPattern)
if err != nil {
return nil, err
}
// Get minor:major from the dev file
_, devData, err := readFileByPattern(devPattern)
if err != nil {
return nil, err
}
major, minor, err := parseDev(string(devData))
if err != nil {
return nil, err
}
// Find fpga device name
subs := fpgaPortDevReg.FindStringSubmatch(idPath)
if len(subs) < 2 {
return nil, fmt.Errorf("can't parse afu_id path: %s", idPath)
}
name := subs[1]
return &FPGADevice{
ID: CanonizeID(string(idData)),
Name: name,
SysfsPath: idPath,
DevNode: path.Join("/dev", name),
Minor: minor,
Major: major,
}, nil
}
// GetAFUDevice creates FPGADevice struct for using port device name
func GetAFUDevice(sysFsPrefix, portDeviceName string) (*FPGADevice, error) {
afuIDTemplate, _, devTemplate, err := getTemplates(sysFsPrefix)
if err != nil {
return nil, err
}
return getFPGADevice(fmt.Sprintf(afuIDTemplate, portDeviceName), fmt.Sprintf(devTemplate, portDeviceName))
}
// GetFMEDevice reads FME properties from SysFs
func GetFMEDevice(sysFsPrefix, portDeviceName string) (*FPGADevice, error) {
portDeviceName = strings.TrimPrefix(portDeviceName, "/dev/")
afuDevice, err := GetAFUDevice(sysFsPrefix, portDeviceName)
if err != nil {
return nil, err
}
// Get top level region name from the AFU path
subs := fpgaDevReg.FindStringSubmatch(afuDevice.SysfsPath)
if len(subs) < 2 {
return nil, fmt.Errorf("can't parse sysfs path: %s", afuDevice.SysfsPath)
}
region := subs[1]
_, interfaceIDTemplate, devTemplate, err := getTemplates(sysFsPrefix)
if err != nil {
return nil, err
}
return getFPGADevice(fmt.Sprintf(interfaceIDTemplate, region), fmt.Sprintf(devTemplate, portDeviceName))
}