mirror of
https://github.com/intel/intel-device-plugins-for-kubernetes.git
synced 2025-06-03 03:59:37 +00:00

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>
190 lines
6.0 KiB
Go
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))
|
|
}
|