mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
Add an efaproxy driver plug-in for AWS Elastic Fabric Adapter (EFA) devices
bound to the host `efa` driver. EFA's driver-private uverbs ABI carries no
userspace buffer pointers on CQ/QP CREATE: queue memory is kernel-allocated
and handed back via mmap keys in the CREATE response, which the core already
forwards to the host FD verbatim. PrepareCreateDMA is therefore a no-op; the
plug-in exists to register the "efa" driver name and to model EFA's MR_QUERY
driver-namespace method (used by libfabric to fetch RDMA read/write
interconnect IDs).
Model the standard AH object's destroy method (UVERBS_OBJECT_AH): EFA (SRD)
creates address handles via the legacy write path and destroys them through
the modern object, which the core must allow.
Extend the virtual RDMA sysfs for libfabric/hwloc topology discovery:
- device/driver symlink and /sys/bus/pci/drivers/<driver> tree, which
libfabric's EFA provider resolves during device discovery.
- Mirror each PCI node's raw config space ("config" file). hwloc (used by
aws-ofi-nccl to build the NCCL topology) reads it to recover the PCI
bridge bus-number registers; without it hwloc cannot reconstruct the
bridge hierarchy and aws-ofi-nccl aborts its topology write.
- Serve the per-port lid_mask_count attribute alongside the other port
attributes libibverbs reads from sysfs; ib_core creates it on every
port.
The sysfs integration tests cover the new driver tree and config mirroring.
Advertise a >= 5.12 kernel release in RDMA-enabled sandboxes so aws-ofi-nccl
enables dmabuf-based GPU memory registration (ibv_reg_dmabuf_mr), which the
proxy handles via the existing REG_DMABUF_MR path. Sandboxes without RDMA
keep the current 4.19 release to limit the bump's blast radius; a later
change can make the newer release the default.
Co-authored-by: Alessio Ricci Toniolo <[email protected]>
514 lines
18 KiB
Go
514 lines
18 KiB
Go
// Copyright 2019 The gVisor Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package sys_test
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"gvisor.dev/gvisor/pkg/abi/linux"
|
|
"gvisor.dev/gvisor/pkg/rdma"
|
|
"gvisor.dev/gvisor/pkg/sentry/fsimpl/sys"
|
|
"gvisor.dev/gvisor/pkg/sentry/fsimpl/testutil"
|
|
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
|
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
|
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
|
)
|
|
|
|
const (
|
|
vfioDev = "vfio-dev"
|
|
)
|
|
|
|
func tryNewTestSystem(t *testing.T, idata *sys.InternalData) (*testutil.System, error) {
|
|
k, err := testutil.Boot()
|
|
if err != nil {
|
|
t.Fatalf("Failed to create test kernel: %v", err)
|
|
}
|
|
ctx := k.SupervisorContext()
|
|
creds := auth.CredentialsFromContext(ctx)
|
|
k.VFS().MustRegisterFilesystemType(sys.Name, sys.FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{
|
|
AllowUserMount: true,
|
|
})
|
|
|
|
mountOpts := &vfs.MountOptions{
|
|
GetFilesystemOptions: vfs.GetFilesystemOptions{
|
|
InternalData: idata,
|
|
},
|
|
}
|
|
|
|
mns, err := k.VFS().NewMountNamespace(ctx, creds, "", sys.Name, mountOpts, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return testutil.NewSystem(ctx, t, k.VFS(), mns), nil
|
|
}
|
|
|
|
func newTestSystemWithInternalData(t *testing.T, idata *sys.InternalData) *testutil.System {
|
|
s, err := tryNewTestSystem(t, idata)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create new mount namespace: %v", err)
|
|
}
|
|
return s
|
|
}
|
|
|
|
func newTestSystem(t *testing.T, pciTestDir string) *testutil.System {
|
|
return newTestSystemWithInternalData(t, &sys.InternalData{
|
|
EnableTPUProxyPaths: pciTestDir != "",
|
|
TestSysfsPathPrefix: pciTestDir,
|
|
})
|
|
}
|
|
|
|
func TestReadCPUFile(t *testing.T) {
|
|
s := newTestSystem(t, "" /*pciTestDir*/)
|
|
defer s.Destroy()
|
|
k := kernel.KernelFromContext(s.Ctx)
|
|
maxCPUCores := k.ApplicationCores()
|
|
|
|
expected := fmt.Sprintf("0-%d\n", maxCPUCores-1)
|
|
|
|
for _, fname := range []string{"online", "possible", "present"} {
|
|
pop := s.PathOpAtRoot(fmt.Sprintf("devices/system/cpu/%s", fname))
|
|
fd, err := s.VFS.OpenAt(s.Ctx, s.Creds, pop, &vfs.OpenOptions{})
|
|
if err != nil {
|
|
t.Fatalf("OpenAt(pop:%+v) = %+v failed: %v", pop, fd, err)
|
|
}
|
|
defer fd.DecRef(s.Ctx)
|
|
content, err := s.ReadToEnd(fd)
|
|
if err != nil {
|
|
t.Fatalf("Read failed: %v", err)
|
|
}
|
|
if diff := cmp.Diff(expected, content); diff != "" {
|
|
t.Fatalf("Read returned unexpected data:\n--- want\n+++ got\n%v", diff)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSysRootContainsExpectedEntries(t *testing.T) {
|
|
s := newTestSystem(t, "" /*pciTestDir*/)
|
|
defer s.Destroy()
|
|
pop := s.PathOpAtRoot("/")
|
|
s.AssertAllDirentTypes(s.ListDirents(pop), map[string]testutil.DirentType{
|
|
"block": linux.DT_DIR,
|
|
"bus": linux.DT_DIR,
|
|
"class": linux.DT_DIR,
|
|
"dev": linux.DT_DIR,
|
|
"devices": linux.DT_DIR,
|
|
"firmware": linux.DT_DIR,
|
|
"fs": linux.DT_DIR,
|
|
"kernel": linux.DT_DIR,
|
|
"module": linux.DT_DIR,
|
|
"power": linux.DT_DIR,
|
|
})
|
|
}
|
|
|
|
func TestSysDevContainsBlockAndChar(t *testing.T) {
|
|
s := newTestSystem(t, "" /*pciTestDir*/)
|
|
defer s.Destroy()
|
|
pop := s.PathOpAtRoot("/dev")
|
|
s.AssertAllDirentTypes(s.ListDirents(pop), map[string]testutil.DirentType{
|
|
"block": linux.DT_DIR,
|
|
"char": linux.DT_DIR,
|
|
})
|
|
}
|
|
|
|
func TestCgroupMountpointExists(t *testing.T) {
|
|
// Note: The mountpoint is only created if cgroups are available.
|
|
s := newTestSystem(t, "" /*pciTestDir*/)
|
|
defer s.Destroy()
|
|
pop := s.PathOpAtRoot("/fs")
|
|
s.AssertAllDirentTypes(s.ListDirents(pop), map[string]testutil.DirentType{
|
|
"cgroup": linux.DT_DIR,
|
|
})
|
|
pop = s.PathOpAtRoot("/fs/cgroup")
|
|
s.AssertAllDirentTypes(s.ListDirents(pop), map[string]testutil.DirentType{ /*empty*/ })
|
|
}
|
|
|
|
// Check that sysfs creates the required PCI paths for V4 TPUs.
|
|
func TestEnableTPUProxyPathsV4(t *testing.T) {
|
|
// Set up the fs tree that will be mirrored in the sentry.
|
|
sysfsTestDir := t.TempDir()
|
|
busPath := path.Join(sysfsTestDir, "sys", "bus", "pci", "devices")
|
|
if err := os.MkdirAll(busPath, 0755); err != nil {
|
|
t.Fatalf("Failed to create bus directory: %v", err)
|
|
}
|
|
classAccelPath := path.Join(sysfsTestDir, "sys", "class", "accel")
|
|
if err := os.MkdirAll(classAccelPath, 0755); err != nil {
|
|
t.Fatalf("Failed to create accel directory: %v", err)
|
|
}
|
|
for i, pciAddress := range []string{"0000:00:04.0", "0000:00:05.0"} {
|
|
accelDev := fmt.Sprintf("accel%d", i)
|
|
accelPath := path.Join(sysfsTestDir, "sys", "devices", "pci0000:00", pciAddress, "accel", accelDev)
|
|
if err := os.MkdirAll(accelPath, 0755); err != nil {
|
|
t.Fatalf("Failed to create accel directory: %v", err)
|
|
}
|
|
if err := os.Symlink(path.Join("..", "..", "..", pciAddress), path.Join(accelPath, pciAddress)); err != nil {
|
|
t.Fatalf("Failed to symlink accel directory: %v", err)
|
|
}
|
|
if err := os.Symlink(path.Join("..", "..", "..", pciAddress), path.Join(accelPath, "device")); err != nil {
|
|
t.Fatalf("Failed to symlink accel device directory: %v", err)
|
|
}
|
|
if _, err := os.Create(path.Join(accelPath, "chip_model")); err != nil {
|
|
t.Fatalf("Failed to create chip_model: %v", err)
|
|
}
|
|
if _, err := os.Create(path.Join(accelPath, "device_owner")); err != nil {
|
|
t.Fatalf("Failed to create device_owner: %v", err)
|
|
}
|
|
if _, err := os.Create(path.Join(accelPath, "pci_address")); err != nil {
|
|
t.Fatalf("Failed to create pci_address: %v", err)
|
|
}
|
|
if err := os.Symlink(path.Join("..", "..", "..", "devices", "pci0000:00", pciAddress), path.Join(busPath, pciAddress)); err != nil {
|
|
t.Fatalf("Failed to symlink bus directory: %v", err)
|
|
}
|
|
if err := os.Symlink(path.Join("..", "..", "devices", "pci0000:00", pciAddress, "accel", accelDev), path.Join(classAccelPath, accelDev)); err != nil {
|
|
t.Fatalf("Failed to symlink accel directory: %v", err)
|
|
}
|
|
}
|
|
|
|
s := newTestSystem(t, sysfsTestDir)
|
|
defer s.Destroy()
|
|
|
|
pop := s.PathOpAtRoot("/devices/pci0000:00")
|
|
s.AssertAllDirentTypes(s.ListDirents(pop), map[string]testutil.DirentType{
|
|
"0000:00:04.0": linux.DT_DIR,
|
|
"0000:00:05.0": linux.DT_DIR,
|
|
})
|
|
pop = s.PathOpAtRoot("/devices/pci0000:00/0000:00:04.0/accel/accel0")
|
|
s.AssertAllDirentTypes(s.ListDirents(pop), map[string]testutil.DirentType{
|
|
"0000:00:04.0": linux.DT_LNK,
|
|
"device": linux.DT_LNK,
|
|
"chip_model": linux.DT_REG,
|
|
"device_owner": linux.DT_REG,
|
|
"pci_address": linux.DT_REG,
|
|
})
|
|
pop = s.PathOpAtRoot("/bus/pci/devices")
|
|
s.AssertAllDirentTypes(s.ListDirents(pop), map[string]testutil.DirentType{
|
|
"0000:00:04.0": linux.DT_LNK,
|
|
"0000:00:05.0": linux.DT_LNK,
|
|
})
|
|
pop = s.PathOpAtRoot("/class/accel")
|
|
s.AssertAllDirentTypes(s.ListDirents(pop), map[string]testutil.DirentType{
|
|
"accel0": linux.DT_LNK,
|
|
"accel1": linux.DT_LNK,
|
|
})
|
|
}
|
|
|
|
type PCIDeviceInfo struct {
|
|
// IOMMU group.
|
|
group string
|
|
pciPath string
|
|
pciAddress string
|
|
name string
|
|
nestedDeviceIndex int
|
|
}
|
|
|
|
func (dev PCIDeviceInfo) path() string {
|
|
return path.Join(dev.pciPath, dev.pciAddress, vfioDev, dev.name)
|
|
}
|
|
|
|
func TestEnableTPUProxyPathsV5(t *testing.T) {
|
|
// Set up the fs tree that will be mirrored in the sentry.
|
|
sysfsTestDir := t.TempDir()
|
|
pciPath0 := path.Join(sysfsTestDir, "sys", "devices", "pci0000:00")
|
|
if err := os.MkdirAll(pciPath0, 0755); err != nil {
|
|
t.Fatalf("Failed to create PCI directory: %v", err)
|
|
}
|
|
pciPath1 := path.Join(sysfsTestDir, "sys", "devices", "pci0000:10")
|
|
if err := os.MkdirAll(pciPath1, 0755); err != nil {
|
|
t.Fatalf("Failed to create PCI directory: %v", err)
|
|
}
|
|
busPath := path.Join(sysfsTestDir, "sys", "bus", "pci", "devices")
|
|
if err := os.MkdirAll(busPath, 0755); err != nil {
|
|
t.Fatalf("Failed to create bus directory: %v", err)
|
|
}
|
|
sysClassPath := path.Join(sysfsTestDir, "sys", "class", vfioDev)
|
|
if err := os.MkdirAll(sysClassPath, 0755); err != nil {
|
|
t.Fatalf("Failed to create class directory: %v", err)
|
|
}
|
|
|
|
devices := []PCIDeviceInfo{
|
|
{
|
|
group: "0",
|
|
pciPath: pciPath0,
|
|
pciAddress: "0000:00:04.0",
|
|
name: "vfio0",
|
|
nestedDeviceIndex: -1,
|
|
},
|
|
{
|
|
group: "1",
|
|
pciPath: pciPath0,
|
|
pciAddress: "0000:00:05.0",
|
|
name: "vfio1",
|
|
nestedDeviceIndex: -1,
|
|
},
|
|
{
|
|
group: "2",
|
|
pciPath: pciPath1,
|
|
pciAddress: "0000:10:05.0",
|
|
name: "vfio2",
|
|
nestedDeviceIndex: 3,
|
|
},
|
|
{
|
|
group: "3",
|
|
pciPath: pciPath1,
|
|
pciAddress: "0000:10:05.0/0000:03:00.1",
|
|
name: "vfio3",
|
|
nestedDeviceIndex: -1,
|
|
},
|
|
}
|
|
for _, device := range devices {
|
|
devicePath := device.path()
|
|
if err := os.MkdirAll(devicePath, 0755); err != nil {
|
|
t.Fatalf("Failed to create PCI device directory: %v", err)
|
|
}
|
|
if err := os.Symlink(path.Join("..", "..", "..", device.pciAddress), path.Join(devicePath, "device")); err != nil {
|
|
t.Fatalf("Failed to symlink device directory: %v", err)
|
|
}
|
|
if err := os.Symlink(path.Join("..", "..", "..", "devices", path.Base(device.pciPath), device.pciAddress), path.Join(busPath, path.Base(device.pciAddress))); err != nil {
|
|
t.Fatalf("Failed to symlink bus directory: %v", err)
|
|
}
|
|
if err := os.Symlink(path.Join("..", "..", "devices", path.Base(device.pciPath), device.pciAddress, vfioDev, device.name), path.Join(sysClassPath, device.name)); err != nil {
|
|
t.Fatalf("Failed to symlink class directory: %v", err)
|
|
}
|
|
iommuPath := path.Join(sysfsTestDir, "sys", "kernel", "iommu_groups", device.group, "devices")
|
|
if err := os.MkdirAll(iommuPath, 0755); err != nil {
|
|
t.Fatalf("Failed to create iommu_groups directory: %v", err)
|
|
}
|
|
if err := os.Symlink(path.Join("..", "..", "..", "..", "devices", path.Base(device.pciPath), device.pciAddress), path.Join(iommuPath, path.Base(device.pciAddress))); err != nil {
|
|
t.Fatalf("Failed to symlink iommu_group devices directory: %v", err)
|
|
}
|
|
if err := os.Symlink(path.Join("..", "..", "..", "kernel", "iommu_groups", device.group), path.Join(device.pciPath, device.pciAddress, "iommu_group")); err != nil {
|
|
t.Fatalf("Failed to symlink iommu_groups directory: %v", err)
|
|
}
|
|
}
|
|
s := newTestSystem(t, sysfsTestDir)
|
|
defer s.Destroy()
|
|
|
|
for _, device := range devices {
|
|
// Validate PCI device symlinks.
|
|
pop := s.PathOpAtRoot(path.Join("devices", path.Base(device.pciPath), device.pciAddress))
|
|
contents := map[string]testutil.DirentType{
|
|
"iommu_group": linux.DT_LNK,
|
|
vfioDev: linux.DT_DIR,
|
|
}
|
|
if device.nestedDeviceIndex != -1 {
|
|
deviceName := path.Base(devices[device.nestedDeviceIndex].pciAddress)
|
|
contents[deviceName] = linux.DT_DIR
|
|
}
|
|
s.AssertAllDirentTypes(s.ListDirents(pop), contents)
|
|
// Validate VFIO device symlinks.
|
|
pop = s.PathOpAtRoot(path.Join("devices", path.Base(device.pciPath), device.pciAddress, vfioDev, device.name))
|
|
s.AssertAllDirentTypes(s.ListDirents(pop), map[string]testutil.DirentType{
|
|
"device": linux.DT_LNK,
|
|
})
|
|
// Validate $IOMMU_GROUP/devices.
|
|
pop = s.PathOpAtRoot(path.Join("kernel", "iommu_groups", string(device.group), "devices"))
|
|
s.AssertAllDirentTypes(s.ListDirents(pop), map[string]testutil.DirentType{
|
|
path.Base(device.pciAddress): linux.DT_LNK,
|
|
})
|
|
}
|
|
}
|
|
|
|
// rdmaTestPCIConfig is a fake raw PCI config space blob; deliberately not
|
|
// valid UTF-8 to check binary-faithful mirroring.
|
|
var rdmaTestPCIConfig = []byte{0x86, 0x80, 0x0d, 0x02, 0x00, 0x00, 0x10, 0x00}
|
|
|
|
// newRDMATestSnapshot returns a snapshot with a ConnectX NIC behind a bridge
|
|
// on domain 0000 and a GPU on an extended (VMD-style, 5-hex-digit) domain.
|
|
func newRDMATestSnapshot() *rdma.Snapshot {
|
|
const nicLeaf = "devices/pci0000:07/0000:07:01.0/0000:0c:00.0"
|
|
return &rdma.Snapshot{
|
|
VerbsABIVersion: "6\n",
|
|
PCINodes: []rdma.PCINode{
|
|
{Path: "devices/pci0000:07"},
|
|
{Path: "devices/pci0000:07/0000:07:01.0", Attrs: map[string]string{"class": "0x060400\n"}, Config: rdmaTestPCIConfig},
|
|
{Path: nicLeaf, Attrs: map[string]string{
|
|
"vendor": "0x15b3\n",
|
|
"uevent": "DRIVER=mlx5_core\nPCI_SLOT_NAME=0000:0c:00.0\n",
|
|
// Host-view CPU affinity: must be rewritten to the sandbox's
|
|
// single-node view.
|
|
"numa_node": "1\n",
|
|
"local_cpus": "ffffffff,ffffff00,00000000\n",
|
|
"local_cpulist": "56-111\n",
|
|
}},
|
|
{Path: "devices/pci10000:e0"},
|
|
{Path: "devices/pci10000:e0/10000:e0:06.0", Attrs: map[string]string{
|
|
"class": "0x030200\n",
|
|
"vendor": "0x10de\n",
|
|
// A device on no NUMA node stays that way.
|
|
"numa_node": "-1\n",
|
|
}},
|
|
},
|
|
Devices: []rdma.Device{{
|
|
Uverbs: "uverbs0",
|
|
IBDev: "mlx5_0",
|
|
LeafPCI: nicLeaf,
|
|
Dev: "231:192\n",
|
|
ABIVersion: "1\n",
|
|
IBAttrs: map[string]string{"node_guid": "0011:2233:4455:6677\n"},
|
|
Ports: map[string]rdma.Port{
|
|
"1": {
|
|
StaticAttrs: map[string]string{"link_layer": "Ethernet\n"},
|
|
LiveAttrs: []string{"state"},
|
|
GIDNames: []string{"0", "1"},
|
|
CounterNames: []string{"port_rcv_data"},
|
|
},
|
|
},
|
|
NetDevs: []rdma.NetDev{{Name: "eth1", Attrs: map[string]string{"mtu": "9000\n"}}},
|
|
}},
|
|
NUMA: &rdma.NUMA{
|
|
// Two-node host (aggregate ranges "0-1"): the sandbox must
|
|
// advertise a single node covering the sandbox's CPUs.
|
|
Aggregate: map[string]string{"online": "0-1\n", "possible": "0-1\n"},
|
|
},
|
|
}
|
|
}
|
|
|
|
func readTestFile(s *testutil.System, p string) (string, error) {
|
|
pop := s.PathOpAtRoot(p)
|
|
fd, err := s.VFS.OpenAt(s.Ctx, s.Creds, pop, &vfs.OpenOptions{})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer fd.DecRef(s.Ctx)
|
|
return s.ReadToEnd(fd)
|
|
}
|
|
|
|
func TestRDMASysfs(t *testing.T) {
|
|
s := newTestSystemWithInternalData(t, &sys.InternalData{RDMASysfs: newRDMATestSnapshot()})
|
|
defer s.Destroy()
|
|
|
|
const nicLeaf = "/devices/pci0000:07/0000:07:01.0/0000:0c:00.0"
|
|
for p, want := range map[string]map[string]testutil.DirentType{
|
|
"/devices/pci0000:07": {
|
|
"0000:07:01.0": linux.DT_DIR,
|
|
"pci_bus": linux.DT_DIR,
|
|
},
|
|
"/devices/pci0000:07/0000:07:01.0": {
|
|
"0000:0c:00.0": linux.DT_DIR,
|
|
"pci_bus": linux.DT_DIR,
|
|
"subsystem": linux.DT_LNK,
|
|
"class": linux.DT_REG,
|
|
"config": linux.DT_REG,
|
|
},
|
|
nicLeaf: {
|
|
"infiniband": linux.DT_DIR,
|
|
"infiniband_verbs": linux.DT_DIR,
|
|
"net": linux.DT_DIR,
|
|
"subsystem": linux.DT_LNK,
|
|
"driver": linux.DT_LNK,
|
|
"vendor": linux.DT_REG,
|
|
"uevent": linux.DT_REG,
|
|
"numa_node": linux.DT_REG,
|
|
"local_cpus": linux.DT_REG,
|
|
"local_cpulist": linux.DT_REG,
|
|
},
|
|
nicLeaf + "/infiniband/mlx5_0": {
|
|
"device": linux.DT_LNK,
|
|
"node_guid": linux.DT_REG,
|
|
"ports": linux.DT_DIR,
|
|
},
|
|
nicLeaf + "/infiniband/mlx5_0/ports/1": {
|
|
"link_layer": linux.DT_REG,
|
|
"state": linux.DT_REG,
|
|
"gids": linux.DT_DIR,
|
|
"gid_attrs": linux.DT_DIR,
|
|
"counters": linux.DT_DIR,
|
|
},
|
|
nicLeaf + "/infiniband/mlx5_0/ports/1/gids": {
|
|
"0": linux.DT_REG,
|
|
"1": linux.DT_REG,
|
|
},
|
|
nicLeaf + "/infiniband_verbs/uverbs0": {
|
|
"ibdev": linux.DT_REG,
|
|
"abi_version": linux.DT_REG,
|
|
"dev": linux.DT_REG,
|
|
"device": linux.DT_LNK,
|
|
},
|
|
nicLeaf + "/net/eth1": {
|
|
"mtu": linux.DT_REG,
|
|
"device": linux.DT_LNK,
|
|
},
|
|
// Extended-domain (VMD-style) GPU root.
|
|
"/devices/pci10000:e0": {
|
|
"10000:e0:06.0": linux.DT_DIR,
|
|
"pci_bus": linux.DT_DIR,
|
|
},
|
|
"/class/infiniband": {"mlx5_0": linux.DT_LNK},
|
|
"/class/infiniband_verbs": {"uverbs0": linux.DT_LNK, "abi_version": linux.DT_REG},
|
|
"/class/net": {"eth1": linux.DT_LNK},
|
|
"/class/pci_bus": {
|
|
"0000:07": linux.DT_LNK,
|
|
"0000:0c": linux.DT_LNK,
|
|
"10000:e0": linux.DT_LNK,
|
|
},
|
|
"/bus/pci/devices": {
|
|
"0000:07:01.0": linux.DT_LNK,
|
|
"0000:0c:00.0": linux.DT_LNK,
|
|
"10000:e0:06.0": linux.DT_LNK,
|
|
},
|
|
"/bus/pci/drivers": {"mlx5_core": linux.DT_DIR},
|
|
"/bus/pci/drivers/mlx5_core": {"0000:0c:00.0": linux.DT_LNK},
|
|
// The two host nodes collapse into a single sandbox node.
|
|
"/devices/system/node": {"online": linux.DT_REG, "possible": linux.DT_REG, "node0": linux.DT_DIR},
|
|
"/devices/system/node/node0": {
|
|
"cpumap": linux.DT_REG,
|
|
"cpulist": linux.DT_REG,
|
|
"distance": linux.DT_REG,
|
|
},
|
|
} {
|
|
pop := s.PathOpAtRoot(p)
|
|
s.AssertAllDirentTypes(s.ListDirents(pop), want)
|
|
}
|
|
|
|
// The advertised NUMA view is a single node covering the sandbox's CPUs.
|
|
wantCPUList := "0\n"
|
|
if cores := kernel.KernelFromContext(s.Ctx).ApplicationCores(); cores > 1 {
|
|
wantCPUList = fmt.Sprintf("0-%d\n", cores-1)
|
|
}
|
|
|
|
// Attribute contents, including via a /sys/class symlink (the path
|
|
// consumers actually resolve).
|
|
for p, want := range map[string]string{
|
|
"/class/infiniband/mlx5_0/node_guid": "0011:2233:4455:6677\n",
|
|
"/class/infiniband_verbs/abi_version": "6\n",
|
|
nicLeaf + "/infiniband_verbs/uverbs0/ibdev": "mlx5_0\n",
|
|
"/class/infiniband/mlx5_0/ports/1/../../device/vendor": "0x15b3\n",
|
|
"/devices/system/node/online": "0\n",
|
|
"/devices/system/node/node0/cpulist": wantCPUList,
|
|
"/devices/system/node/node0/distance": "10\n",
|
|
nicLeaf + "/numa_node": "0\n",
|
|
nicLeaf + "/local_cpulist": wantCPUList,
|
|
"/devices/pci10000:e0/10000:e0:06.0/numa_node": "-1\n",
|
|
// Raw PCI config space is mirrored byte-faithfully.
|
|
"/devices/pci0000:07/0000:07:01.0/config": string(rdmaTestPCIConfig),
|
|
// The device/driver symlink and the /sys/bus/pci/drivers back-symlink
|
|
// resolve to the same PCI function (libfabric realpath's the former
|
|
// during EFA discovery).
|
|
nicLeaf + "/driver/0000:0c:00.0/vendor": "0x15b3\n",
|
|
"/bus/pci/drivers/mlx5_core/0000:0c:00.0/vendor": "0x15b3\n",
|
|
} {
|
|
got, err := readTestFile(s, p)
|
|
if err != nil {
|
|
t.Fatalf("reading %q: %v", p, err)
|
|
}
|
|
if got != want {
|
|
t.Errorf("%q contains %q, want %q", p, got, want)
|
|
}
|
|
}
|
|
}
|