Skip to content

Commit

Permalink
Merge pull request #385 from mlorenzofr/accelerators
Browse files Browse the repository at this point in the history
Add support for processing accelerators hardware
  • Loading branch information
jaypipes authored Oct 15, 2024
2 parents 80d3134 + 25c02a9 commit e050c5a
Show file tree
Hide file tree
Showing 14 changed files with 409 additions and 26 deletions.
57 changes: 56 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ hardware:
* [`ghw.Network()`](#network)
* [`ghw.PCI()`](#pci)
* [`ghw.GPU()`](#gpu) (graphical processing unit)
* [`ghw.Accelerator()`](#accelerator) (processing accelerators, AI)
* [`ghw.Chassis()`](#chassis)
* [`ghw.BIOS()`](#bios)
* [`ghw.Baseboard()`](#baseboard)
Expand Down Expand Up @@ -893,7 +894,7 @@ information about the host computer's graphics hardware.
The `ghw.GPUInfo` struct contains one field:

* `ghw.GPUInfo.GraphicCards` is an array of pointers to `ghw.GraphicsCard`
structs, one for each graphics card found for the systen
structs, one for each graphics card found for the system

Each `ghw.GraphicsCard` struct contains the following fields:

Expand Down Expand Up @@ -945,6 +946,60 @@ information
`ghw.TopologyNode` struct if you'd like to dig deeper into the NUMA/topology
subsystem

### Accelerator

The `ghw.Accelerator()` function returns a `ghw.AcceleratorInfo` struct that contains
information about the host computer's processing accelerator hardware. In this category
we can find used hardware for AI. The hardware detected in this category will be
processing accelerators (PCI class `1200`), 3D controllers (`0302`) and Display
controllers (`0380`).

The `ghw.AcceleratorInfo` struct contains one field:

* `ghw.AcceleratorInfo.Devices` is an array of pointers to `ghw.AcceleratorDevice`
structs, one for each processing accelerator card found for the system.

Each `ghw.AcceleratorDevice` struct contains the following fields:

* `ghw.AcceleratorDevice.Address` is the PCI address for the processing accelerator card.
* `ghw.AcceleratorDevice.PCIDevice` is a pointer to a `ghw.PCIDevice` struct.
describing the processing accelerator card. This may be `nil` if no PCI device
information could be determined for the card.

```go
package main

import (
"fmt"

"github.com/jaypipes/ghw"
)

func main() {
accel, err := ghw.Accelerator()
if err != nil {
fmt.Printf("Error getting processing accelerator info: %v", err)
}

fmt.Printf("%v\n", accel)

for _, card := range accel.Devices {
fmt.Printf(" %v\n", device)
}
}
```

Example output from a testing machine:

```
processing accelerators (1 device)
device @0000:00:04.0 -> driver: 'fake_pci_driver' class: 'Processing accelerators' vendor: 'Red Hat, Inc.' product: 'QEMU PCI Test Device'
```

**NOTE**: You can [read more](#pci) about the fields of the `ghw.PCIDevice`
struct if you'd like to dig deeper into PCI subsystem and programming interface
information

### Chassis

The `ghw.Chassis()` function returns a `ghw.ChassisInfo` struct that contains
Expand Down
8 changes: 8 additions & 0 deletions alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package ghw

import (
"github.com/jaypipes/ghw/pkg/accelerator"
"github.com/jaypipes/ghw/pkg/baseboard"
"github.com/jaypipes/ghw/pkg/bios"
"github.com/jaypipes/ghw/pkg/block"
Expand Down Expand Up @@ -183,3 +184,10 @@ type GraphicsCard = gpu.GraphicsCard
var (
GPU = gpu.New
)

type AcceleratorInfo = accelerator.Info
type AcceleratorDevice = accelerator.AcceleratorDevice

var (
Accelerator = accelerator.New
)
48 changes: 48 additions & 0 deletions cmd/ghwc/commands/accelerator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// Use and distribution licensed under the Apache license version 2.
//
// See the COPYING file in the root project directory for full text.
//

package commands

import (
"fmt"

"github.com/jaypipes/ghw"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)

// acceleratorCmd represents the install command
var acceleratorCmd = &cobra.Command{
Use: "accelerator",
Short: "Show processing accelerators information for the host system",
RunE: showGPU,
}

// showAccelerator show processing accelerators information for the host system.
func showAccelerator(cmd *cobra.Command, args []string) error {
accel, err := ghw.Accelerator()
if err != nil {
return errors.Wrap(err, "error getting Accelerator info")
}

switch outputFormat {
case outputFormatHuman:
fmt.Printf("%v\n", accel)

for _, card := range accel.Devices {
fmt.Printf(" %v\n", card)
}
case outputFormatJSON:
fmt.Printf("%s\n", accel.JSONString(pretty))
case outputFormatYAML:
fmt.Printf("%s", accel.YAMLString())
}
return nil
}

func init() {
rootCmd.AddCommand(acceleratorCmd)
}
3 changes: 3 additions & 0 deletions cmd/ghwc/commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ func showAll(cmd *cobra.Command, args []string) error {
if err := showProduct(cmd, args); err != nil {
return err
}
if err := showAccelerator(cmd, args); err != nil {
return err
}
case outputFormatJSON:
host, err := ghw.Host()
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/samber/lo v1.47.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/sys v0.1.0 // indirect
golang.org/x/text v0.16.0 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,17 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc=
github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
58 changes: 33 additions & 25 deletions host.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"github.com/jaypipes/ghw/pkg/context"

"github.com/jaypipes/ghw/pkg/accelerator"
"github.com/jaypipes/ghw/pkg/baseboard"
"github.com/jaypipes/ghw/pkg/bios"
"github.com/jaypipes/ghw/pkg/block"
Expand All @@ -28,18 +29,19 @@ import (
// HostInfo is a wrapper struct containing information about the host system's
// memory, block storage, CPU, etc
type HostInfo struct {
ctx *context.Context
Memory *memory.Info `json:"memory"`
Block *block.Info `json:"block"`
CPU *cpu.Info `json:"cpu"`
Topology *topology.Info `json:"topology"`
Network *net.Info `json:"network"`
GPU *gpu.Info `json:"gpu"`
Chassis *chassis.Info `json:"chassis"`
BIOS *bios.Info `json:"bios"`
Baseboard *baseboard.Info `json:"baseboard"`
Product *product.Info `json:"product"`
PCI *pci.Info `json:"pci"`
ctx *context.Context
Memory *memory.Info `json:"memory"`
Block *block.Info `json:"block"`
CPU *cpu.Info `json:"cpu"`
Topology *topology.Info `json:"topology"`
Network *net.Info `json:"network"`
GPU *gpu.Info `json:"gpu"`
Accelerator *accelerator.Info `json:"accelerator"`
Chassis *chassis.Info `json:"chassis"`
BIOS *bios.Info `json:"bios"`
Baseboard *baseboard.Info `json:"baseboard"`
Product *product.Info `json:"product"`
PCI *pci.Info `json:"pci"`
}

// Host returns a pointer to a HostInfo struct that contains fields with
Expand Down Expand Up @@ -71,6 +73,10 @@ func Host(opts ...*WithOption) (*HostInfo, error) {
if err != nil {
return nil, err
}
acceleratorInfo, err := accelerator.New(opts...)
if err != nil {
return nil, err
}
chassisInfo, err := chassis.New(opts...)
if err != nil {
return nil, err
Expand All @@ -92,29 +98,31 @@ func Host(opts ...*WithOption) (*HostInfo, error) {
return nil, err
}
return &HostInfo{
ctx: ctx,
CPU: cpuInfo,
Memory: memInfo,
Block: blockInfo,
Topology: topologyInfo,
Network: netInfo,
GPU: gpuInfo,
Chassis: chassisInfo,
BIOS: biosInfo,
Baseboard: baseboardInfo,
Product: productInfo,
PCI: pciInfo,
ctx: ctx,
CPU: cpuInfo,
Memory: memInfo,
Block: blockInfo,
Topology: topologyInfo,
Network: netInfo,
GPU: gpuInfo,
Accelerator: acceleratorInfo,
Chassis: chassisInfo,
BIOS: biosInfo,
Baseboard: baseboardInfo,
Product: productInfo,
PCI: pciInfo,
}, nil
}

// String returns a newline-separated output of the HostInfo's component
// structs' String-ified output
func (info *HostInfo) String() string {
return fmt.Sprintf(
"%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
"%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
info.Block.String(),
info.CPU.String(),
info.GPU.String(),
info.Accelerator.String(),
info.Memory.String(),
info.Network.String(),
info.Topology.String(),
Expand Down
7 changes: 7 additions & 0 deletions host_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,11 @@ func TestHost(t *testing.T) {
if gpu == nil {
t.Fatalf("Expected non-nil GPU but got nil.")
}

// Processing accelerator cards are not common nowadays.
// You may not have one in your machine, so this check displays a message but does not interrupt the test.
accel := host.Accelerator
if accel == nil {
t.Logf("WARNING: Processing accelerator cards not detected.")
}
}
84 changes: 84 additions & 0 deletions pkg/accelerator/accelerator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//
// Use and distribution licensed under the Apache license version 2.
//
// See the COPYING file in the root project directory for full text.
//

package accelerator

import (
"fmt"

"github.com/jaypipes/ghw/pkg/context"
"github.com/jaypipes/ghw/pkg/marshal"
"github.com/jaypipes/ghw/pkg/option"
"github.com/jaypipes/ghw/pkg/pci"
)

type AcceleratorDevice struct {
// the PCI address where the accelerator device can be found
Address string `json:"address"`
// pointer to a PCIDevice struct that describes the vendor and product
// model, etc
PCIDevice *pci.Device `json:"pci_device"`
}

func (dev *AcceleratorDevice) String() string {
deviceStr := dev.Address
if dev.PCIDevice != nil {
deviceStr = dev.PCIDevice.String()
}
nodeStr := ""
return fmt.Sprintf(
"device %s@%s",
nodeStr,
deviceStr,
)
}

type Info struct {
ctx *context.Context
Devices []*AcceleratorDevice `json:"devices"`
}

// New returns a pointer to an Info struct that contains information about the
// accelerator devices on the host system
func New(opts ...*option.Option) (*Info, error) {
ctx := context.New(opts...)
info := &Info{ctx: ctx}

if err := ctx.Do(info.load); err != nil {
return nil, err
}
return info, nil
}

func (i *Info) String() string {
numDevsStr := "devices"
if len(i.Devices) == 1 {
numDevsStr = "device"
}
return fmt.Sprintf(
"processing accelerators (%d %s)",
len(i.Devices),
numDevsStr,
)
}

// simple private struct used to encapsulate processing accelerators information in a top-level
// "accelerator" YAML/JSON map/object key
type acceleratorPrinter struct {
Info *Info `json:"accelerator"`
}

// YAMLString returns a string with the processing accelerators information formatted as YAML
// under a top-level "accelerator:" key
func (i *Info) YAMLString() string {
return marshal.SafeYAML(i.ctx, acceleratorPrinter{i})
}

// JSONString returns a string with the processing accelerators information formatted as JSON
// under a top-level "accelerator:" key
func (i *Info) JSONString(indent bool) string {
return marshal.SafeJSON(i.ctx, acceleratorPrinter{i}, indent)
}
Loading

0 comments on commit e050c5a

Please sign in to comment.