From 2430e204d55746fdc7724cf9d7a246ff269596e5 Mon Sep 17 00:00:00 2001 From: Alexander Kanevskiy Date: Sun, 25 Aug 2019 01:28:57 +0300 Subject: [PATCH] fpga_tool: UX improvements - user readable output for fpgainfo/fmeinfo/portinfo commands - new commands: list, list-fme, list-port - new -q flag to suppres headers, progress and too verbose messages - install command will now fail if destination file already exist - new --force flag: allows overwrite files in install command - removed development and debug output --- cmd/fpga_tool/fpga_tool.go | 270 +++++++++++++++++++++++------------ pkg/fpga/linux/dfl.go | 19 +++ pkg/fpga/linux/fpga.go | 4 +- pkg/fpga/linux/intel-fpga.go | 19 +++ pkg/fpga/linux/interfaces.go | 8 +- 5 files changed, 222 insertions(+), 98 deletions(-) diff --git a/cmd/fpga_tool/fpga_tool.go b/cmd/fpga_tool/fpga_tool.go index f8edb268..7d8ed8b2 100644 --- a/cmd/fpga_tool/fpga_tool.go +++ b/cmd/fpga_tool/fpga_tool.go @@ -34,17 +34,18 @@ const ( func main() { var err error - var bitstream string - var device string - var dryRun bool + 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, fmeinfo, portinfo, install, pr") + log.Fatal("Please provide command: info, fpgainfo, install, list, fmeinfo, portinfo, list-fme, list-port, pr") } cmd := flag.Arg(0) @@ -53,25 +54,27 @@ func main() { log.Fatalf("Invalid arguments: %+v", err) } - // fmt.Printf("Cmd: %q\nBitstream: %q\nDevice: %q\n", cmd, bitstream, device) switch cmd { case "info": - err = printBitstreamInfo(bitstream) + err = printBitstreamInfo(bitstream, quiet) case "pr": - err = doPR(device, bitstream, dryRun) + err = doPR(device, bitstream, dryRun, quiet) case "fpgainfo": - err = fpgaInfo(device) + err = fpgaInfo(device, quiet) case "fmeinfo": - err = fmeInfo(device) + err = fmeInfo(device, quiet) case "portinfo": - err = portInfo(device) + err = portInfo(device, quiet) case "install": - err = installBitstream(bitstream, dryRun) - case "magic": - err = magic(device) + 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) @@ -85,7 +88,7 @@ func validateFlags(cmd, bitstream, device string) error { if bitstream == "" { return errors.Errorf("bitstream filename is missing") } - case "fpgainfo", "fmeinfo", "portinfo", "magic": + case "fpgainfo", "fmeinfo", "portinfo": // device must not be empty if device == "" { return errors.Errorf("FPGA device name is missing") @@ -102,13 +105,7 @@ func validateFlags(cmd, bitstream, device string) error { return nil } -// WIP testing command -func magic(dev string) (err error) { - fmt.Println(fpga.ListFpgaDevices()) - return -} - -func installBitstream(fname string, dryRun bool) (err error) { +func installBitstream(fname string, dryRun, force, quiet bool) (err error) { info, err := bitstream.Open(fname) if err != nil { return @@ -117,10 +114,12 @@ func installBitstream(fname string, dryRun bool) (err error) { installPath := info.InstallPath(fpgaBitStreamDirectory) - fmt.Printf("Installing bitstream %q as %q\n", fname, installPath) - if dryRun { - fmt.Println("Dry-run: no copying performed") - return + 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 { @@ -131,8 +130,15 @@ func installBitstream(fname string, dryRun bool) (err error) { return errors.Wrap(err, "can't open bitstream file") } defer src.Close() - dst, err := os.OpenFile(installPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) + 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() @@ -140,17 +146,7 @@ func installBitstream(fname string, dryRun bool) (err error) { return } -func fpgaInfo(fname string) error { - switch { - case fpga.IsFpgaFME(fname): - return fmeInfo(fname) - case fpga.IsFpgaPort(fname): - return portInfo(fname) - } - return errors.Errorf("unknown FPGA device file %s", fname) -} - -func printBitstreamInfo(fname string) (err error) { +func printBitstreamInfo(fname string, quiet bool) (err error) { info, err := bitstream.Open(fname) if err != nil { return @@ -162,7 +158,7 @@ func printBitstreamInfo(fname string) (err error) { fmt.Printf("Unique UUID : %q\n", info.UniqueUUID()) fmt.Printf("Installation Path : %q\n", info.InstallPath(fpgaBitStreamDirectory)) extra := info.ExtraMetadata() - if len(extra) > 0 { + if len(extra) > 0 && !quiet { fmt.Println("Extra:") for k, v := range extra { fmt.Printf("\t%s : %q\n", k, v) @@ -171,7 +167,17 @@ func printBitstreamInfo(fname string) (err error) { return } -func fmeInfo(fname string) error { +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.FpgaFME var err error f, err = fpga.NewFpgaFME(fname) @@ -179,22 +185,36 @@ func fmeInfo(fname string) error { return err } defer f.Close() - fmt.Print("API:") - fmt.Println(f.GetAPIVersion()) - fmt.Print("CheckExtension:") - fmt.Println(f.CheckExtension()) - - fmt.Println("GetDevPath: ", f.GetDevPath()) - fmt.Println("GetSysFsPath: ", f.GetSysFsPath()) - fmt.Println("GetName: ", f.GetName()) - pci, err := f.GetPCIDevice() - fmt.Printf("GetPCIDevice: %+v %+v\n", pci, err) - fmt.Println("GetInterfaceUUID: ", f.GetInterfaceUUID()) - fmt.Println("GetPortNums: ", f.GetPortsNum()) - return nil + return printFpgaFME(f, quiet) } -func portInfo(fname string) error { +func printFpgaFME(f fpga.FpgaFME, 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.FpgaPort var err error f, err = fpga.NewFpgaPort(fname) @@ -202,53 +222,113 @@ func portInfo(fname string) error { return err } defer f.Close() - fmt.Print("API:") - fmt.Println(f.GetAPIVersion()) - fmt.Print("CheckExtension:") - fmt.Println(f.CheckExtension()) - fmt.Print("Reset:") - fmt.Println(f.PortReset()) - fmt.Print("PortGetInfo:") - fmt.Println(f.PortGetInfo()) - pi, err := f.PortGetInfo() - if err == nil { - for idx := 0; uint32(idx) < pi.Regions; idx++ { - fmt.Printf("PortGetRegionInfo %d\n", idx) - fmt.Println(f.PortGetRegionInfo(uint32(idx))) + return printFpgaPort(f, quiet) +} + +func printFpgaPort(f fpga.FpgaPort, 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 { + if apiVer, err := f.GetAPIVersion(); err == nil { + fmt.Printf("Kernet API Version : %d\n", apiVer) + pi, err := f.PortGetInfo() + if err == nil { + fmt.Printf("Port Regions : %d\n", pi.Regions) + for idx := 0; uint32(idx) < pi.Regions; idx++ { + if ri, err := f.PortGetRegionInfo(uint32(idx)); err == nil { + fmt.Printf("Port Region (Index/Size/Offset) : %d / %d / %d\n", ri.Index, ri.Size, ri.Offset) + } + } + } } } - - fmt.Println("GetDevPath: ", f.GetDevPath()) - fmt.Println("GetSysFsPath: ", f.GetSysFsPath()) - fmt.Println("GetName: ", f.GetName()) - pci, err := f.GetPCIDevice() - fmt.Printf("GetPCIDevice: %+v %+v\n", pci, err) - id, err := f.GetPortID() - fmt.Printf("GetPort: %+v %+v\n", id, err) - fmt.Println("GetAcceleratorTypeUUID: ", f.GetAcceleratorTypeUUID()) - fmt.Println("GetInterfaceUUID: ", f.GetInterfaceUUID()) - fme, err := f.GetFME() - fmt.Printf("GetFME: %+v %+v\n", fme, err) - - return nil + return } -func doPR(dev, bs string, dryRun bool) error { - - f, err := fpga.NewFpgaPort(dev) - if err != nil { - return err +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) } - defer f.Close() - m, err := bitstream.Open(bs) - if err != nil { - return err + 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) + } } - defer m.Close() + return +} - fmt.Printf("Before programming I %q A %q\n", f.GetInterfaceUUID(), f.GetAcceleratorTypeUUID()) - fmt.Printf("Trying to program %s to port %s: ", bs, dev) - fmt.Println(f.PR(m, dryRun)) - fmt.Printf("After programming I %q A %q\n", f.GetInterfaceUUID(), f.GetAcceleratorTypeUUID()) +func doPR(dev, fname string, dryRun, quiet bool) (err error) { + fp, err := fpga.NewFpgaPort(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 } diff --git a/pkg/fpga/linux/dfl.go b/pkg/fpga/linux/dfl.go index 2e82e9a3..da24c4ea 100644 --- a/pkg/fpga/linux/dfl.go +++ b/pkg/fpga/linux/dfl.go @@ -275,6 +275,25 @@ func (f *DflFME) GetInterfaceUUID() (id string) { return f.CompatID } +// GetSocketID returns physical socket number, in case NUMA enumeration fails +func (f *DflFME) GetSocketID() (uint32, error) { + if f.SocketID == "" { + return math.MaxUint32, errors.Errorf("n/a") + } + id, err := strconv.ParseUint(f.SocketID, 10, 32) + return uint32(id), err +} + +// GetBitstreamID returns FME bitstream id +func (f *DflFME) GetBitstreamID() string { + return f.BitstreamID +} + +// GetBitstreamMetadata returns FME bitstream metadata +func (f *DflFME) GetBitstreamMetadata() string { + return f.BitstreamMetadata +} + // Update properties from sysfs func (f *DflFME) updateProperties() error { pci, err := f.GetPCIDevice() diff --git a/pkg/fpga/linux/fpga.go b/pkg/fpga/linux/fpga.go index f601da90..01316e00 100644 --- a/pkg/fpga/linux/fpga.go +++ b/pkg/fpga/linux/fpga.go @@ -56,7 +56,7 @@ func NewFpgaPort(fname string) (FpgaPort, error) { case strings.HasPrefix(devName, intelFpgaPortPrefix): return NewIntelFpgaPort(fname) } - return nil, errors.Errorf("unknown type of FPGA port %s", devName) + return nil, errors.Errorf("unknown type of FPGA port %s", fname) } // NewFpgaFME returns FpgaFME for specified device node @@ -71,7 +71,7 @@ func NewFpgaFME(fname string) (FpgaFME, error) { case strings.HasPrefix(devName, intelFpgaFmePrefix): return NewIntelFpgaFME(fname) } - return nil, errors.Errorf("unknown type of FPGA FME %s", devName) + return nil, errors.Errorf("unknown type of FPGA FME %s", fname) } // ListFpgaDevices returns two lists of FPGA device nodes: FMEs and Ports diff --git a/pkg/fpga/linux/intel-fpga.go b/pkg/fpga/linux/intel-fpga.go index e2cda365..d95f681b 100644 --- a/pkg/fpga/linux/intel-fpga.go +++ b/pkg/fpga/linux/intel-fpga.go @@ -271,6 +271,25 @@ func (f *IntelFpgaFME) GetInterfaceUUID() (id string) { return f.CompatID } +// GetSocketID returns physical socket number, in case NUMA enumeration fails +func (f *IntelFpgaFME) GetSocketID() (uint32, error) { + if f.SocketID == "" { + return math.MaxUint32, errors.Errorf("n/a") + } + id, err := strconv.ParseUint(f.SocketID, 10, 32) + return uint32(id), err +} + +// GetBitstreamID returns FME bitstream id +func (f *IntelFpgaFME) GetBitstreamID() string { + return f.BitstreamID +} + +// GetBitstreamMetadata returns FME bitstream metadata +func (f *IntelFpgaFME) GetBitstreamMetadata() string { + return f.BitstreamMetadata +} + // Update properties from sysfs func (f *IntelFpgaFME) updateProperties() error { pci, err := f.GetPCIDevice() diff --git a/pkg/fpga/linux/interfaces.go b/pkg/fpga/linux/interfaces.go index 002ae32d..e4956332 100644 --- a/pkg/fpga/linux/interfaces.go +++ b/pkg/fpga/linux/interfaces.go @@ -66,7 +66,13 @@ type FpgaFME interface { GetPortsNum() int // InterfaceUUID returns Interface UUID for FME GetInterfaceUUID() string - // GetPort returns FpgaPort of the desired FPGA port within that FME + // GetSocketID returns physical socket number, in case NUMA enumeration fails + GetSocketID() (uint32, error) + // GetBitstreamID returns FME bitstream id + GetBitstreamID() string + // GetBitstreamMetadata returns FME bitstream metadata + GetBitstreamMetadata() string + // GetPort returns FpgaPort of the desired FPGA port index within that FME // GetPort(uint32) (FpgaPort, error) }