Skip to content

Commit

Permalink
Add support for processing accelerators hardware
Browse files Browse the repository at this point in the history
  • Loading branch information
mlorenzofr committed Oct 12, 2024
1 parent 80d3134 commit 768e17f
Show file tree
Hide file tree
Showing 10 changed files with 513 additions and 26 deletions.
69 changes: 68 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,72 @@ 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 `ghw.Accelerator()` function accepts a slice of filters, of type string, as parameter
in format `[<vendor>]:[<device>][:<class>]`, (same is the _lspci_ command). By default all
processing accelerators type cards will be detected (`::1200`).

Some filter examples:
* `::0302`. Select 3D controller cards.
* `10de::0302`. Select Nvidia (`10de`) 3D controller cards (`0302`).
* `1da3:1060:1200`. Select Habana Labs (`1da3`) Gaudi3 (`1060`) processing accelerator cards (`1200`).
* `1002::`. Select AMD ATI hardware.

The `ghw.AcceleratorInfo` struct contains one field:

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

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

* `ghw.AcceleratorCard.Address` is the PCI address for the processing accelerator card.
* `ghw.AcceleratorCard.Pci` 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() {
filter := make([]string, 0)
// example of a filter to detect 3D controllers
// filter = append(filter, "::0302")

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

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

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

Example output from a testing machine:

```
processing accelerators (1 card)
card @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

Check failure on line 188 in alias.go

View workflow job for this annotation

GitHub Actions / lint

type `acceleratorInfo` is unused (unused)
type AcceleratorCard = accelerator.AcceleratorCard

var (
Accelerator = accelerator.New
)
50 changes: 50 additions & 0 deletions cmd/ghwc/commands/accelerator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// 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 {
filter := make([]string, 0)

accel, err := ghw.Accelerator(filter)
if err != nil {
return errors.Wrap(err, "error getting Accelerator info")
}

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

for _, card := range accel.AcceleratorCards {
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
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([]string{}, 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
89 changes: 89 additions & 0 deletions pkg/accelerator/accelerator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//
// 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 AcceleratorCard struct {
// the PCI address where the accelerator card can be found
Address string `json:"address"`
// pointer to a PCIDevice struct that describes the vendor and product
// model, etc
Pci *pci.Device `json:"pci"`
}

func (card *AcceleratorCard) String() string {
deviceStr := card.Address
if card.Pci != nil {
deviceStr = card.Pci.String()
}
nodeStr := ""
return fmt.Sprintf(
"card %s@%s",
nodeStr,
deviceStr,
)
}

type Info struct {
ctx *context.Context
AcceleratorCards []*AcceleratorCard `json:"cards"`
DiscoveryFilters []string
}

// New returns a pointer to an Info struct that contains information about the
// accelerator cards on the host system
func New(filter []string, opts ...*option.Option) (*Info, error) {
ctx := context.New(opts...)

info := &Info{
ctx: ctx,
DiscoveryFilters: filter,
}

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

Check failure on line 55 in pkg/accelerator/accelerator.go

View workflow job for this annotation

GitHub Actions / windows-2019 (1.19)

info.load undefined (type *Info has no field or method load)

Check failure on line 55 in pkg/accelerator/accelerator.go

View workflow job for this annotation

GitHub Actions / windows-2022 (1.20)

info.load undefined (type *Info has no field or method load)

Check failure on line 55 in pkg/accelerator/accelerator.go

View workflow job for this annotation

GitHub Actions / windows-2022 (1.21)

info.load undefined (type *Info has no field or method load)
return nil, err
}
return info, nil
}

func (i *Info) String() string {
numCardsStr := "cards"
if len(i.AcceleratorCards) == 1 {
numCardsStr = "card"
}
return fmt.Sprintf(
"processing accelerators (%d %s)",
len(i.AcceleratorCards),
numCardsStr,
)
}

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

// YAMLString returns a string with the gpu 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 gpu 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 768e17f

Please sign in to comment.