mirror of
https://github.com/intel/intel-device-plugins-for-kubernetes.git
synced 2025-06-03 03:59:37 +00:00
347 lines
9.1 KiB
Go
347 lines
9.1 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 main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/intel/intel-device-plugins-for-kubernetes/pkg/fpga"
|
|
"github.com/intel/intel-device-plugins-for-kubernetes/pkg/fpga/bitstream"
|
|
)
|
|
|
|
const (
|
|
fpgaBitStreamDirectory = "/srv/intel.com/fpga"
|
|
)
|
|
|
|
func main() {
|
|
var err error
|
|
var bitstream, device string
|
|
var dryRun, force, quiet bool
|
|
flag.StringVar(&bitstream, "b", "", "Path to bitstream file (GBS or AOCX)")
|
|
flag.StringVar(&device, "d", "", "Path to device node (FME or Port)")
|
|
flag.BoolVar(&dryRun, "dry-run", false, "Don't write/program, just validate and log")
|
|
flag.BoolVar(&force, "force", false, "Force overwrite operation for installing bitstreams")
|
|
flag.BoolVar(&quiet, "q", false, "Quiet mode. Only errors will be reported")
|
|
|
|
flag.Parse()
|
|
|
|
if flag.NArg() < 1 {
|
|
log.Fatal("Please provide command: info, fpgainfo, install, list, fmeinfo, portinfo, list-fme, list-port, pr")
|
|
}
|
|
|
|
cmd := flag.Arg(0)
|
|
err = validateFlags(cmd, bitstream, device)
|
|
if err != nil {
|
|
log.Fatalf("Invalid arguments: %+v", err)
|
|
}
|
|
|
|
switch cmd {
|
|
case "info":
|
|
err = printBitstreamInfo(bitstream, quiet)
|
|
case "pr":
|
|
err = doPR(device, bitstream, dryRun, quiet)
|
|
case "fpgainfo":
|
|
err = fpgaInfo(device, quiet)
|
|
case "fmeinfo":
|
|
err = fmeInfo(device, quiet)
|
|
case "portinfo":
|
|
err = portInfo(device, quiet)
|
|
case "install":
|
|
err = installBitstream(bitstream, dryRun, force, quiet)
|
|
case "list":
|
|
err = listDevices(true, true, quiet)
|
|
case "list-fme":
|
|
err = listDevices(true, false, quiet)
|
|
case "list-port":
|
|
err = listDevices(false, true, quiet)
|
|
default:
|
|
err = errors.Errorf("unknown command %+v", flag.Args())
|
|
}
|
|
if err != nil {
|
|
log.Fatalf("%+v", err)
|
|
}
|
|
}
|
|
|
|
func validateFlags(cmd, bitstream, device string) error {
|
|
switch cmd {
|
|
case "info", "install":
|
|
// bitstream must not be empty
|
|
if bitstream == "" {
|
|
return errors.Errorf("bitstream filename is missing")
|
|
}
|
|
case "fpgainfo", "fmeinfo", "portinfo":
|
|
// device must not be empty
|
|
if device == "" {
|
|
return errors.Errorf("FPGA device name is missing")
|
|
}
|
|
case "pr":
|
|
// device and bitstream can't be empty
|
|
if bitstream == "" {
|
|
return errors.Errorf("bitstream filename is missing")
|
|
}
|
|
if device == "" {
|
|
return errors.Errorf("FPGA device name is missing")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func installBitstream(fname string, dryRun, force, quiet bool) (err error) {
|
|
info, err := bitstream.Open(fname)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer info.Close()
|
|
|
|
installPath := info.InstallPath(fpgaBitStreamDirectory)
|
|
|
|
if !quiet {
|
|
fmt.Printf("Installing bitstream %q as %q\n", fname, installPath)
|
|
if dryRun {
|
|
fmt.Println("Dry-run: no copying performed")
|
|
return
|
|
}
|
|
}
|
|
err = os.MkdirAll(filepath.Dir(installPath), 0755)
|
|
if err != nil {
|
|
return errors.Wrap(err, "unable to create destination directory")
|
|
}
|
|
src, err := os.Open(fname)
|
|
if err != nil {
|
|
return errors.Wrap(err, "can't open bitstream file")
|
|
}
|
|
defer src.Close()
|
|
flags := os.O_WRONLY | os.O_CREATE | os.O_TRUNC
|
|
if !force {
|
|
flags = flags | os.O_EXCL
|
|
}
|
|
dst, err := os.OpenFile(installPath, flags, 0644)
|
|
if err != nil {
|
|
if os.IsExist(err) {
|
|
return fmt.Errorf("destination file %q already exist. Use --force to overwrite it", installPath)
|
|
}
|
|
return errors.Wrap(err, "can't create destination file")
|
|
}
|
|
defer dst.Close()
|
|
_, err = io.Copy(dst, src)
|
|
return err
|
|
}
|
|
|
|
func printBitstreamInfo(fname string, quiet bool) (err error) {
|
|
info, err := bitstream.Open(fname)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer info.Close()
|
|
fmt.Printf("Bitstream file : %q\n", fname)
|
|
fmt.Printf("Interface UUID : %q\n", info.InterfaceUUID())
|
|
fmt.Printf("Accelerator Type UUID : %q\n", info.AcceleratorTypeUUID())
|
|
fmt.Printf("Unique UUID : %q\n", info.UniqueUUID())
|
|
fmt.Printf("Installation Path : %q\n", info.InstallPath(fpgaBitStreamDirectory))
|
|
extra := info.ExtraMetadata()
|
|
if len(extra) > 0 && !quiet {
|
|
fmt.Println("Extra:")
|
|
for k, v := range extra {
|
|
fmt.Printf("\t%s : %q\n", k, v)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func fpgaInfo(fname string, quiet bool) error {
|
|
switch {
|
|
case fpga.IsFpgaFME(fname):
|
|
return fmeInfo(fname, quiet)
|
|
case fpga.IsFpgaPort(fname):
|
|
return portInfo(fname, quiet)
|
|
}
|
|
return errors.Errorf("unknown FPGA device file %s", fname)
|
|
}
|
|
|
|
func fmeInfo(fname string, quiet bool) error {
|
|
var f fpga.FME
|
|
var err error
|
|
f, err = fpga.NewFME(fname)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
return printFpgaFME(f, quiet)
|
|
}
|
|
|
|
func printFpgaFME(f fpga.FME, quiet bool) (err error) {
|
|
fmt.Println("//****** FME ******//")
|
|
fmt.Printf("Name : %s\n", f.GetName())
|
|
fmt.Printf("Device Node : %s\n", f.GetDevPath())
|
|
fmt.Printf("SysFS Path : %s\n", f.GetSysFsPath())
|
|
pci, err := f.GetPCIDevice()
|
|
if err != nil {
|
|
return
|
|
}
|
|
printPCIeInfo(pci, quiet)
|
|
fmt.Printf("Interface UUID : %s\n", f.GetInterfaceUUID())
|
|
if !quiet {
|
|
if apiVer, err := f.GetAPIVersion(); err == nil {
|
|
fmt.Printf("Kernet API Version : %d\n", apiVer)
|
|
}
|
|
fmt.Printf("Ports Num : %d\n", f.GetPortsNum())
|
|
if id, err := f.GetSocketID(); err == nil {
|
|
fmt.Printf("Socket Id : %d\n", id)
|
|
}
|
|
fmt.Printf("Bitstream Id : %s\n", f.GetBitstreamID())
|
|
fmt.Printf("Bitstream Metadata : %s\n", f.GetBitstreamMetadata())
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func portInfo(fname string, quiet bool) error {
|
|
var f fpga.Port
|
|
var err error
|
|
f, err = fpga.NewPort(fname)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
return printFpgaPort(f, quiet)
|
|
}
|
|
|
|
func printFpgaPort(f fpga.Port, quiet bool) (err error) {
|
|
fmt.Println("//****** PORT ******//")
|
|
fmt.Printf("Name : %s\n", f.GetName())
|
|
fmt.Printf("Device Node : %s\n", f.GetDevPath())
|
|
fmt.Printf("SysFS Path : %s\n", f.GetSysFsPath())
|
|
pci, err := f.GetPCIDevice()
|
|
if err != nil {
|
|
return
|
|
}
|
|
printPCIeInfo(pci, quiet)
|
|
fme, err := f.GetFME()
|
|
if err != nil {
|
|
return
|
|
}
|
|
fmt.Printf("FME Name : %s\n", fme.GetName())
|
|
num, err := f.GetPortID()
|
|
if err != nil {
|
|
return
|
|
}
|
|
fmt.Printf("Port Id : %d\n", num)
|
|
fmt.Printf("Interface UUID : %s\n", f.GetInterfaceUUID())
|
|
fmt.Printf("Accelerator UUID : %s\n", f.GetAcceleratorTypeUUID())
|
|
|
|
if quiet {
|
|
return err
|
|
}
|
|
|
|
apiVer, err := f.GetAPIVersion()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Printf("Kernet API Version : %d\n", apiVer)
|
|
|
|
pi, err := f.PortGetInfo()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Printf("Port Regions : %d\n", pi.Regions)
|
|
for idx := uint32(0); idx < pi.Regions; idx++ {
|
|
ri, err := f.PortGetRegionInfo(idx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Printf("Port Region (Index/Size/Offset) : %d / %d / %d\n", ri.Index, ri.Size, ri.Offset)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func printPCIeInfo(pci *fpga.PCIDevice, quiet bool) {
|
|
fmt.Printf("PCIe s:b:d:f : %s\n", pci.BDF)
|
|
if pci.PhysFn != nil && !quiet {
|
|
fmt.Printf("Physical Function PCIe s:b:d:f : %s\n", pci.PhysFn.BDF)
|
|
}
|
|
fmt.Printf("Device Id : %s:%s\n", pci.Vendor, pci.Device)
|
|
if !quiet {
|
|
fmt.Printf("Device Class : %s\n", pci.Class)
|
|
fmt.Printf("Local CPUs : %s\n", pci.CPUs)
|
|
fmt.Printf("NUMA : %s\n", pci.NUMA)
|
|
if pci.VFs != "" {
|
|
fmt.Printf("SR-IOV Virtual Functions : %s\n", pci.VFs)
|
|
}
|
|
if pci.TotalVFs != "" {
|
|
fmt.Printf("SR-IOV maximum Virtual Functions : %s\n", pci.TotalVFs)
|
|
}
|
|
}
|
|
}
|
|
|
|
func doPR(dev, fname string, dryRun, quiet bool) (err error) {
|
|
fp, err := fpga.NewPort(dev)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer fp.Close()
|
|
bs, err := bitstream.Open(fname)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer bs.Close()
|
|
|
|
if !quiet {
|
|
fmt.Printf("Before: Interface ID: %q AFU ID: %q\n", fp.GetInterfaceUUID(), fp.GetAcceleratorTypeUUID())
|
|
fmt.Printf("Programming %q to port %q: ", fname, dev)
|
|
}
|
|
err = fp.PR(bs, dryRun)
|
|
if !quiet {
|
|
if err != nil {
|
|
fmt.Println("FAILED")
|
|
} else {
|
|
fmt.Println("OK")
|
|
}
|
|
fmt.Printf("After : Interface ID: %q AFU ID: %q\n", fp.GetInterfaceUUID(), fp.GetAcceleratorTypeUUID())
|
|
}
|
|
return
|
|
}
|
|
|
|
func listDevices(listFMEs, listPorts, quiet bool) error {
|
|
fmes, ports := fpga.ListFpgaDevices()
|
|
if listFMEs {
|
|
if !quiet {
|
|
fmt.Printf("Detected FPGA FMEs: %d\n", len(fmes))
|
|
}
|
|
for _, v := range fmes {
|
|
fmt.Println(v)
|
|
}
|
|
}
|
|
if listPorts {
|
|
if !quiet {
|
|
fmt.Printf("Detected FPGA Ports: %d\n", len(ports))
|
|
}
|
|
for _, v := range ports {
|
|
fmt.Println(v)
|
|
}
|
|
}
|
|
return nil
|
|
}
|