Files

97 lines
3.6 KiB
Go

// Copyright 2024 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 tpuproxy contains tpu backend driver proxy implementations and
// helper functions.
package tpuproxy
import (
"bytes"
"fmt"
"os"
"path"
"path/filepath"
"regexp"
"strconv"
"strings"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/tpu"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/sentry/devices/tpuproxy/accel"
"gvisor.dev/gvisor/pkg/sentry/devices/tpuproxy/vfio"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
)
var (
// TPUv4DeviceRegex is the regex for detecting TPUv4 device paths.
TPUv4DeviceRegex = regexp.MustCompile(`/dev/accel(\d+)`)
// TPUv5DeviceRegex is the regex for detecting TPUv5 device paths.
TPUv5DeviceRegex = regexp.MustCompile(`/dev/vfio/(\d+)`)
)
// RegisterTPUv4Device registers the TPUv4 device with the provided minor number
// where the corresponding PCI device is located at pciPath. Accel devices
// always have their device file number set to their minor number.
func RegisterTPUv4Device(ctx context.Context, creds *auth.Credentials, root vfs.VirtualDentry, vfsObj *vfs.VirtualFilesystem, devPath string, minorNum uint32) error {
// Get the PCI path from the accel device's symlink at
// /sys/class/accel/accel\d+. The link will be in the form
// "../../devices/pci0000:*/**/accel/accel\d+".
linkPath := filepath.Join("/sys/class/accel", filepath.Base(devPath))
linkContent, err := vfsObj.ReadlinkAt(ctx, creds, &vfs.PathOperation{Root: root, Start: root, Path: fspath.Parse(linkPath)})
if err != nil {
return fmt.Errorf("reading link %q: %w", linkPath, err)
}
// Exclude the ../../devices prefix and the accel/accel\d+ suffix.
pciPath := strings.TrimSuffix(strings.TrimPrefix(linkContent, "../../devices"), fmt.Sprintf("accel/%s", filepath.Base(devPath)))
pciDeviceIDPath := path.Join("/sys/devices", pciPath, "device")
fd, err := unix.Openat(-1, pciDeviceIDPath, unix.O_RDONLY|unix.O_NOFOLLOW, 0)
if err != nil {
return err
}
file := os.NewFile(uintptr(fd), pciDeviceIDPath)
defer file.Close()
buf := bytes.Buffer{}
if _, err := buf.ReadFrom(file); err != nil {
return err
}
deviceIDStr := strings.ReplaceAll(buf.String(), "0x", "")
deviceID, err := strconv.ParseInt(strings.TrimSpace(deviceIDStr), 16, 64)
if err != nil {
return fmt.Errorf("parsing PCI device ID: %w", err)
}
if err := accel.RegisterTPUDevice(vfsObj, minorNum, deviceID == tpu.TPUV4liteDeviceID); err != nil {
return fmt.Errorf("registering TPU driver: %w", err)
}
return nil
}
// RegisterTPUv5Device registers the TPUv5 device with the provided device path
// and minor number.
func RegisterTPUv5Device(vfsObj *vfs.VirtualFilesystem, devPath string, minorNum uint32) error {
deviceNum, err := strconv.ParseInt(path.Base(devPath), 10, 32)
if err != nil {
return fmt.Errorf("parsing device path number: %w", err)
}
if err := vfio.RegisterTPUDevice(vfsObj, uint32(minorNum), uint32(deviceNum), true /* useDevGofer */); err != nil {
return fmt.Errorf("registering TPU driver: %w", err)
}
return nil
}