Skip to content

Commit 25c02a9

Browse files
committed
Add support for processing accelerators hardware
Signed-off-by: Manuel Lorenzo <mlorenzofr@redhat.com>
1 parent 80d3134 commit 25c02a9

File tree

14 files changed

+409
-26
lines changed

14 files changed

+409
-26
lines changed

README.md

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ hardware:
6262
* [`ghw.Network()`](#network)
6363
* [`ghw.PCI()`](#pci)
6464
* [`ghw.GPU()`](#gpu) (graphical processing unit)
65+
* [`ghw.Accelerator()`](#accelerator) (processing accelerators, AI)
6566
* [`ghw.Chassis()`](#chassis)
6667
* [`ghw.BIOS()`](#bios)
6768
* [`ghw.Baseboard()`](#baseboard)
@@ -893,7 +894,7 @@ information about the host computer's graphics hardware.
893894
The `ghw.GPUInfo` struct contains one field:
894895

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

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

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

949+
### Accelerator
950+
951+
The `ghw.Accelerator()` function returns a `ghw.AcceleratorInfo` struct that contains
952+
information about the host computer's processing accelerator hardware. In this category
953+
we can find used hardware for AI. The hardware detected in this category will be
954+
processing accelerators (PCI class `1200`), 3D controllers (`0302`) and Display
955+
controllers (`0380`).
956+
957+
The `ghw.AcceleratorInfo` struct contains one field:
958+
959+
* `ghw.AcceleratorInfo.Devices` is an array of pointers to `ghw.AcceleratorDevice`
960+
structs, one for each processing accelerator card found for the system.
961+
962+
Each `ghw.AcceleratorDevice` struct contains the following fields:
963+
964+
* `ghw.AcceleratorDevice.Address` is the PCI address for the processing accelerator card.
965+
* `ghw.AcceleratorDevice.PCIDevice` is a pointer to a `ghw.PCIDevice` struct.
966+
describing the processing accelerator card. This may be `nil` if no PCI device
967+
information could be determined for the card.
968+
969+
```go
970+
package main
971+
972+
import (
973+
"fmt"
974+
975+
"github.com/jaypipes/ghw"
976+
)
977+
978+
func main() {
979+
accel, err := ghw.Accelerator()
980+
if err != nil {
981+
fmt.Printf("Error getting processing accelerator info: %v", err)
982+
}
983+
984+
fmt.Printf("%v\n", accel)
985+
986+
for _, card := range accel.Devices {
987+
fmt.Printf(" %v\n", device)
988+
}
989+
}
990+
```
991+
992+
Example output from a testing machine:
993+
994+
```
995+
processing accelerators (1 device)
996+
device @0000:00:04.0 -> driver: 'fake_pci_driver' class: 'Processing accelerators' vendor: 'Red Hat, Inc.' product: 'QEMU PCI Test Device'
997+
```
998+
999+
**NOTE**: You can [read more](#pci) about the fields of the `ghw.PCIDevice`
1000+
struct if you'd like to dig deeper into PCI subsystem and programming interface
1001+
information
1002+
9481003
### Chassis
9491004

9501005
The `ghw.Chassis()` function returns a `ghw.ChassisInfo` struct that contains

alias.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
package ghw
88

99
import (
10+
"github.com/jaypipes/ghw/pkg/accelerator"
1011
"github.com/jaypipes/ghw/pkg/baseboard"
1112
"github.com/jaypipes/ghw/pkg/bios"
1213
"github.com/jaypipes/ghw/pkg/block"
@@ -183,3 +184,10 @@ type GraphicsCard = gpu.GraphicsCard
183184
var (
184185
GPU = gpu.New
185186
)
187+
188+
type AcceleratorInfo = accelerator.Info
189+
type AcceleratorDevice = accelerator.AcceleratorDevice
190+
191+
var (
192+
Accelerator = accelerator.New
193+
)

cmd/ghwc/commands/accelerator.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//
2+
// Use and distribution licensed under the Apache license version 2.
3+
//
4+
// See the COPYING file in the root project directory for full text.
5+
//
6+
7+
package commands
8+
9+
import (
10+
"fmt"
11+
12+
"github.com/jaypipes/ghw"
13+
"github.com/pkg/errors"
14+
"github.com/spf13/cobra"
15+
)
16+
17+
// acceleratorCmd represents the install command
18+
var acceleratorCmd = &cobra.Command{
19+
Use: "accelerator",
20+
Short: "Show processing accelerators information for the host system",
21+
RunE: showGPU,
22+
}
23+
24+
// showAccelerator show processing accelerators information for the host system.
25+
func showAccelerator(cmd *cobra.Command, args []string) error {
26+
accel, err := ghw.Accelerator()
27+
if err != nil {
28+
return errors.Wrap(err, "error getting Accelerator info")
29+
}
30+
31+
switch outputFormat {
32+
case outputFormatHuman:
33+
fmt.Printf("%v\n", accel)
34+
35+
for _, card := range accel.Devices {
36+
fmt.Printf(" %v\n", card)
37+
}
38+
case outputFormatJSON:
39+
fmt.Printf("%s\n", accel.JSONString(pretty))
40+
case outputFormatYAML:
41+
fmt.Printf("%s", accel.YAMLString())
42+
}
43+
return nil
44+
}
45+
46+
func init() {
47+
rootCmd.AddCommand(acceleratorCmd)
48+
}

cmd/ghwc/commands/root.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ func showAll(cmd *cobra.Command, args []string) error {
9090
if err := showProduct(cmd, args); err != nil {
9191
return err
9292
}
93+
if err := showAccelerator(cmd, args); err != nil {
94+
return err
95+
}
9396
case outputFormatJSON:
9497
host, err := ghw.Host()
9598
if err != nil {

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ require (
1616
github.com/inconshreveable/mousetrap v1.1.0 // indirect
1717
github.com/kr/pretty v0.1.0 // indirect
1818
github.com/mitchellh/go-homedir v1.1.0 // indirect
19+
github.com/samber/lo v1.47.0 // indirect
1920
github.com/spf13/pflag v1.0.5 // indirect
2021
golang.org/x/sys v0.1.0 // indirect
22+
golang.org/x/text v0.16.0 // indirect
2123
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
2224
)

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,17 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
1919
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
2020
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
2121
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
22+
github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc=
23+
github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
2224
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
2325
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
2426
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
2527
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
2628
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
2729
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
2830
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
31+
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
32+
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
2933
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
3034
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
3135
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

host.go

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

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

14+
"github.com/jaypipes/ghw/pkg/accelerator"
1415
"github.com/jaypipes/ghw/pkg/baseboard"
1516
"github.com/jaypipes/ghw/pkg/bios"
1617
"github.com/jaypipes/ghw/pkg/block"
@@ -28,18 +29,19 @@ import (
2829
// HostInfo is a wrapper struct containing information about the host system's
2930
// memory, block storage, CPU, etc
3031
type HostInfo struct {
31-
ctx *context.Context
32-
Memory *memory.Info `json:"memory"`
33-
Block *block.Info `json:"block"`
34-
CPU *cpu.Info `json:"cpu"`
35-
Topology *topology.Info `json:"topology"`
36-
Network *net.Info `json:"network"`
37-
GPU *gpu.Info `json:"gpu"`
38-
Chassis *chassis.Info `json:"chassis"`
39-
BIOS *bios.Info `json:"bios"`
40-
Baseboard *baseboard.Info `json:"baseboard"`
41-
Product *product.Info `json:"product"`
42-
PCI *pci.Info `json:"pci"`
32+
ctx *context.Context
33+
Memory *memory.Info `json:"memory"`
34+
Block *block.Info `json:"block"`
35+
CPU *cpu.Info `json:"cpu"`
36+
Topology *topology.Info `json:"topology"`
37+
Network *net.Info `json:"network"`
38+
GPU *gpu.Info `json:"gpu"`
39+
Accelerator *accelerator.Info `json:"accelerator"`
40+
Chassis *chassis.Info `json:"chassis"`
41+
BIOS *bios.Info `json:"bios"`
42+
Baseboard *baseboard.Info `json:"baseboard"`
43+
Product *product.Info `json:"product"`
44+
PCI *pci.Info `json:"pci"`
4345
}
4446

4547
// Host returns a pointer to a HostInfo struct that contains fields with
@@ -71,6 +73,10 @@ func Host(opts ...*WithOption) (*HostInfo, error) {
7173
if err != nil {
7274
return nil, err
7375
}
76+
acceleratorInfo, err := accelerator.New(opts...)
77+
if err != nil {
78+
return nil, err
79+
}
7480
chassisInfo, err := chassis.New(opts...)
7581
if err != nil {
7682
return nil, err
@@ -92,29 +98,31 @@ func Host(opts ...*WithOption) (*HostInfo, error) {
9298
return nil, err
9399
}
94100
return &HostInfo{
95-
ctx: ctx,
96-
CPU: cpuInfo,
97-
Memory: memInfo,
98-
Block: blockInfo,
99-
Topology: topologyInfo,
100-
Network: netInfo,
101-
GPU: gpuInfo,
102-
Chassis: chassisInfo,
103-
BIOS: biosInfo,
104-
Baseboard: baseboardInfo,
105-
Product: productInfo,
106-
PCI: pciInfo,
101+
ctx: ctx,
102+
CPU: cpuInfo,
103+
Memory: memInfo,
104+
Block: blockInfo,
105+
Topology: topologyInfo,
106+
Network: netInfo,
107+
GPU: gpuInfo,
108+
Accelerator: acceleratorInfo,
109+
Chassis: chassisInfo,
110+
BIOS: biosInfo,
111+
Baseboard: baseboardInfo,
112+
Product: productInfo,
113+
PCI: pciInfo,
107114
}, nil
108115
}
109116

110117
// String returns a newline-separated output of the HostInfo's component
111118
// structs' String-ified output
112119
func (info *HostInfo) String() string {
113120
return fmt.Sprintf(
114-
"%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
121+
"%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
115122
info.Block.String(),
116123
info.CPU.String(),
117124
info.GPU.String(),
125+
info.Accelerator.String(),
118126
info.Memory.String(),
119127
info.Network.String(),
120128
info.Topology.String(),

host_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,11 @@ func TestHost(t *testing.T) {
7979
if gpu == nil {
8080
t.Fatalf("Expected non-nil GPU but got nil.")
8181
}
82+
83+
// Processing accelerator cards are not common nowadays.
84+
// You may not have one in your machine, so this check displays a message but does not interrupt the test.
85+
accel := host.Accelerator
86+
if accel == nil {
87+
t.Logf("WARNING: Processing accelerator cards not detected.")
88+
}
8289
}

pkg/accelerator/accelerator.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
//
2+
// Use and distribution licensed under the Apache license version 2.
3+
//
4+
// See the COPYING file in the root project directory for full text.
5+
//
6+
7+
package accelerator
8+
9+
import (
10+
"fmt"
11+
12+
"github.com/jaypipes/ghw/pkg/context"
13+
"github.com/jaypipes/ghw/pkg/marshal"
14+
"github.com/jaypipes/ghw/pkg/option"
15+
"github.com/jaypipes/ghw/pkg/pci"
16+
)
17+
18+
type AcceleratorDevice struct {
19+
// the PCI address where the accelerator device can be found
20+
Address string `json:"address"`
21+
// pointer to a PCIDevice struct that describes the vendor and product
22+
// model, etc
23+
PCIDevice *pci.Device `json:"pci_device"`
24+
}
25+
26+
func (dev *AcceleratorDevice) String() string {
27+
deviceStr := dev.Address
28+
if dev.PCIDevice != nil {
29+
deviceStr = dev.PCIDevice.String()
30+
}
31+
nodeStr := ""
32+
return fmt.Sprintf(
33+
"device %s@%s",
34+
nodeStr,
35+
deviceStr,
36+
)
37+
}
38+
39+
type Info struct {
40+
ctx *context.Context
41+
Devices []*AcceleratorDevice `json:"devices"`
42+
}
43+
44+
// New returns a pointer to an Info struct that contains information about the
45+
// accelerator devices on the host system
46+
func New(opts ...*option.Option) (*Info, error) {
47+
ctx := context.New(opts...)
48+
info := &Info{ctx: ctx}
49+
50+
if err := ctx.Do(info.load); err != nil {
51+
return nil, err
52+
}
53+
return info, nil
54+
}
55+
56+
func (i *Info) String() string {
57+
numDevsStr := "devices"
58+
if len(i.Devices) == 1 {
59+
numDevsStr = "device"
60+
}
61+
return fmt.Sprintf(
62+
"processing accelerators (%d %s)",
63+
len(i.Devices),
64+
numDevsStr,
65+
)
66+
}
67+
68+
// simple private struct used to encapsulate processing accelerators information in a top-level
69+
// "accelerator" YAML/JSON map/object key
70+
type acceleratorPrinter struct {
71+
Info *Info `json:"accelerator"`
72+
}
73+
74+
// YAMLString returns a string with the processing accelerators information formatted as YAML
75+
// under a top-level "accelerator:" key
76+
func (i *Info) YAMLString() string {
77+
return marshal.SafeYAML(i.ctx, acceleratorPrinter{i})
78+
}
79+
80+
// JSONString returns a string with the processing accelerators information formatted as JSON
81+
// under a top-level "accelerator:" key
82+
func (i *Info) JSONString(indent bool) string {
83+
return marshal.SafeJSON(i.ctx, acceleratorPrinter{i}, indent)
84+
}

0 commit comments

Comments
 (0)