Skip to content

Commit

Permalink
snapshot: collect data related to GPU devices
Browse files Browse the repository at this point in the history
make sure the snapshot code collects data related to GPU devices.
Turns out not much was missing, most notably the `/sys/class/drm` link;
nevertheless, let's make sure to collect this data, so now also the
gpu package can consume snapshots.

We choose to refactor the code from net devices collection more
for clarity -what we collect and what we filter out is a bit more
explicit now- than to avoid duplication.

The PCI collection code is a bit more elaborate, because it needs to
deal with PCI roots and subtrees (subforests?), so it was intentionally
left out for the time being.

Signed-off-by: Francesco Romani <fromani@redhat.com>
  • Loading branch information
ffromani committed May 12, 2021
1 parent 56ca185 commit bf547f1
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 38 deletions.
59 changes: 59 additions & 0 deletions pkg/snapshot/clonetree.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ func ExpectedCloneContent() []string {
fileSpecs := ExpectedCloneStaticContent()
fileSpecs = append(fileSpecs, ExpectedCloneNetContent()...)
fileSpecs = append(fileSpecs, ExpectedClonePCIContent()...)
fileSpecs = append(fileSpecs, ExpectedCloneGPUContent()...)
return fileSpecs
}

Expand Down Expand Up @@ -202,3 +203,61 @@ func copyLink(path, targetPath string) error {

return nil
}

type filterFunc func(string) bool

// cloneContentByClass copies all the content related to a given device class
// (devClass), possibly filtering out devices whose name does NOT pass a
// filter (filterName). Each entry in `/sys/class/$CLASS` is actually a
// symbolic link. We can filter out entries depending on the link target.
// Each filter is a simple function which takes the entry name or the link
// target and must return true if the entry should be collected, false
// otherwise. Last, explicitely collect a list of attributes for each entry,
// given as list of glob patterns as `subEntries`.
// Return the final list of glob patterns to be collected.
func cloneContentByClass(devClass string, subEntries []string, filterName filterFunc, filterLink filterFunc) []string {
var fileSpecs []string

// warning: don't use the context package here, this means not even the linuxpath package.
// TODO(fromani) remove the path duplication
sysClass := filepath.Join("sys", "class", devClass)
entries, err := ioutil.ReadDir(sysClass)
if err != nil {
// we should not import context, hence we can't Warn()
return fileSpecs
}
for _, entry := range entries {
devName := entry.Name()

if !filterName(devName) {
continue
}

devPath := filepath.Join(sysClass, devName)
dest, err := os.Readlink(devPath)
if err != nil {
continue
}

if !filterLink(dest) {
continue
}

// so, first copy the symlink itself
fileSpecs = append(fileSpecs, devPath)
// now we have to clone the content of the actual entry
// related (and found into a subdir of) the backing hardware
// device
devData := filepath.Clean(filepath.Join(sysClass, dest))
for _, subEntry := range subEntries {
fileSpecs = append(fileSpecs, filepath.Join(devData, subEntry))
}
}

return fileSpecs
}

// filterNone allows all content, filtering out none of it
func filterNone(_ string) bool {
return true
}
33 changes: 33 additions & 0 deletions pkg/snapshot/clonetree_gpu.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// Use and distribution licensed under the Apache license version 2.
//
// See the COPYING file in the root project directory for full text.
//

package snapshot

import (
"strings"
)

// ExpectedCloneGPUContent returns a slice of strings pertaining to the GPU devices ghw
// cares about. We cannot use a static list because we want to grab only the first cardX data
// (see comment in pkg/gpu/gpu_linux.go)
// Additionally, we want to make sure to clone the backing device data.
func ExpectedCloneGPUContent() []string {
cardEntries := []string{
"device",
}

filterName := func(cardName string) bool {
if !strings.HasPrefix(cardName, "card") {
return false
}
if strings.ContainsRune(cardName, '-') {
return false
}
return true
}

return cloneContentByClass("drm", cardEntries, filterName, filterNone)
}
43 changes: 5 additions & 38 deletions pkg/snapshot/clonetree_net.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,58 +7,25 @@
package snapshot

import (
"io/ioutil"
"os"
"path/filepath"
"strings"
)

const (
// warning: don't use the context package here, this means not even the linuxpath package.
// TODO(fromani) remove the path duplication
sysClassNet = "/sys/class/net"
)

// ExpectedCloneNetContent returns a slice of strings pertaning to the network interfaces ghw
// cares about. We cannot use a static list because we want to filter away the virtual devices,
// which ghw doesn't concern itself about. So we need to do some runtime discovery.
// Additionally, we want to make sure to clone the backing device data.
func ExpectedCloneNetContent() []string {
var fileSpecs []string
ifaceEntries := []string{
"addr_assign_type",
// intentionally avoid to clone "address" to avoid to leak any host-idenfifiable data.
}
entries, err := ioutil.ReadDir(sysClassNet)
if err != nil {
// we should not import context, hence we can't Warn()
return fileSpecs
}
for _, entry := range entries {
netName := entry.Name()
netPath := filepath.Join(sysClassNet, netName)
dest, err := os.Readlink(netPath)
if err != nil {
continue
}
if strings.Contains(dest, "devices/virtual/net") {
// there is no point in cloning data for virtual devices,
// because ghw concerns itself with HardWare.
continue
}

// so, first copy the symlink itself
fileSpecs = append(fileSpecs, netPath)

// now we have to clone the content of the actual network interface
// data related (and found into a subdir of) the backing hardware
// device
netIface := filepath.Clean(filepath.Join(sysClassNet, dest))
for _, ifaceEntry := range ifaceEntries {
fileSpecs = append(fileSpecs, filepath.Join(netIface, ifaceEntry))
filterLink := func(linkDest string) bool {
if strings.Contains(linkDest, "devices/virtual/net") {
return false
}

return true
}

return fileSpecs
return cloneContentByClass("net", ifaceEntries, filterNone, filterLink)
}

0 comments on commit bf547f1

Please sign in to comment.