gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/sentry/fsimpl/sys/sys_test.go (about)

     1  // Copyright 2019 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package sys_test
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"path"
    21  	"testing"
    22  
    23  	"github.com/google/go-cmp/cmp"
    24  	"gvisor.dev/gvisor/pkg/abi/linux"
    25  	"gvisor.dev/gvisor/pkg/sentry/fsimpl/sys"
    26  	"gvisor.dev/gvisor/pkg/sentry/fsimpl/testutil"
    27  	"gvisor.dev/gvisor/pkg/sentry/kernel"
    28  	"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
    29  	"gvisor.dev/gvisor/pkg/sentry/vfs"
    30  )
    31  
    32  const (
    33  	vfioDev = "vfio-dev"
    34  )
    35  
    36  func newTestSystem(t *testing.T, pciTestDir string) *testutil.System {
    37  	k, err := testutil.Boot()
    38  	if err != nil {
    39  		t.Fatalf("Failed to create test kernel: %v", err)
    40  	}
    41  	ctx := k.SupervisorContext()
    42  	creds := auth.CredentialsFromContext(ctx)
    43  	k.VFS().MustRegisterFilesystemType(sys.Name, sys.FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{
    44  		AllowUserMount: true,
    45  	})
    46  
    47  	mountOpts := &vfs.MountOptions{
    48  		GetFilesystemOptions: vfs.GetFilesystemOptions{
    49  			InternalData: &sys.InternalData{
    50  				EnableTPUProxyPaths: pciTestDir != "",
    51  				TestSysfsPathPrefix: pciTestDir,
    52  			},
    53  		},
    54  	}
    55  
    56  	mns, err := k.VFS().NewMountNamespace(ctx, creds, "", sys.Name, mountOpts, nil)
    57  	if err != nil {
    58  		t.Fatalf("Failed to create new mount namespace: %v", err)
    59  	}
    60  	return testutil.NewSystem(ctx, t, k.VFS(), mns)
    61  }
    62  
    63  func TestReadCPUFile(t *testing.T) {
    64  	s := newTestSystem(t, "" /*pciTestDir*/)
    65  	defer s.Destroy()
    66  	k := kernel.KernelFromContext(s.Ctx)
    67  	maxCPUCores := k.ApplicationCores()
    68  
    69  	expected := fmt.Sprintf("0-%d\n", maxCPUCores-1)
    70  
    71  	for _, fname := range []string{"online", "possible", "present"} {
    72  		pop := s.PathOpAtRoot(fmt.Sprintf("devices/system/cpu/%s", fname))
    73  		fd, err := s.VFS.OpenAt(s.Ctx, s.Creds, pop, &vfs.OpenOptions{})
    74  		if err != nil {
    75  			t.Fatalf("OpenAt(pop:%+v) = %+v failed: %v", pop, fd, err)
    76  		}
    77  		defer fd.DecRef(s.Ctx)
    78  		content, err := s.ReadToEnd(fd)
    79  		if err != nil {
    80  			t.Fatalf("Read failed: %v", err)
    81  		}
    82  		if diff := cmp.Diff(expected, content); diff != "" {
    83  			t.Fatalf("Read returned unexpected data:\n--- want\n+++ got\n%v", diff)
    84  		}
    85  	}
    86  }
    87  
    88  func TestSysRootContainsExpectedEntries(t *testing.T) {
    89  	s := newTestSystem(t, "" /*pciTestDir*/)
    90  	defer s.Destroy()
    91  	pop := s.PathOpAtRoot("/")
    92  	s.AssertAllDirentTypes(s.ListDirents(pop), map[string]testutil.DirentType{
    93  		"block":    linux.DT_DIR,
    94  		"bus":      linux.DT_DIR,
    95  		"class":    linux.DT_DIR,
    96  		"dev":      linux.DT_DIR,
    97  		"devices":  linux.DT_DIR,
    98  		"firmware": linux.DT_DIR,
    99  		"fs":       linux.DT_DIR,
   100  		"kernel":   linux.DT_DIR,
   101  		"module":   linux.DT_DIR,
   102  		"power":    linux.DT_DIR,
   103  	})
   104  }
   105  
   106  func TestCgroupMountpointExists(t *testing.T) {
   107  	// Note: The mountpoint is only created if cgroups are available.
   108  	s := newTestSystem(t, "" /*pciTestDir*/)
   109  	defer s.Destroy()
   110  	pop := s.PathOpAtRoot("/fs")
   111  	s.AssertAllDirentTypes(s.ListDirents(pop), map[string]testutil.DirentType{
   112  		"cgroup": linux.DT_DIR,
   113  	})
   114  	pop = s.PathOpAtRoot("/fs/cgroup")
   115  	s.AssertAllDirentTypes(s.ListDirents(pop), map[string]testutil.DirentType{ /*empty*/ })
   116  }
   117  
   118  // Check that sysfs creates the required PCI paths for V4 TPUs.
   119  func TestEnableTPUProxyPathsV4(t *testing.T) {
   120  	// Set up the fs tree that will be mirrored in the sentry.
   121  	sysfsTestDir := t.TempDir()
   122  	accelPath := path.Join(sysfsTestDir, "sys", "devices", "pci0000:00", "0000:00:04.0", "accel", "accel0")
   123  	if err := os.MkdirAll(accelPath, 0755); err != nil {
   124  		t.Fatalf("Failed to create accel directory: %v", err)
   125  	}
   126  	if err := os.Symlink(path.Join("..", "..", "..", "0000:00:04.0"), path.Join(accelPath, "0000:00:04.0")); err != nil {
   127  		t.Fatalf("Failed to symlink accel directory: %v", err)
   128  	}
   129  	if err := os.Symlink(path.Join("..", "..", "..", "0000:00:04.0"), path.Join(accelPath, "device")); err != nil {
   130  		t.Fatalf("Failed to symlink accel device directory: %v", err)
   131  	}
   132  	if _, err := os.Create(path.Join(accelPath, "chip_model")); err != nil {
   133  		t.Fatalf("Failed to create chip_model: %v", err)
   134  	}
   135  	if _, err := os.Create(path.Join(accelPath, "device_owner")); err != nil {
   136  		t.Fatalf("Failed to create device_owner: %v", err)
   137  	}
   138  	if _, err := os.Create(path.Join(accelPath, "pci_address")); err != nil {
   139  		t.Fatalf("Failed to create pci_address: %v", err)
   140  	}
   141  	busPath := path.Join(sysfsTestDir, "sys", "bus", "pci", "devices")
   142  	if err := os.MkdirAll(busPath, 0755); err != nil {
   143  		t.Fatalf("Failed to create bus directory: %v", err)
   144  	}
   145  	if err := os.Symlink(path.Join("..", "..", "..", "devices", "pci0000:00", "0000:00:04.0"), path.Join(busPath, "0000:00:04.0")); err != nil {
   146  		t.Fatalf("Failed to symlink bus directory: %v", err)
   147  	}
   148  	classAccelPath := path.Join(sysfsTestDir, "sys", "class", "accel")
   149  	if err := os.MkdirAll(classAccelPath, 0755); err != nil {
   150  		t.Fatalf("Failed to create accel directory: %v", err)
   151  	}
   152  	if err := os.Symlink(path.Join("..", "..", "devices", "pci0000:00", "0000:00:04.0", "accel", "accel0"), path.Join(classAccelPath, "accel0")); err != nil {
   153  		t.Fatalf("Failed to symlink accel directory: %v", err)
   154  	}
   155  
   156  	s := newTestSystem(t, sysfsTestDir)
   157  	defer s.Destroy()
   158  
   159  	pop := s.PathOpAtRoot("/devices/pci0000:00")
   160  	s.AssertAllDirentTypes(s.ListDirents(pop), map[string]testutil.DirentType{
   161  		"0000:00:04.0": linux.DT_DIR,
   162  	})
   163  	pop = s.PathOpAtRoot("/devices/pci0000:00/0000:00:04.0/accel/accel0")
   164  	s.AssertAllDirentTypes(s.ListDirents(pop), map[string]testutil.DirentType{
   165  		"0000:00:04.0": linux.DT_LNK,
   166  		"device":       linux.DT_LNK,
   167  		"chip_model":   linux.DT_REG,
   168  		"device_owner": linux.DT_REG,
   169  		"pci_address":  linux.DT_REG,
   170  	})
   171  	pop = s.PathOpAtRoot("/bus/pci/devices")
   172  	s.AssertAllDirentTypes(s.ListDirents(pop), map[string]testutil.DirentType{
   173  		"0000:00:04.0": linux.DT_LNK,
   174  	})
   175  	pop = s.PathOpAtRoot("/class/accel")
   176  	s.AssertAllDirentTypes(s.ListDirents(pop), map[string]testutil.DirentType{
   177  		"accel0": linux.DT_LNK,
   178  	})
   179  }
   180  
   181  type PCIDeviceInfo struct {
   182  	// IOMMU group.
   183  	group      string
   184  	pciPath    string
   185  	pciAddress string
   186  	name       string
   187  }
   188  
   189  func (dev PCIDeviceInfo) path() string {
   190  	return path.Join(dev.pciPath, dev.pciAddress, vfioDev, dev.name)
   191  }
   192  
   193  func TestEnableTPUProxyPathsV5(t *testing.T) {
   194  	// Set up the fs tree that will be mirrored in the sentry.
   195  	sysfsTestDir := t.TempDir()
   196  	pciPath := path.Join(sysfsTestDir, "sys", "devices", "pci0000:00")
   197  	if err := os.MkdirAll(pciPath, 0755); err != nil {
   198  		t.Fatalf("Failed to create PCI directory: %v", err)
   199  	}
   200  	busPath := path.Join(sysfsTestDir, "sys", "bus", "pci", "devices")
   201  	if err := os.MkdirAll(busPath, 0755); err != nil {
   202  		t.Fatalf("Failed to create bus directory: %v", err)
   203  	}
   204  	sysClassPath := path.Join(sysfsTestDir, "sys", "class", vfioDev)
   205  	if err := os.MkdirAll(sysClassPath, 0755); err != nil {
   206  		t.Fatalf("Failed to create class directory: %v", err)
   207  	}
   208  
   209  	devices := []PCIDeviceInfo{
   210  		PCIDeviceInfo{
   211  			group:      "0",
   212  			pciPath:    pciPath,
   213  			pciAddress: "0000:00:04.0",
   214  			name:       "vfio0",
   215  		},
   216  		PCIDeviceInfo{
   217  			group:      "1",
   218  			pciPath:    pciPath,
   219  			pciAddress: "0000:00:05.0",
   220  			name:       "vfio1",
   221  		},
   222  	}
   223  	for _, device := range devices {
   224  		devicePath := device.path()
   225  		if err := os.MkdirAll(devicePath, 0755); err != nil {
   226  			t.Fatalf("Failed to create PCI device directory: %v", err)
   227  		}
   228  		if err := os.Symlink(path.Join("..", "..", "..", device.pciAddress), path.Join(devicePath, "device")); err != nil {
   229  			t.Fatalf("Failed to symlink device directory: %v", err)
   230  		}
   231  		if err := os.Symlink(path.Join("..", "..", "..", "devices", "pci0000:00", device.pciAddress), path.Join(busPath, device.pciAddress)); err != nil {
   232  			t.Fatalf("Failed to symlink bus directory: %v", err)
   233  		}
   234  		if err := os.Symlink(path.Join("..", "..", "devices", "pci0000:00", device.pciAddress, vfioDev, device.name), path.Join(sysClassPath, device.name)); err != nil {
   235  			t.Fatalf("Failed to symlink class directory: %v", err)
   236  		}
   237  		iommuPath := path.Join(sysfsTestDir, "sys", "kernel", "iommu_groups", device.group, "devices")
   238  		if err := os.MkdirAll(iommuPath, 0755); err != nil {
   239  			t.Fatalf("Failed to create iommu_groups directory: %v", err)
   240  		}
   241  		if err := os.Symlink(path.Join("..", "..", "..", "..", "devices", "pci0000:00", device.pciAddress), path.Join(iommuPath, device.pciAddress)); err != nil {
   242  			t.Fatalf("Failed to symlink iommu_group devices directory: %v", err)
   243  		}
   244  		if err := os.Symlink(path.Join("..", "..", "..", "kernel", "iommu_groups", device.group), path.Join(pciPath, device.pciAddress, "iommu_group")); err != nil {
   245  			t.Fatalf("Failed to symlink iommu_groups directory: %v", err)
   246  		}
   247  	}
   248  	s := newTestSystem(t, sysfsTestDir)
   249  	defer s.Destroy()
   250  
   251  	for _, device := range devices {
   252  		// Validate PCI device symlinks.
   253  		pop := s.PathOpAtRoot(path.Join("devices", "pci0000:00", device.pciAddress))
   254  		s.AssertAllDirentTypes(s.ListDirents(pop), map[string]testutil.DirentType{
   255  			"iommu_group": linux.DT_LNK,
   256  			vfioDev:       linux.DT_DIR,
   257  		})
   258  		// Validate VFIO device symlinks.
   259  		pop = s.PathOpAtRoot(path.Join("devices", "pci0000:00", device.pciAddress, vfioDev, device.name))
   260  		s.AssertAllDirentTypes(s.ListDirents(pop), map[string]testutil.DirentType{
   261  			"device": linux.DT_LNK,
   262  		})
   263  		// Validate $IOMMU_GROUP/devices.
   264  		pop = s.PathOpAtRoot(path.Join("kernel", "iommu_groups", string(device.group), "devices"))
   265  		s.AssertAllDirentTypes(s.ListDirents(pop), map[string]testutil.DirentType{
   266  			device.pciAddress: linux.DT_LNK,
   267  		})
   268  	}
   269  }