mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Reorganize tpuproxy code.
Now there is a main tpuproxy directory and two subdirs for each of the different proxy implementations. PiperOrigin-RevId: 667688748
This commit is contained in:
committed by
gVisor bot
parent
8db8a6b7de
commit
e29e270730
@@ -1,83 +0,0 @@
|
||||
// Copyright 2023 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 accel
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/abi/gasket"
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/seccomp"
|
||||
)
|
||||
|
||||
// Filters returns seccomp-bpf filters for this package.
|
||||
func Filters() seccomp.SyscallRules {
|
||||
return seccomp.MakeSyscallRules(map[uintptr]seccomp.SyscallRule{
|
||||
unix.SYS_OPENAT: seccomp.PerArg{
|
||||
// All paths that we openat() are absolute, so we pass a dirfd
|
||||
// of -1 (which is invalid for relative paths, but ignored for
|
||||
// absolute paths) to hedge against bugs involving AT_FDCWD or
|
||||
// real dirfds.
|
||||
seccomp.EqualTo(^uintptr(0)),
|
||||
seccomp.AnyValue{},
|
||||
seccomp.MaskedEqual(unix.O_CREAT|unix.O_NOFOLLOW, unix.O_NOFOLLOW),
|
||||
seccomp.AnyValue{},
|
||||
},
|
||||
unix.SYS_GETDENTS64: seccomp.MatchAll{},
|
||||
unix.SYS_IOCTL: seccomp.Or{
|
||||
seccomp.PerArg{
|
||||
seccomp.NonNegativeFD{},
|
||||
seccomp.EqualTo(gasket.GASKET_IOCTL_RESET),
|
||||
},
|
||||
seccomp.PerArg{
|
||||
seccomp.NonNegativeFD{},
|
||||
seccomp.EqualTo(gasket.GASKET_IOCTL_MAP_BUFFER),
|
||||
},
|
||||
seccomp.PerArg{
|
||||
seccomp.NonNegativeFD{},
|
||||
seccomp.EqualTo(gasket.GASKET_IOCTL_UNMAP_BUFFER),
|
||||
},
|
||||
seccomp.PerArg{
|
||||
seccomp.NonNegativeFD{},
|
||||
seccomp.EqualTo(gasket.GASKET_IOCTL_CLEAR_INTERRUPT_COUNTS),
|
||||
},
|
||||
seccomp.PerArg{
|
||||
seccomp.NonNegativeFD{},
|
||||
seccomp.EqualTo(gasket.GASKET_IOCTL_REGISTER_INTERRUPT),
|
||||
},
|
||||
seccomp.PerArg{
|
||||
seccomp.NonNegativeFD{},
|
||||
seccomp.EqualTo(gasket.GASKET_IOCTL_UNREGISTER_INTERRUPT),
|
||||
},
|
||||
},
|
||||
unix.SYS_EVENTFD2: seccomp.Or{
|
||||
seccomp.PerArg{
|
||||
seccomp.AnyValue{},
|
||||
seccomp.EqualTo(linux.EFD_NONBLOCK),
|
||||
},
|
||||
seccomp.PerArg{
|
||||
seccomp.AnyValue{},
|
||||
seccomp.EqualTo(linux.EFD_NONBLOCK | linux.EFD_SEMAPHORE),
|
||||
},
|
||||
},
|
||||
unix.SYS_MREMAP: seccomp.PerArg{
|
||||
seccomp.AnyValue{},
|
||||
seccomp.EqualTo(0), /* old_size */
|
||||
seccomp.AnyValue{},
|
||||
seccomp.EqualTo(linux.MREMAP_MAYMOVE | linux.MREMAP_FIXED),
|
||||
seccomp.AnyValue{},
|
||||
seccomp.EqualTo(0),
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
load("//tools:defs.bzl", "go_library")
|
||||
load("//tools/go_generics:defs.bzl", "go_template_instance")
|
||||
load("//tools:defs.bzl", "go_library", "go_test")
|
||||
|
||||
package(default_applicable_licenses = ["//:license"])
|
||||
|
||||
@@ -8,70 +7,26 @@ licenses(["notice"])
|
||||
go_library(
|
||||
name = "tpuproxy",
|
||||
srcs = [
|
||||
"devaddr_range.go",
|
||||
"devaddr_set.go",
|
||||
"device.go",
|
||||
"ioctl_unsafe.go",
|
||||
"seccomp_filter.go",
|
||||
"tpu.go",
|
||||
"tpu_mmap.go",
|
||||
"vfio.go",
|
||||
"vfio_mmap.go",
|
||||
"tpuproxy.go",
|
||||
],
|
||||
visibility = [
|
||||
"//pkg/sentry:internal",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/abi/gasket",
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/cleanup",
|
||||
"//pkg/context",
|
||||
"//pkg/devutil",
|
||||
"//pkg/errors/linuxerr",
|
||||
"//pkg/fdnotifier",
|
||||
"//pkg/hostarch",
|
||||
"//pkg/log",
|
||||
"//pkg/marshal/primitive",
|
||||
"//pkg/safemem",
|
||||
"//pkg/abi/tpu",
|
||||
"//pkg/seccomp",
|
||||
"//pkg/sentry/arch",
|
||||
"//pkg/sentry/fsimpl/eventfd",
|
||||
"//pkg/sentry/fsimpl/kernfs",
|
||||
"//pkg/sentry/kernel",
|
||||
"//pkg/sentry/memmap",
|
||||
"//pkg/sentry/mm",
|
||||
"//pkg/sentry/devices/tpuproxy/accel",
|
||||
"//pkg/sentry/devices/tpuproxy/vfio",
|
||||
"//pkg/sentry/vfs",
|
||||
"//pkg/sync",
|
||||
"//pkg/usermem",
|
||||
"//pkg/waiter",
|
||||
"@org_golang_x_exp//constraints:go_default_library",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_template_instance(
|
||||
name = "devaddr_range",
|
||||
out = "devaddr_range.go",
|
||||
package = "tpuproxy",
|
||||
prefix = "DevAddr",
|
||||
template = "//pkg/segment:generic_range",
|
||||
types = {
|
||||
"T": "uint64",
|
||||
},
|
||||
)
|
||||
|
||||
go_template_instance(
|
||||
name = "devaddr_set",
|
||||
out = "devaddr_set.go",
|
||||
imports = {
|
||||
"mm": "gvisor.dev/gvisor/pkg/sentry/mm",
|
||||
},
|
||||
package = "tpuproxy",
|
||||
prefix = "DevAddr",
|
||||
template = "//pkg/segment:generic_set",
|
||||
types = {
|
||||
"Key": "uint64",
|
||||
"Range": "DevAddrRange",
|
||||
"Value": "mm.PinnedRange",
|
||||
"Functions": "devAddrSetFuncs",
|
||||
},
|
||||
go_test(
|
||||
name = "tpuproxy_test",
|
||||
srcs = ["tpuproxy_test.go"],
|
||||
library = ":tpuproxy",
|
||||
)
|
||||
|
||||
@@ -8,13 +8,12 @@ licenses(["notice"])
|
||||
go_library(
|
||||
name = "accel",
|
||||
srcs = [
|
||||
"accel.go",
|
||||
"accel_fd.go",
|
||||
"accel_fd_mmap.go",
|
||||
"devaddr_range.go",
|
||||
"devaddr_set.go",
|
||||
"device.go",
|
||||
"gasket.go",
|
||||
"seccomp_filters.go",
|
||||
"tpu_v4.go",
|
||||
"tpu_v4_mmap.go",
|
||||
"gasket_ioctl.go",
|
||||
],
|
||||
visibility = ["//pkg/sentry:internal"],
|
||||
deps = [
|
||||
@@ -31,7 +30,7 @@ go_library(
|
||||
"//pkg/safemem",
|
||||
"//pkg/seccomp",
|
||||
"//pkg/sentry/arch",
|
||||
"//pkg/sentry/devices/tpuproxy",
|
||||
"//pkg/sentry/devices/tpuproxy/util",
|
||||
"//pkg/sentry/fsimpl/eventfd",
|
||||
"//pkg/sentry/kernel",
|
||||
"//pkg/sentry/memmap",
|
||||
@@ -12,6 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package accel implements a proxy for gasket based accel devices.
|
||||
package accel
|
||||
|
||||
import (
|
||||
@@ -29,10 +30,10 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
)
|
||||
|
||||
// tpuV4Device implements vfs.Device for /dev/accel[0-9]+.
|
||||
// accelDevice implements vfs.Device for /dev/accel[0-9]+.
|
||||
//
|
||||
// +stateify savable
|
||||
type tpuV4Device struct {
|
||||
type accelDevice struct {
|
||||
mu sync.Mutex
|
||||
|
||||
minor uint32
|
||||
@@ -45,7 +46,7 @@ type tpuV4Device struct {
|
||||
owner *kernel.ThreadGroup
|
||||
}
|
||||
|
||||
func (dev *tpuV4Device) Open(ctx context.Context, mnt *vfs.Mount, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
|
||||
func (dev *accelDevice) Open(ctx context.Context, mnt *vfs.Mount, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
|
||||
devClient := devutil.GoferClientFromContext(ctx)
|
||||
if devClient == nil {
|
||||
log.Warningf("devutil.CtxDevGoferClient is not set")
|
||||
@@ -59,7 +60,7 @@ func (dev *tpuV4Device) Open(ctx context.Context, mnt *vfs.Mount, vfsd *vfs.Dent
|
||||
ctx.Warningf("accelDevice: failed to open device %s: %v", name, err)
|
||||
return nil, err
|
||||
}
|
||||
fd := &tpuV4FD{
|
||||
fd := &accelFD{
|
||||
hostFD: int32(hostFD),
|
||||
device: dev,
|
||||
}
|
||||
@@ -89,7 +90,7 @@ func (dev *tpuV4Device) Open(ctx context.Context, mnt *vfs.Mount, vfsd *vfs.Dent
|
||||
|
||||
// RegisterTPUDevice registers all devices implemented by this package in vfsObj.
|
||||
func RegisterTPUDevice(vfsObj *vfs.VirtualFilesystem, minor uint32, lite bool) error {
|
||||
return vfsObj.RegisterDevice(vfs.CharDevice, linux.ACCEL_MAJOR, minor, &tpuV4Device{
|
||||
return vfsObj.RegisterDevice(vfs.CharDevice, linux.ACCEL_MAJOR, minor, &accelDevice{
|
||||
lite: lite,
|
||||
minor: minor,
|
||||
}, &vfs.RegisterDeviceOptions{
|
||||
+15
-16
@@ -12,7 +12,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package accel implements proxying for hardware accelerators.
|
||||
package accel
|
||||
|
||||
import (
|
||||
@@ -27,7 +26,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/devices/tpuproxy"
|
||||
"gvisor.dev/gvisor/pkg/sentry/devices/tpuproxy/util"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/mm"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
@@ -35,24 +34,24 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
// tpuV4FD implements vfs.FileDescriptionImpl for /dev/accel[0-9]+.
|
||||
// accelFD implements vfs.FileDescriptionImpl for /dev/accel[0-9]+.
|
||||
//
|
||||
// accelFD is not savable; we do not implement save/restore of accelerator
|
||||
// state.
|
||||
type tpuV4FD struct {
|
||||
type accelFD struct {
|
||||
vfsfd vfs.FileDescription
|
||||
vfs.FileDescriptionDefaultImpl
|
||||
vfs.DentryMetadataFileDescriptionImpl
|
||||
vfs.NoLockFD
|
||||
|
||||
hostFD int32
|
||||
device *tpuV4Device
|
||||
device *accelDevice
|
||||
queue waiter.Queue
|
||||
memmapFile accelFDMemmapFile
|
||||
}
|
||||
|
||||
// Release implements vfs.FileDescriptionImpl.Release.
|
||||
func (fd *tpuV4FD) Release(context.Context) {
|
||||
func (fd *accelFD) Release(context.Context) {
|
||||
fd.device.mu.Lock()
|
||||
defer fd.device.mu.Unlock()
|
||||
fd.device.openWriteFDs--
|
||||
@@ -68,7 +67,7 @@ func (fd *tpuV4FD) Release(context.Context) {
|
||||
Size: r.End - r.Start,
|
||||
HostAddress: 0,
|
||||
}
|
||||
_, err := tpuproxy.IOCTLInvokePtrArg[gasket.Ioctl](fd.hostFD, gasket.GASKET_IOCTL_UNMAP_BUFFER, &gpti)
|
||||
_, err := util.IOCTLInvokePtrArg[gasket.Ioctl](fd.hostFD, gasket.GASKET_IOCTL_UNMAP_BUFFER, &gpti)
|
||||
if err != nil {
|
||||
log.Warningf("could not unmap range [%#x, %#x) (index %d) on device: %v", r.Start, r.End, v.pageTableIndex, err)
|
||||
}
|
||||
@@ -83,7 +82,7 @@ func (fd *tpuV4FD) Release(context.Context) {
|
||||
}
|
||||
|
||||
// EventRegister implements waiter.Waitable.EventRegister.
|
||||
func (fd *tpuV4FD) EventRegister(e *waiter.Entry) error {
|
||||
func (fd *accelFD) EventRegister(e *waiter.Entry) error {
|
||||
fd.queue.EventRegister(e)
|
||||
if err := fdnotifier.UpdateFD(fd.hostFD); err != nil {
|
||||
fd.queue.EventUnregister(e)
|
||||
@@ -93,7 +92,7 @@ func (fd *tpuV4FD) EventRegister(e *waiter.Entry) error {
|
||||
}
|
||||
|
||||
// EventUnregister implements waiter.Waitable.EventUnregister.
|
||||
func (fd *tpuV4FD) EventUnregister(e *waiter.Entry) {
|
||||
func (fd *accelFD) EventUnregister(e *waiter.Entry) {
|
||||
fd.queue.EventUnregister(e)
|
||||
if err := fdnotifier.UpdateFD(fd.hostFD); err != nil {
|
||||
panic(fmt.Sprint("UpdateFD:", err))
|
||||
@@ -101,17 +100,17 @@ func (fd *tpuV4FD) EventUnregister(e *waiter.Entry) {
|
||||
}
|
||||
|
||||
// Readiness implements waiter.Waitable.Readiness.
|
||||
func (fd *tpuV4FD) Readiness(mask waiter.EventMask) waiter.EventMask {
|
||||
func (fd *accelFD) Readiness(mask waiter.EventMask) waiter.EventMask {
|
||||
return fdnotifier.NonBlockingPoll(fd.hostFD, mask)
|
||||
}
|
||||
|
||||
// Epollable implements vfs.FileDescriptionImpl.Epollable.
|
||||
func (fd *tpuV4FD) Epollable() bool {
|
||||
func (fd *accelFD) Epollable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Ioctl implements vfs.FileDescriptionImpl.Ioctl.
|
||||
func (fd *tpuV4FD) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) {
|
||||
func (fd *accelFD) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) {
|
||||
cmd := args[1].Uint()
|
||||
argPtr := args[2].Pointer()
|
||||
argSize := linux.IOC_SIZE(cmd)
|
||||
@@ -133,17 +132,17 @@ func (fd *tpuV4FD) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, arg
|
||||
gasket.GASKET_IOCTL_MAP_DMA_BUF:
|
||||
return 0, linuxerr.ENOSYS
|
||||
case gasket.GASKET_IOCTL_RESET:
|
||||
return tpuproxy.IOCTLInvoke[gasket.Ioctl, uint64](fd.hostFD, gasket.GASKET_IOCTL_RESET, args[2].Uint64())
|
||||
return util.IOCTLInvoke[gasket.Ioctl, uint64](fd.hostFD, gasket.GASKET_IOCTL_RESET, args[2].Uint64())
|
||||
case gasket.GASKET_IOCTL_MAP_BUFFER:
|
||||
return gasketMapBufferIoctl(ctx, t, fd.hostFD, fd, argPtr)
|
||||
case gasket.GASKET_IOCTL_UNMAP_BUFFER:
|
||||
return gasketUnmapBufferIoctl(ctx, t, fd.hostFD, fd, argPtr)
|
||||
case gasket.GASKET_IOCTL_CLEAR_INTERRUPT_COUNTS:
|
||||
return tpuproxy.IOCTLInvoke[gasket.Ioctl](fd.hostFD, gasket.GASKET_IOCTL_CLEAR_INTERRUPT_COUNTS, 0)
|
||||
return util.IOCTLInvoke[gasket.Ioctl](fd.hostFD, gasket.GASKET_IOCTL_CLEAR_INTERRUPT_COUNTS, 0)
|
||||
case gasket.GASKET_IOCTL_REGISTER_INTERRUPT:
|
||||
return gasketInterruptMappingIoctl(ctx, t, fd.hostFD, argPtr, fd.device.lite)
|
||||
case gasket.GASKET_IOCTL_UNREGISTER_INTERRUPT:
|
||||
return tpuproxy.IOCTLInvoke[gasket.Ioctl, uint64](fd.hostFD, gasket.GASKET_IOCTL_UNREGISTER_INTERRUPT, args[2].Uint64())
|
||||
return util.IOCTLInvoke[gasket.Ioctl, uint64](fd.hostFD, gasket.GASKET_IOCTL_UNREGISTER_INTERRUPT, args[2].Uint64())
|
||||
default:
|
||||
return 0, linuxerr.EINVAL
|
||||
}
|
||||
@@ -152,7 +151,7 @@ func (fd *tpuV4FD) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, arg
|
||||
// checkPermission checks that the thread that owns this device is the only
|
||||
// one that can issue commands to the TPU. Other threads with access to
|
||||
// /dev/accel will not be able to issue commands to the device.
|
||||
func (fd *tpuV4FD) checkPermission(t *kernel.Task) error {
|
||||
func (fd *accelFD) checkPermission(t *kernel.Task) error {
|
||||
fd.device.mu.Lock()
|
||||
defer fd.device.mu.Unlock()
|
||||
owner := fd.device.owner
|
||||
+7
-7
@@ -25,26 +25,26 @@ import (
|
||||
)
|
||||
|
||||
// ConfigureMMap implements vfs.FileDescriptionImpl.ConfigureMMap.
|
||||
func (fd *tpuV4FD) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error {
|
||||
func (fd *accelFD) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error {
|
||||
return vfs.GenericConfigureMMap(&fd.vfsfd, fd, opts)
|
||||
}
|
||||
|
||||
// AddMapping implements memmap.Mappable.AddMapping.
|
||||
func (fd *tpuV4FD) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) error {
|
||||
func (fd *accelFD) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveMapping implements memmap.Mappable.RemoveMapping.
|
||||
func (fd *tpuV4FD) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) {
|
||||
func (fd *accelFD) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) {
|
||||
}
|
||||
|
||||
// CopyMapping implements memmap.Mappable.CopyMapping.
|
||||
func (fd *tpuV4FD) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR hostarch.AddrRange, offset uint64, writable bool) error {
|
||||
func (fd *accelFD) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR hostarch.AddrRange, offset uint64, writable bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Translate implements memmap.Mappable.Translate.
|
||||
func (fd *tpuV4FD) Translate(ctx context.Context, required, optional memmap.MappableRange, at hostarch.AccessType) ([]memmap.Translation, error) {
|
||||
func (fd *accelFD) Translate(ctx context.Context, required, optional memmap.MappableRange, at hostarch.AccessType) ([]memmap.Translation, error) {
|
||||
return []memmap.Translation{
|
||||
{
|
||||
Source: optional,
|
||||
@@ -56,14 +56,14 @@ func (fd *tpuV4FD) Translate(ctx context.Context, required, optional memmap.Mapp
|
||||
}
|
||||
|
||||
// InvalidateUnsavable implements memmap.Mappable.InvalidateUnsavable.
|
||||
func (fd *tpuV4FD) InvalidateUnsavable(ctx context.Context) error {
|
||||
func (fd *accelFD) InvalidateUnsavable(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type accelFDMemmapFile struct {
|
||||
memmap.NoBufferedIOFallback
|
||||
|
||||
fd *tpuV4FD
|
||||
fd *accelFD
|
||||
}
|
||||
|
||||
// IncRef implements memmap.File.IncRef.
|
||||
+6
-6
@@ -23,14 +23,14 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/devices/tpuproxy"
|
||||
"gvisor.dev/gvisor/pkg/sentry/devices/tpuproxy/util"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/eventfd"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/memmap"
|
||||
"gvisor.dev/gvisor/pkg/sentry/mm"
|
||||
)
|
||||
|
||||
func gasketMapBufferIoctl(ctx context.Context, t *kernel.Task, hostFd int32, fd *tpuV4FD, paramsAddr hostarch.Addr) (uintptr, error) {
|
||||
func gasketMapBufferIoctl(ctx context.Context, t *kernel.Task, hostFd int32, fd *accelFD, paramsAddr hostarch.Addr) (uintptr, error) {
|
||||
var userIoctlParams gasket.GasketPageTableIoctl
|
||||
if _, err := userIoctlParams.CopyIn(t, paramsAddr); err != nil {
|
||||
return 0, err
|
||||
@@ -104,7 +104,7 @@ func gasketMapBufferIoctl(ctx context.Context, t *kernel.Task, hostFd int32, fd
|
||||
}
|
||||
sentryIoctlParams := userIoctlParams
|
||||
sentryIoctlParams.HostAddress = uint64(m)
|
||||
n, err := tpuproxy.IOCTLInvokePtrArg[gasket.Ioctl](hostFd, gasket.GASKET_IOCTL_MAP_BUFFER, &sentryIoctlParams)
|
||||
n, err := util.IOCTLInvokePtrArg[gasket.Ioctl](hostFd, gasket.GASKET_IOCTL_MAP_BUFFER, &sentryIoctlParams)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
@@ -125,7 +125,7 @@ func gasketMapBufferIoctl(ctx context.Context, t *kernel.Task, hostFd int32, fd
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func gasketUnmapBufferIoctl(ctx context.Context, t *kernel.Task, hostFd int32, fd *tpuV4FD, paramsAddr hostarch.Addr) (uintptr, error) {
|
||||
func gasketUnmapBufferIoctl(ctx context.Context, t *kernel.Task, hostFd int32, fd *accelFD, paramsAddr hostarch.Addr) (uintptr, error) {
|
||||
var userIoctlParams gasket.GasketPageTableIoctl
|
||||
if _, err := userIoctlParams.CopyIn(t, paramsAddr); err != nil {
|
||||
return 0, err
|
||||
@@ -151,7 +151,7 @@ func gasketUnmapBufferIoctl(ctx context.Context, t *kernel.Task, hostFd int32, f
|
||||
|
||||
sentryIoctlParams := userIoctlParams
|
||||
sentryIoctlParams.HostAddress = 0 // clobber this value, it's unused.
|
||||
n, err := tpuproxy.IOCTLInvokePtrArg[gasket.Ioctl](hostFd, gasket.GASKET_IOCTL_UNMAP_BUFFER, &sentryIoctlParams)
|
||||
n, err := util.IOCTLInvokePtrArg[gasket.Ioctl](hostFd, gasket.GASKET_IOCTL_UNMAP_BUFFER, &sentryIoctlParams)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
@@ -211,7 +211,7 @@ func gasketInterruptMappingIoctl(ctx context.Context, t *kernel.Task, hostFd int
|
||||
|
||||
sentryIoctlParams := userIoctlParams
|
||||
sentryIoctlParams.EventFD = uint64(eventfd)
|
||||
n, err := tpuproxy.IOCTLInvokePtrArg[gasket.Ioctl](hostFd, gasket.GASKET_IOCTL_REGISTER_INTERRUPT, &sentryIoctlParams)
|
||||
n, err := util.IOCTLInvokePtrArg[gasket.Ioctl](hostFd, gasket.GASKET_IOCTL_REGISTER_INTERRUPT, &sentryIoctlParams)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
@@ -16,6 +16,7 @@ package tpuproxy
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/abi/gasket"
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/seccomp"
|
||||
)
|
||||
@@ -103,6 +104,30 @@ func Filters() seccomp.SyscallRules {
|
||||
seccomp.NonNegativeFD{},
|
||||
seccomp.EqualTo(linux.VFIO_SET_IOMMU),
|
||||
},
|
||||
seccomp.PerArg{
|
||||
seccomp.NonNegativeFD{},
|
||||
seccomp.EqualTo(gasket.GASKET_IOCTL_RESET),
|
||||
},
|
||||
seccomp.PerArg{
|
||||
seccomp.NonNegativeFD{},
|
||||
seccomp.EqualTo(gasket.GASKET_IOCTL_MAP_BUFFER),
|
||||
},
|
||||
seccomp.PerArg{
|
||||
seccomp.NonNegativeFD{},
|
||||
seccomp.EqualTo(gasket.GASKET_IOCTL_UNMAP_BUFFER),
|
||||
},
|
||||
seccomp.PerArg{
|
||||
seccomp.NonNegativeFD{},
|
||||
seccomp.EqualTo(gasket.GASKET_IOCTL_CLEAR_INTERRUPT_COUNTS),
|
||||
},
|
||||
seccomp.PerArg{
|
||||
seccomp.NonNegativeFD{},
|
||||
seccomp.EqualTo(gasket.GASKET_IOCTL_REGISTER_INTERRUPT),
|
||||
},
|
||||
seccomp.PerArg{
|
||||
seccomp.NonNegativeFD{},
|
||||
seccomp.EqualTo(gasket.GASKET_IOCTL_UNREGISTER_INTERRUPT),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/tpu"
|
||||
"gvisor.dev/gvisor/pkg/sentry/devices/tpuproxy/accel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/devices/tpuproxy/vfio"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
)
|
||||
|
||||
const (
|
||||
pciPathGlobTPUv4 = "/sys/devices/pci0000:*/*/accel/accel*"
|
||||
pciPathGlobTPUv5 = "/sys/devices/pci0000:*/*/vfio-dev/vfio*"
|
||||
iommuGroupPathGlob = "/sys/kernel/iommu_groups/*/devices/*"
|
||||
)
|
||||
|
||||
var (
|
||||
// pathGlobToPathRegex is a map that points a TPU PCI path glob to its path regex.
|
||||
// TPU v4 devices are accessible via /sys/devices/pci0000:00/<pci_address>/accel/accel# on the host.
|
||||
// TPU v5 devices are accessible via at /sys/devices/pci0000:00/<pci_address>/vfio-dev/vfio# on the host.
|
||||
pathGlobToPathRegex = map[string]string{
|
||||
pciPathGlobTPUv4: `^/sys/devices/pci0000:[[:xdigit:]]{2}/\d+:\d+:\d+\.\d+/accel/accel(\d+)$`,
|
||||
pciPathGlobTPUv5: `^/sys/devices/pci0000:[[:xdigit:]]{2}/\d+:\d+:\d+\.\d+/vfio-dev/vfio(\d+)$`,
|
||||
}
|
||||
)
|
||||
|
||||
// RegisterHostTPUDevices enumerates TPU devices on the host and registers them
|
||||
// in the sandbox VFS.
|
||||
func RegisterHostTPUDevices(vfsObj *vfs.VirtualFilesystem, allowedDeviceIDs map[int64]any) error {
|
||||
for pciPathGlobal, pathRegex := range pathGlobToPathRegex {
|
||||
pciAddrs, err := filepath.Glob(pciPathGlobal)
|
||||
if err != nil {
|
||||
return fmt.Errorf("enumerating PCI device files: %w", err)
|
||||
}
|
||||
pciPathRegex := regexp.MustCompile(pathRegex)
|
||||
for _, pciPath := range pciAddrs {
|
||||
ms := pciPathRegex.FindStringSubmatch(pciPath)
|
||||
if ms == nil {
|
||||
continue
|
||||
}
|
||||
minorNum, err := strconv.ParseUint(ms[1], 10, 32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing PCI device number: %w", err)
|
||||
}
|
||||
var deviceIDBytes []byte
|
||||
if deviceIDBytes, err = os.ReadFile(path.Join(pciPath, "device/device")); err != nil {
|
||||
return fmt.Errorf("reading PCI device ID: %w", err)
|
||||
}
|
||||
deviceIDStr := strings.Replace(string(deviceIDBytes), "0x", "", -1)
|
||||
deviceID, err := strconv.ParseInt(strings.TrimSpace(deviceIDStr), 16, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing PCI device ID: %w", err)
|
||||
}
|
||||
if _, ok := allowedDeviceIDs[deviceID]; !ok {
|
||||
return fmt.Errorf("unsupported TPU device with ID: 0x%x", deviceID)
|
||||
}
|
||||
// VFIO iommu groups correspond to the device number. Use these
|
||||
// paths to get the correct number for the sentry-internal TPU
|
||||
// device files.
|
||||
var deviceNum int
|
||||
switch deviceID {
|
||||
case tpu.TPUV4DeviceID, tpu.TPUV4liteDeviceID:
|
||||
deviceNum = int(deviceNum)
|
||||
case tpu.TPUV5eDeviceID, tpu.TPUV5pDeviceID:
|
||||
groupPaths, err := filepath.Glob(iommuGroupPathGlob)
|
||||
if err != nil {
|
||||
return fmt.Errorf("enumerating IOMMU group files: %w", err)
|
||||
}
|
||||
for _, groupPath := range groupPaths {
|
||||
pci := path.Base(groupPath)
|
||||
if strings.Contains(pciPath, pci) {
|
||||
n, err := strconv.Atoi(strings.Split(groupPath, "/")[4])
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing IOMMU group minor number: %w", err)
|
||||
}
|
||||
deviceNum = n
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unsupported TPU device with ID: 0x%x", deviceID)
|
||||
}
|
||||
if err := registerTPUDevice(vfsObj, uint32(minorNum), uint32(deviceNum), deviceID); err != nil {
|
||||
return fmt.Errorf("registering TPU driver: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// registerTPUDevice registers a TPU device in vfsObj based on the given device ID.
|
||||
func registerTPUDevice(vfsObj *vfs.VirtualFilesystem, minor, deviceNum uint32, deviceID int64) error {
|
||||
switch deviceID {
|
||||
case tpu.TPUV4DeviceID, tpu.TPUV4liteDeviceID:
|
||||
return accel.RegisterTPUDevice(vfsObj, minor, deviceID == tpu.TPUV4liteDeviceID)
|
||||
case tpu.TPUV5eDeviceID, tpu.TPUV5pDeviceID:
|
||||
return vfio.RegisterTPUDevice(vfsObj, minor, deviceNum)
|
||||
default:
|
||||
return fmt.Errorf("unsupported TPU device with ID: 0x%x", deviceID)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// 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
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"slices"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTPUPath(t *testing.T) {
|
||||
for _, tst := range []struct {
|
||||
name string
|
||||
pathGlob string
|
||||
path string
|
||||
submatch []string
|
||||
}{
|
||||
{
|
||||
name: "TPUv4PCIPathMatch",
|
||||
pathGlob: pciPathGlobTPUv4,
|
||||
path: "/sys/devices/pci0000:00/0000:00:01.0/accel/accel16",
|
||||
submatch: []string{"/sys/devices/pci0000:00/0000:00:01.0/accel/accel16", "16"},
|
||||
},
|
||||
{
|
||||
name: "TPUv4PCIPathNoMatch",
|
||||
pathGlob: pciPathGlobTPUv4,
|
||||
path: "/sys/devices/pci0000:00/0000:00:01.0/accel/123",
|
||||
submatch: nil,
|
||||
},
|
||||
{
|
||||
name: "TPUv5PCIPathMatch",
|
||||
pathGlob: pciPathGlobTPUv5,
|
||||
path: "/sys/devices/pci0000:00/0000:00:05.0/vfio-dev/vfio20",
|
||||
submatch: []string{"/sys/devices/pci0000:00/0000:00:05.0/vfio-dev/vfio20", "20"},
|
||||
},
|
||||
{
|
||||
name: "TPUv5PCIPathNoMatch",
|
||||
pathGlob: pciPathGlobTPUv5,
|
||||
path: "/sys/devices/pci0000:00/0000:00:05.0/vfio/vfio20",
|
||||
submatch: nil,
|
||||
},
|
||||
} {
|
||||
t.Run(tst.name, func(t *testing.T) {
|
||||
if _, err := filepath.Glob(tst.pathGlob); err != nil {
|
||||
t.Errorf("Malformed path glob: %v", err)
|
||||
}
|
||||
pathRegex := regexp.MustCompile(pathGlobToPathRegex[tst.pathGlob])
|
||||
if submatch := pathRegex.FindStringSubmatch(tst.path); !slices.Equal(submatch, tst.submatch) {
|
||||
t.Errorf("Match TPU PCI path, got: %v, want: %v", submatch, tst.submatch)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
load("//tools:defs.bzl", "go_library")
|
||||
|
||||
package(default_applicable_licenses = ["//:license"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
go_library(
|
||||
name = "util",
|
||||
srcs = [
|
||||
"ioctl_unsafe.go",
|
||||
"util.go",
|
||||
],
|
||||
visibility = [
|
||||
"//pkg/sentry:internal",
|
||||
],
|
||||
deps = [
|
||||
"@org_golang_x_exp//constraints:go_default_library",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
],
|
||||
)
|
||||
+1
-1
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License
|
||||
|
||||
package tpuproxy
|
||||
package util
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
@@ -0,0 +1,16 @@
|
||||
// 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 util contains helper functions for tpuproxy implementations.
|
||||
package util
|
||||
@@ -0,0 +1,75 @@
|
||||
load("//tools:defs.bzl", "go_library")
|
||||
load("//tools/go_generics:defs.bzl", "go_template_instance")
|
||||
|
||||
package(default_applicable_licenses = ["//:license"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
go_library(
|
||||
name = "vfio",
|
||||
srcs = [
|
||||
"devaddr_range.go",
|
||||
"devaddr_set.go",
|
||||
"pci_device_fd.go",
|
||||
"pci_device_fd_mmap.go",
|
||||
"tpu_fd.go",
|
||||
"tpu_fd_mmap.go",
|
||||
"vfio.go",
|
||||
"vfio_fd.go",
|
||||
"vfio_fd_mmap.go",
|
||||
],
|
||||
visibility = [
|
||||
"//pkg/sentry:internal",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/cleanup",
|
||||
"//pkg/context",
|
||||
"//pkg/devutil",
|
||||
"//pkg/errors/linuxerr",
|
||||
"//pkg/fdnotifier",
|
||||
"//pkg/hostarch",
|
||||
"//pkg/log",
|
||||
"//pkg/marshal/primitive",
|
||||
"//pkg/safemem",
|
||||
"//pkg/sentry/arch",
|
||||
"//pkg/sentry/devices/tpuproxy/util",
|
||||
"//pkg/sentry/fsimpl/eventfd",
|
||||
"//pkg/sentry/kernel",
|
||||
"//pkg/sentry/memmap",
|
||||
"//pkg/sentry/mm",
|
||||
"//pkg/sentry/vfs",
|
||||
"//pkg/sync",
|
||||
"//pkg/usermem",
|
||||
"//pkg/waiter",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_template_instance(
|
||||
name = "devaddr_range",
|
||||
out = "devaddr_range.go",
|
||||
package = "vfio",
|
||||
prefix = "DevAddr",
|
||||
template = "//pkg/segment:generic_range",
|
||||
types = {
|
||||
"T": "uint64",
|
||||
},
|
||||
)
|
||||
|
||||
go_template_instance(
|
||||
name = "devaddr_set",
|
||||
out = "devaddr_set.go",
|
||||
imports = {
|
||||
"mm": "gvisor.dev/gvisor/pkg/sentry/mm",
|
||||
},
|
||||
package = "vfio",
|
||||
prefix = "DevAddr",
|
||||
template = "//pkg/segment:generic_set",
|
||||
types = {
|
||||
"Key": "uint64",
|
||||
"Range": "DevAddrRange",
|
||||
"Value": "mm.PinnedRange",
|
||||
"Functions": "devAddrSetFuncs",
|
||||
},
|
||||
)
|
||||
+9
-210
@@ -12,8 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package tpuproxy implements proxying for TPU devices.
|
||||
package tpuproxy
|
||||
package vfio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -26,160 +25,14 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
"gvisor.dev/gvisor/pkg/marshal/primitive"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/devices/tpuproxy/util"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/eventfd"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/mm"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/usermem"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
const (
|
||||
// A value of -1 can be used to either de-assign interrupts if already
|
||||
// assigned or skip un-assigned interrupts.
|
||||
disableInterrupt = -1
|
||||
)
|
||||
|
||||
var (
|
||||
// vfioDeviceInfoFlags contains all available flags for
|
||||
// IOCTL command VFIO_DEVICE_GET_INFO.
|
||||
vfioDeviceInfoFlags uint32 = linux.VFIO_DEVICE_FLAGS_RESET | linux.VFIO_DEVICE_FLAGS_PCI |
|
||||
linux.VFIO_DEVICE_FLAGS_PLATFORM | linux.VFIO_DEVICE_FLAGS_AMBA |
|
||||
linux.VFIO_DEVICE_FLAGS_CCW | linux.VFIO_DEVICE_FLAGS_AP | linux.VFIO_DEVICE_FLAGS_FSL_MC |
|
||||
linux.VFIO_DEVICE_FLAGS_CAPS | linux.VFIO_DEVICE_FLAGS_CDX
|
||||
// vfioIrqSetFlags includes all available flags for IOCTL comamnd VFIO_DEVICE_SET_IRQS
|
||||
vfioIrqSetFlags uint32 = linux.VFIO_IRQ_SET_DATA_TYPE_MASK | linux.VFIO_IRQ_SET_ACTION_TYPE_MASK
|
||||
)
|
||||
|
||||
// tpuFD implements vfs.FileDescriptionImpl for /dev/vfio/[0-9]+
|
||||
//
|
||||
// tpuFD is not savable until TPU save/restore is needed.
|
||||
type tpuFD struct {
|
||||
vfsfd vfs.FileDescription
|
||||
vfs.FileDescriptionDefaultImpl
|
||||
vfs.DentryMetadataFileDescriptionImpl
|
||||
vfs.NoLockFD
|
||||
|
||||
hostFD int32
|
||||
device *tpuDevice
|
||||
queue waiter.Queue
|
||||
memmapFile tpuFDMemmapFile
|
||||
}
|
||||
|
||||
// Release implements vfs.FileDescriptionImpl.Release.
|
||||
func (fd *tpuFD) Release(context.Context) {
|
||||
fdnotifier.RemoveFD(fd.hostFD)
|
||||
fd.queue.Notify(waiter.EventHUp)
|
||||
unix.Close(int(fd.hostFD))
|
||||
}
|
||||
|
||||
// EventRegister implements waiter.Waitable.EventRegister.
|
||||
func (fd *tpuFD) EventRegister(e *waiter.Entry) error {
|
||||
fd.queue.EventRegister(e)
|
||||
if err := fdnotifier.UpdateFD(fd.hostFD); err != nil {
|
||||
fd.queue.EventUnregister(e)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EventUnregister implements waiter.Waitable.EventUnregister.
|
||||
func (fd *tpuFD) EventUnregister(e *waiter.Entry) {
|
||||
fd.queue.EventUnregister(e)
|
||||
if err := fdnotifier.UpdateFD(fd.hostFD); err != nil {
|
||||
panic(fmt.Sprint("UpdateFD:", err))
|
||||
}
|
||||
}
|
||||
|
||||
// Readiness implements waiter.Waitable.Readiness.
|
||||
func (fd *tpuFD) Readiness(mask waiter.EventMask) waiter.EventMask {
|
||||
return fdnotifier.NonBlockingPoll(fd.hostFD, mask)
|
||||
}
|
||||
|
||||
// Epollable implements vfs.FileDescriptionImpl.Epollable.
|
||||
func (fd *tpuFD) Epollable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Ioctl implements vfs.FileDescriptionImpl.Ioctl.
|
||||
func (fd *tpuFD) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) {
|
||||
cmd := args[1].Uint()
|
||||
|
||||
t := kernel.TaskFromContext(ctx)
|
||||
if t == nil {
|
||||
panic("Ioctl should be called from a task context")
|
||||
}
|
||||
switch cmd {
|
||||
case linux.VFIO_GROUP_SET_CONTAINER:
|
||||
return fd.setContainer(ctx, t, args[2].Pointer())
|
||||
case linux.VFIO_GROUP_GET_DEVICE_FD:
|
||||
ret, cleanup, err := fd.getPciDeviceFd(t, args[2].Pointer())
|
||||
defer cleanup()
|
||||
return ret, err
|
||||
}
|
||||
return 0, linuxerr.ENOSYS
|
||||
}
|
||||
|
||||
func (fd *tpuFD) setContainer(ctx context.Context, t *kernel.Task, arg hostarch.Addr) (uintptr, error) {
|
||||
var vfioContainerFD int32
|
||||
if _, err := primitive.CopyInt32In(t, arg, &vfioContainerFD); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
vfioContainerFile, _ := t.FDTable().Get(vfioContainerFD)
|
||||
if vfioContainerFile == nil {
|
||||
return 0, linuxerr.EBADF
|
||||
}
|
||||
defer vfioContainerFile.DecRef(ctx)
|
||||
vfioContainer, ok := vfioContainerFile.Impl().(*vfioFD)
|
||||
if !ok {
|
||||
return 0, linuxerr.EINVAL
|
||||
}
|
||||
return IOCTLInvokePtrArg[uint32](fd.hostFD, linux.VFIO_GROUP_SET_CONTAINER, &vfioContainer.hostFD)
|
||||
}
|
||||
|
||||
// It will be the caller's responsibility to call the returned cleanup function.
|
||||
func (fd *tpuFD) getPciDeviceFd(t *kernel.Task, arg hostarch.Addr) (uintptr, func(), error) {
|
||||
pciAddress, err := t.CopyInString(arg, hostarch.PageSize)
|
||||
if err != nil {
|
||||
return 0, func() {}, err
|
||||
}
|
||||
// Build a NUL-terminated slice of bytes containing the PCI address.
|
||||
pciAddressBytes, err := unix.ByteSliceFromString(pciAddress)
|
||||
if err != nil {
|
||||
return 0, func() {}, err
|
||||
}
|
||||
// Pass the address of the PCI address' first byte which can be
|
||||
// recognized by the IOCTL syscall.
|
||||
hostFD, err := IOCTLInvokePtrArg[uint32](fd.hostFD, linux.VFIO_GROUP_GET_DEVICE_FD, &pciAddressBytes[0])
|
||||
if err != nil {
|
||||
return 0, func() {}, err
|
||||
}
|
||||
pciDevFD := &pciDeviceFD{
|
||||
hostFD: int32(hostFD),
|
||||
}
|
||||
cleanup := func() {
|
||||
unix.Close(int(hostFD))
|
||||
}
|
||||
// See drivers/vfio/group.c:vfio_device_open_file(), the PCI device
|
||||
// is accessed for both reads and writes.
|
||||
vd := t.Kernel().VFS().NewAnonVirtualDentry("[vfio-device]")
|
||||
if err := pciDevFD.vfsfd.Init(pciDevFD, linux.O_RDWR, vd.Mount(), vd.Dentry(), &vfs.FileDescriptionOptions{
|
||||
UseDentryMetadata: true,
|
||||
}); err != nil {
|
||||
return 0, cleanup, err
|
||||
}
|
||||
if err := fdnotifier.AddFD(int32(hostFD), &fd.queue); err != nil {
|
||||
return 0, cleanup, err
|
||||
}
|
||||
newFD, err := t.NewFDFrom(0, &pciDevFD.vfsfd, kernel.FDFlags{})
|
||||
if err != nil {
|
||||
return 0, cleanup, err
|
||||
}
|
||||
// Initialize a mapping that is backed by a host FD.
|
||||
pciDevFD.memmapFile.fd = pciDevFD
|
||||
return uintptr(newFD), func() {}, nil
|
||||
}
|
||||
|
||||
// pciDeviceFD implements vfs.FileDescriptionImpl for TPU's PCI device.
|
||||
type pciDeviceFD struct {
|
||||
vfsfd vfs.FileDescription
|
||||
@@ -247,7 +100,7 @@ func (fd *pciDeviceFD) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr,
|
||||
return fd.vfioSetIrqs(ctx, t, args[2].Pointer())
|
||||
case linux.VFIO_DEVICE_RESET:
|
||||
// VFIO_DEVICE_RESET is just a simple IOCTL command that carries no data.
|
||||
return IOCTLInvoke[uint32, uintptr](fd.hostFD, linux.VFIO_DEVICE_RESET, 0)
|
||||
return util.IOCTLInvoke[uint32, uintptr](fd.hostFD, linux.VFIO_DEVICE_RESET, 0)
|
||||
}
|
||||
return 0, linuxerr.ENOSYS
|
||||
}
|
||||
@@ -262,7 +115,7 @@ func (fd *pciDeviceFD) vfioRegionInfo(ctx context.Context, t *kernel.Task, arg h
|
||||
if regionInfo.Argsz == 0 {
|
||||
return 0, linuxerr.EINVAL
|
||||
}
|
||||
ret, err := IOCTLInvokePtrArg[uint32](fd.hostFD, linux.VFIO_DEVICE_GET_REGION_INFO, ®ionInfo)
|
||||
ret, err := util.IOCTLInvokePtrArg[uint32](fd.hostFD, linux.VFIO_DEVICE_GET_REGION_INFO, ®ionInfo)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -285,7 +138,7 @@ func (fd *pciDeviceFD) vfioDeviceInfo(ctx context.Context, t *kernel.Task, arg h
|
||||
if deviceInfo.Flags&^vfioDeviceInfoFlags != 0 {
|
||||
return 0, linuxerr.EINVAL
|
||||
}
|
||||
ret, err := IOCTLInvokePtrArg[uint32](fd.hostFD, linux.VFIO_DEVICE_GET_INFO, &deviceInfo)
|
||||
ret, err := util.IOCTLInvokePtrArg[uint32](fd.hostFD, linux.VFIO_DEVICE_GET_INFO, &deviceInfo)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -308,7 +161,7 @@ func (fd *pciDeviceFD) vfioIrqInfo(ctx context.Context, t *kernel.Task, arg host
|
||||
if irqInfo.Argsz == 0 {
|
||||
return 0, linuxerr.EINVAL
|
||||
}
|
||||
ret, err := IOCTLInvokePtrArg[uint32](fd.hostFD, linux.VFIO_DEVICE_GET_IRQ_INFO, &irqInfo)
|
||||
ret, err := util.IOCTLInvokePtrArg[uint32](fd.hostFD, linux.VFIO_DEVICE_GET_IRQ_INFO, &irqInfo)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -349,7 +202,7 @@ func (fd *pciDeviceFD) vfioSetIrqs(ctx context.Context, t *kernel.Task, arg host
|
||||
case linux.VFIO_IRQ_SET_DATA_NONE:
|
||||
// When there is no data, passing through the given payload
|
||||
// works just fine.
|
||||
return IOCTLInvokePtrArg[uint32](fd.hostFD, linux.VFIO_DEVICE_SET_IRQS, &irqSet)
|
||||
return util.IOCTLInvokePtrArg[uint32](fd.hostFD, linux.VFIO_DEVICE_SET_IRQS, &irqSet)
|
||||
// VFIO_IRQ_SET_DATA_BOOL indicates that the data field is an array of uint8.
|
||||
// The action will be performed if the corresponding boolean is true.
|
||||
case linux.VFIO_IRQ_SET_DATA_BOOL:
|
||||
@@ -358,7 +211,7 @@ func (fd *pciDeviceFD) vfioSetIrqs(ctx context.Context, t *kernel.Task, arg host
|
||||
if _, err := primitive.CopyUint8SliceIn(t, arg, payload); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return IOCTLInvokePtrArg[uint32](fd.hostFD, linux.VFIO_DEVICE_SET_IRQS, &payload[0])
|
||||
return util.IOCTLInvokePtrArg[uint32](fd.hostFD, linux.VFIO_DEVICE_SET_IRQS, &payload[0])
|
||||
// VFIO_IRQ_SET_DATA_EVENTFD indicates that the data field is an array
|
||||
// of int32 (or event file descriptors). These descriptors will be
|
||||
// signalled when an action in the flags happens.
|
||||
@@ -391,7 +244,7 @@ func (fd *pciDeviceFD) vfioSetIrqs(ctx context.Context, t *kernel.Task, arg host
|
||||
}
|
||||
payload[index] = int32(eventfd)
|
||||
}
|
||||
return IOCTLInvokePtrArg[uint32](fd.hostFD, linux.VFIO_DEVICE_SET_IRQS, &payload[0])
|
||||
return util.IOCTLInvokePtrArg[uint32](fd.hostFD, linux.VFIO_DEVICE_SET_IRQS, &payload[0])
|
||||
}
|
||||
// No data type is specified or multiple data types are specified.
|
||||
return 0, linuxerr.EINVAL
|
||||
@@ -424,57 +277,3 @@ func (fd *pciDeviceFD) PWrite(ctx context.Context, src usermem.IOSequence, offse
|
||||
n, err := unix.Pwrite(int(fd.hostFD), buf, offset)
|
||||
return int64(n), err
|
||||
}
|
||||
|
||||
// DevAddrSet tracks device address ranges that have been mapped.
|
||||
type devAddrSetFuncs struct{}
|
||||
|
||||
func (devAddrSetFuncs) MinKey() uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (devAddrSetFuncs) MaxKey() uint64 {
|
||||
return ^uint64(0)
|
||||
}
|
||||
|
||||
func (devAddrSetFuncs) ClearValue(val *mm.PinnedRange) {
|
||||
*val = mm.PinnedRange{}
|
||||
}
|
||||
|
||||
func (devAddrSetFuncs) Merge(r1 DevAddrRange, v1 mm.PinnedRange, r2 DevAddrRange, v2 mm.PinnedRange) (mm.PinnedRange, bool) {
|
||||
// Do we have the same backing file?
|
||||
if v1.File != v2.File {
|
||||
return mm.PinnedRange{}, false
|
||||
}
|
||||
|
||||
// Do we have contiguous offsets in the backing file?
|
||||
if v1.Offset+uint64(v1.Source.Length()) != v2.Offset {
|
||||
return mm.PinnedRange{}, false
|
||||
}
|
||||
|
||||
// Are the virtual addresses contiguous?
|
||||
//
|
||||
// This check isn't strictly needed because 'mm.PinnedRange.Source'
|
||||
// is only used to track the size of the pinned region (this is
|
||||
// because the virtual address range can be unmapped or remapped
|
||||
// elsewhere). Regardless we require this for simplicity.
|
||||
if v1.Source.End != v2.Source.Start {
|
||||
return mm.PinnedRange{}, false
|
||||
}
|
||||
|
||||
// Extend v1 to account for the adjacent PinnedRange.
|
||||
v1.Source.End = v2.Source.End
|
||||
return v1, true
|
||||
}
|
||||
|
||||
func (devAddrSetFuncs) Split(r DevAddrRange, val mm.PinnedRange, split uint64) (mm.PinnedRange, mm.PinnedRange) {
|
||||
n := split - r.Start
|
||||
|
||||
left := val
|
||||
left.Source.End = left.Source.Start + hostarch.Addr(n)
|
||||
|
||||
right := val
|
||||
right.Source.Start += hostarch.Addr(n)
|
||||
right.Offset += n
|
||||
|
||||
return left, right
|
||||
}
|
||||
+1
-62
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tpuproxy
|
||||
package vfio
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
@@ -24,67 +24,6 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
)
|
||||
|
||||
// ConfigureMMap implements vfs.FileDescriptionImpl.ConfigureMMap.
|
||||
func (fd *tpuFD) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error {
|
||||
return vfs.GenericConfigureMMap(&fd.vfsfd, fd, opts)
|
||||
}
|
||||
|
||||
// AddMapping implements memmap.Mappable.AddMapping.
|
||||
func (fd *tpuFD) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveMapping implements memmap.Mappable.RemoveMapping.
|
||||
func (fd *tpuFD) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) {
|
||||
}
|
||||
|
||||
// CopyMapping implements memmap.Mappable.CopyMapping.
|
||||
func (fd *tpuFD) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR hostarch.AddrRange, offset uint64, writable bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Translate implements memmap.Mappable.Translate.
|
||||
func (fd *tpuFD) Translate(ctx context.Context, required, optional memmap.MappableRange, at hostarch.AccessType) ([]memmap.Translation, error) {
|
||||
return []memmap.Translation{
|
||||
{
|
||||
Source: optional,
|
||||
File: &fd.memmapFile,
|
||||
Offset: optional.Start,
|
||||
Perms: at,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// InvalidateUnsavable implements memmap.Mappable.InvalidateUnsavable.
|
||||
func (fd *tpuFD) InvalidateUnsavable(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type tpuFDMemmapFile struct {
|
||||
memmap.NoBufferedIOFallback
|
||||
|
||||
fd *tpuFD
|
||||
}
|
||||
|
||||
// IncRef implements memmap.File.IncRef.
|
||||
func (mf *tpuFDMemmapFile) IncRef(memmap.FileRange, uint32) {
|
||||
}
|
||||
|
||||
// DecRef implements memmap.File.DecRef.
|
||||
func (mf *tpuFDMemmapFile) DecRef(fr memmap.FileRange) {
|
||||
}
|
||||
|
||||
// MapInternal implements memmap.File.MapInternal.
|
||||
func (mf *tpuFDMemmapFile) MapInternal(fr memmap.FileRange, at hostarch.AccessType) (safemem.BlockSeq, error) {
|
||||
log.Traceback("tpuproxy: rejecting tpuFdMemmapFile.MapInternal")
|
||||
return safemem.BlockSeq{}, linuxerr.EINVAL
|
||||
}
|
||||
|
||||
// FD implements memmap.File.FD.
|
||||
func (mf *tpuFDMemmapFile) FD() int {
|
||||
return int(mf.fd.hostFD)
|
||||
}
|
||||
|
||||
// ConfigureMMap implements vfs.FileDescriptionImpl.ConfigureMMap.
|
||||
func (fd *pciDeviceFD) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error {
|
||||
return vfs.GenericConfigureMMap(&fd.vfsfd, fd, opts)
|
||||
@@ -0,0 +1,234 @@
|
||||
// Copyright 2023 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 vfio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/fdnotifier"
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
"gvisor.dev/gvisor/pkg/marshal/primitive"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/devices/tpuproxy/util"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/mm"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/usermem"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
const (
|
||||
// A value of -1 can be used to either de-assign interrupts if already
|
||||
// assigned or skip un-assigned interrupts.
|
||||
disableInterrupt = -1
|
||||
)
|
||||
|
||||
var (
|
||||
// vfioDeviceInfoFlags contains all available flags for
|
||||
// IOCTL command VFIO_DEVICE_GET_INFO.
|
||||
vfioDeviceInfoFlags uint32 = linux.VFIO_DEVICE_FLAGS_RESET | linux.VFIO_DEVICE_FLAGS_PCI |
|
||||
linux.VFIO_DEVICE_FLAGS_PLATFORM | linux.VFIO_DEVICE_FLAGS_AMBA |
|
||||
linux.VFIO_DEVICE_FLAGS_CCW | linux.VFIO_DEVICE_FLAGS_AP | linux.VFIO_DEVICE_FLAGS_FSL_MC |
|
||||
linux.VFIO_DEVICE_FLAGS_CAPS | linux.VFIO_DEVICE_FLAGS_CDX
|
||||
// vfioIrqSetFlags includes all available flags for IOCTL command VFIO_DEVICE_SET_IRQS
|
||||
vfioIrqSetFlags uint32 = linux.VFIO_IRQ_SET_DATA_TYPE_MASK | linux.VFIO_IRQ_SET_ACTION_TYPE_MASK
|
||||
)
|
||||
|
||||
// tpuFD implements vfs.FileDescriptionImpl for /dev/vfio/[0-9]+
|
||||
//
|
||||
// tpuFD is not savable until TPU save/restore is needed.
|
||||
type tpuFD struct {
|
||||
vfsfd vfs.FileDescription
|
||||
vfs.FileDescriptionDefaultImpl
|
||||
vfs.DentryMetadataFileDescriptionImpl
|
||||
vfs.NoLockFD
|
||||
|
||||
hostFD int32
|
||||
device *tpuDevice
|
||||
queue waiter.Queue
|
||||
memmapFile tpuFDMemmapFile
|
||||
}
|
||||
|
||||
// Release implements vfs.FileDescriptionImpl.Release.
|
||||
func (fd *tpuFD) Release(context.Context) {
|
||||
fdnotifier.RemoveFD(fd.hostFD)
|
||||
fd.queue.Notify(waiter.EventHUp)
|
||||
unix.Close(int(fd.hostFD))
|
||||
}
|
||||
|
||||
// EventRegister implements waiter.Waitable.EventRegister.
|
||||
func (fd *tpuFD) EventRegister(e *waiter.Entry) error {
|
||||
fd.queue.EventRegister(e)
|
||||
if err := fdnotifier.UpdateFD(fd.hostFD); err != nil {
|
||||
fd.queue.EventUnregister(e)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EventUnregister implements waiter.Waitable.EventUnregister.
|
||||
func (fd *tpuFD) EventUnregister(e *waiter.Entry) {
|
||||
fd.queue.EventUnregister(e)
|
||||
if err := fdnotifier.UpdateFD(fd.hostFD); err != nil {
|
||||
panic(fmt.Sprint("UpdateFD:", err))
|
||||
}
|
||||
}
|
||||
|
||||
// Readiness implements waiter.Waitable.Readiness.
|
||||
func (fd *tpuFD) Readiness(mask waiter.EventMask) waiter.EventMask {
|
||||
return fdnotifier.NonBlockingPoll(fd.hostFD, mask)
|
||||
}
|
||||
|
||||
// Epollable implements vfs.FileDescriptionImpl.Epollable.
|
||||
func (fd *tpuFD) Epollable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Ioctl implements vfs.FileDescriptionImpl.Ioctl.
|
||||
func (fd *tpuFD) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) {
|
||||
cmd := args[1].Uint()
|
||||
|
||||
t := kernel.TaskFromContext(ctx)
|
||||
if t == nil {
|
||||
panic("Ioctl should be called from a task context")
|
||||
}
|
||||
switch cmd {
|
||||
case linux.VFIO_GROUP_SET_CONTAINER:
|
||||
return fd.setContainer(ctx, t, args[2].Pointer())
|
||||
case linux.VFIO_GROUP_GET_DEVICE_FD:
|
||||
ret, cleanup, err := fd.getPciDeviceFd(t, args[2].Pointer())
|
||||
defer cleanup()
|
||||
return ret, err
|
||||
}
|
||||
return 0, linuxerr.ENOSYS
|
||||
}
|
||||
|
||||
func (fd *tpuFD) setContainer(ctx context.Context, t *kernel.Task, arg hostarch.Addr) (uintptr, error) {
|
||||
var vfioContainerFD int32
|
||||
if _, err := primitive.CopyInt32In(t, arg, &vfioContainerFD); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
vfioContainerFile, _ := t.FDTable().Get(vfioContainerFD)
|
||||
if vfioContainerFile == nil {
|
||||
return 0, linuxerr.EBADF
|
||||
}
|
||||
defer vfioContainerFile.DecRef(ctx)
|
||||
vfioContainer, ok := vfioContainerFile.Impl().(*vfioFD)
|
||||
if !ok {
|
||||
return 0, linuxerr.EINVAL
|
||||
}
|
||||
return util.IOCTLInvokePtrArg[uint32](fd.hostFD, linux.VFIO_GROUP_SET_CONTAINER, &vfioContainer.hostFD)
|
||||
}
|
||||
|
||||
// It will be the caller's responsibility to call the returned cleanup function.
|
||||
func (fd *tpuFD) getPciDeviceFd(t *kernel.Task, arg hostarch.Addr) (uintptr, func(), error) {
|
||||
pciAddress, err := t.CopyInString(arg, hostarch.PageSize)
|
||||
if err != nil {
|
||||
return 0, func() {}, err
|
||||
}
|
||||
// Build a NUL-terminated slice of bytes containing the PCI address.
|
||||
pciAddressBytes, err := unix.ByteSliceFromString(pciAddress)
|
||||
if err != nil {
|
||||
return 0, func() {}, err
|
||||
}
|
||||
// Pass the address of the PCI address' first byte which can be
|
||||
// recognized by the IOCTL syscall.
|
||||
hostFD, err := util.IOCTLInvokePtrArg[uint32](fd.hostFD, linux.VFIO_GROUP_GET_DEVICE_FD, &pciAddressBytes[0])
|
||||
if err != nil {
|
||||
return 0, func() {}, err
|
||||
}
|
||||
pciDevFD := &pciDeviceFD{
|
||||
hostFD: int32(hostFD),
|
||||
}
|
||||
cleanup := func() {
|
||||
unix.Close(int(hostFD))
|
||||
}
|
||||
// See drivers/vfio/group.c:vfio_device_open_file(), the PCI device
|
||||
// is accessed for both reads and writes.
|
||||
vd := t.Kernel().VFS().NewAnonVirtualDentry("[vfio-device]")
|
||||
if err := pciDevFD.vfsfd.Init(pciDevFD, linux.O_RDWR, vd.Mount(), vd.Dentry(), &vfs.FileDescriptionOptions{
|
||||
UseDentryMetadata: true,
|
||||
}); err != nil {
|
||||
return 0, cleanup, err
|
||||
}
|
||||
if err := fdnotifier.AddFD(int32(hostFD), &fd.queue); err != nil {
|
||||
return 0, cleanup, err
|
||||
}
|
||||
newFD, err := t.NewFDFrom(0, &pciDevFD.vfsfd, kernel.FDFlags{})
|
||||
if err != nil {
|
||||
return 0, cleanup, err
|
||||
}
|
||||
// Initialize a mapping that is backed by a host FD.
|
||||
pciDevFD.memmapFile.fd = pciDevFD
|
||||
return uintptr(newFD), func() {}, nil
|
||||
}
|
||||
|
||||
// DevAddrSet tracks device address ranges that have been mapped.
|
||||
type devAddrSetFuncs struct{}
|
||||
|
||||
func (devAddrSetFuncs) MinKey() uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (devAddrSetFuncs) MaxKey() uint64 {
|
||||
return ^uint64(0)
|
||||
}
|
||||
|
||||
func (devAddrSetFuncs) ClearValue(val *mm.PinnedRange) {
|
||||
*val = mm.PinnedRange{}
|
||||
}
|
||||
|
||||
func (devAddrSetFuncs) Merge(r1 DevAddrRange, v1 mm.PinnedRange, r2 DevAddrRange, v2 mm.PinnedRange) (mm.PinnedRange, bool) {
|
||||
// Do we have the same backing file?
|
||||
if v1.File != v2.File {
|
||||
return mm.PinnedRange{}, false
|
||||
}
|
||||
|
||||
// Do we have contiguous offsets in the backing file?
|
||||
if v1.Offset+uint64(v1.Source.Length()) != v2.Offset {
|
||||
return mm.PinnedRange{}, false
|
||||
}
|
||||
|
||||
// Are the virtual addresses contiguous?
|
||||
//
|
||||
// This check isn't strictly needed because 'mm.PinnedRange.Source'
|
||||
// is only used to track the size of the pinned region (this is
|
||||
// because the virtual address range can be unmapped or remapped
|
||||
// elsewhere). Regardless we require this for simplicity.
|
||||
if v1.Source.End != v2.Source.Start {
|
||||
return mm.PinnedRange{}, false
|
||||
}
|
||||
|
||||
// Extend v1 to account for the adjacent PinnedRange.
|
||||
v1.Source.End = v2.Source.End
|
||||
return v1, true
|
||||
}
|
||||
|
||||
func (devAddrSetFuncs) Split(r DevAddrRange, val mm.PinnedRange, split uint64) (mm.PinnedRange, mm.PinnedRange) {
|
||||
n := split - r.Start
|
||||
|
||||
left := val
|
||||
left.Source.End = left.Source.Start + hostarch.Addr(n)
|
||||
|
||||
right := val
|
||||
right.Source.Start += hostarch.Addr(n)
|
||||
right.Offset += n
|
||||
|
||||
return left, right
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// 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 vfio
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/safemem"
|
||||
"gvisor.dev/gvisor/pkg/sentry/memmap"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
)
|
||||
|
||||
// ConfigureMMap implements vfs.FileDescriptionImpl.ConfigureMMap.
|
||||
func (fd *tpuFD) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error {
|
||||
return vfs.GenericConfigureMMap(&fd.vfsfd, fd, opts)
|
||||
}
|
||||
|
||||
// AddMapping implements memmap.Mappable.AddMapping.
|
||||
func (fd *tpuFD) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveMapping implements memmap.Mappable.RemoveMapping.
|
||||
func (fd *tpuFD) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) {
|
||||
}
|
||||
|
||||
// CopyMapping implements memmap.Mappable.CopyMapping.
|
||||
func (fd *tpuFD) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR hostarch.AddrRange, offset uint64, writable bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Translate implements memmap.Mappable.Translate.
|
||||
func (fd *tpuFD) Translate(ctx context.Context, required, optional memmap.MappableRange, at hostarch.AccessType) ([]memmap.Translation, error) {
|
||||
return []memmap.Translation{
|
||||
{
|
||||
Source: optional,
|
||||
File: &fd.memmapFile,
|
||||
Offset: optional.Start,
|
||||
Perms: at,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// InvalidateUnsavable implements memmap.Mappable.InvalidateUnsavable.
|
||||
func (fd *tpuFD) InvalidateUnsavable(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type tpuFDMemmapFile struct {
|
||||
memmap.NoBufferedIOFallback
|
||||
|
||||
fd *tpuFD
|
||||
}
|
||||
|
||||
// IncRef implements memmap.File.IncRef.
|
||||
func (mf *tpuFDMemmapFile) IncRef(memmap.FileRange, uint32) {
|
||||
}
|
||||
|
||||
// DecRef implements memmap.File.DecRef.
|
||||
func (mf *tpuFDMemmapFile) DecRef(fr memmap.FileRange) {
|
||||
}
|
||||
|
||||
// MapInternal implements memmap.File.MapInternal.
|
||||
func (mf *tpuFDMemmapFile) MapInternal(fr memmap.FileRange, at hostarch.AccessType) (safemem.BlockSeq, error) {
|
||||
log.Traceback("tpuproxy: rejecting tpuFdMemmapFile.MapInternal")
|
||||
return safemem.BlockSeq{}, linuxerr.EINVAL
|
||||
}
|
||||
|
||||
// FD implements memmap.File.FD.
|
||||
func (mf *tpuFDMemmapFile) FD() int {
|
||||
return int(mf.fd.hostFD)
|
||||
}
|
||||
@@ -12,11 +12,12 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tpuproxy
|
||||
package vfio
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
@@ -33,12 +34,11 @@ const (
|
||||
// VFIO_MINOR is the VFIO minor number from include/linux/miscdevice.h.
|
||||
VFIO_MINOR = 196
|
||||
|
||||
// VFIOPath is the path to a VFIO device, it is usually used to
|
||||
// construct a VFIO container.
|
||||
VFIOPath = "/dev/vfio/vfio"
|
||||
|
||||
tpuDeviceGroupName = "vfio"
|
||||
vfioDeviceGroupName = "vfio"
|
||||
|
||||
// VFIOPath is the valid path to a VFIO device.
|
||||
VFIOPath = "/dev/vfio/vfio"
|
||||
)
|
||||
|
||||
// device implements TPU's vfs.Device for /dev/vfio/[0-9]+
|
||||
@@ -97,7 +97,7 @@ func (dev *vfioDevice) Open(ctx context.Context, mnt *vfs.Mount, d *vfs.Dentry,
|
||||
return nil, linuxerr.ENOENT
|
||||
}
|
||||
|
||||
name := filepath.Join("vfio", filepath.Base(VFIOPath))
|
||||
name := strings.ReplaceAll(VFIOPath, "/dev/", "")
|
||||
hostFD, err := client.OpenAt(ctx, name, opts.Flags)
|
||||
if err != nil {
|
||||
ctx.Warningf("failed to open host file %s: %v", name, err)
|
||||
@@ -131,8 +131,8 @@ func RegisterTPUDevice(vfsObj *vfs.VirtualFilesystem, minor, deviceNum uint32) e
|
||||
})
|
||||
}
|
||||
|
||||
// RegisterVfioDevice registers VFIO devices that are implemented by this package in vfsObj.
|
||||
func RegisterVfioDevice(vfsObj *vfs.VirtualFilesystem) error {
|
||||
// RegisterVFIODevice registers VFIO devices that are implemented by this package in vfsObj.
|
||||
func RegisterVFIODevice(vfsObj *vfs.VirtualFilesystem) error {
|
||||
return vfsObj.RegisterDevice(vfs.CharDevice, linux.MISC_MAJOR, VFIO_MINOR, &vfioDevice{}, &vfs.RegisterDeviceOptions{
|
||||
GroupName: vfioDeviceGroupName,
|
||||
})
|
||||
@@ -12,7 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tpuproxy
|
||||
// Package vfio implements a proxy for VFIO devices.
|
||||
package vfio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -27,6 +28,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/devices/tpuproxy/util"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/memmap"
|
||||
"gvisor.dev/gvisor/pkg/sentry/mm"
|
||||
@@ -113,7 +115,7 @@ func (fd *vfioFD) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args
|
||||
func (fd *vfioFD) checkExtension(ext extension) (uintptr, error) {
|
||||
switch ext {
|
||||
case linux.VFIO_TYPE1_IOMMU, linux.VFIO_SPAPR_TCE_IOMMU, linux.VFIO_TYPE1v2_IOMMU:
|
||||
ret, err := IOCTLInvoke[uint32, int32](fd.hostFD, linux.VFIO_CHECK_EXTENSION, int32(ext))
|
||||
ret, err := util.IOCTLInvoke[uint32, int32](fd.hostFD, linux.VFIO_CHECK_EXTENSION, int32(ext))
|
||||
if err != nil {
|
||||
log.Warningf("check VFIO extension %s: %v", ext, err)
|
||||
return 0, err
|
||||
@@ -128,7 +130,7 @@ func (fd *vfioFD) checkExtension(ext extension) (uintptr, error) {
|
||||
func (fd *vfioFD) setIOMMU(ext extension) (uintptr, error) {
|
||||
switch ext {
|
||||
case linux.VFIO_TYPE1_IOMMU, linux.VFIO_SPAPR_TCE_IOMMU, linux.VFIO_TYPE1v2_IOMMU:
|
||||
ret, err := IOCTLInvoke[uint32, int32](fd.hostFD, linux.VFIO_SET_IOMMU, int32(ext))
|
||||
ret, err := util.IOCTLInvoke[uint32, int32](fd.hostFD, linux.VFIO_SET_IOMMU, int32(ext))
|
||||
if err != nil {
|
||||
log.Warningf("set the IOMMU group to %s: %v", ext, err)
|
||||
return 0, err
|
||||
@@ -196,7 +198,7 @@ func (fd *vfioFD) iommuMapDma(ctx context.Context, t *kernel.Task, arg hostarch.
|
||||
}
|
||||
// Replace Vaddr with the host's virtual address.
|
||||
dmaMap.Vaddr = uint64(m)
|
||||
n, err := IOCTLInvokePtrArg[uint32](fd.hostFD, linux.VFIO_IOMMU_MAP_DMA, &dmaMap)
|
||||
n, err := util.IOCTLInvokePtrArg[uint32](fd.hostFD, linux.VFIO_IOMMU_MAP_DMA, &dmaMap)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
@@ -228,7 +230,7 @@ func (fd *vfioFD) iommuUnmapDma(ctx context.Context, t *kernel.Task, arg hostarc
|
||||
// gVisor working with TPU.
|
||||
return 0, linuxerr.ENOSYS
|
||||
}
|
||||
n, err := IOCTLInvokePtrArg[uint32](fd.hostFD, linux.VFIO_IOMMU_UNMAP_DMA, &dmaUnmap)
|
||||
n, err := util.IOCTLInvokePtrArg[uint32](fd.hostFD, linux.VFIO_IOMMU_UNMAP_DMA, &dmaUnmap)
|
||||
if err != nil {
|
||||
return 0, nil
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user