Implement accel fd methods and gasket ioctls.

The implementation of memory mapping and interrupt registration is
very similar to what's already been done for nvproxy.

PiperOrigin-RevId: 552644264
This commit is contained in:
Lucas Manning
2023-07-31 18:05:28 -07:00
committed by gVisor bot
parent ee8df63a0a
commit 1d4792c566
9 changed files with 504 additions and 11 deletions
+7 -7
View File
@@ -102,6 +102,13 @@ go_repository(
version = "v0.4.0",
)
go_repository(
name = "org_golang_x_exp",
importpath = "golang.org/x/exp",
sum = "h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY=",
version = "v0.0.0-20230725093048-515e97ebf090",
)
go_repository(
name = "org_golang_x_net",
importpath = "golang.org/x/net",
@@ -2297,13 +2304,6 @@ go_repository(
version = "v4.2.3",
)
go_repository(
name = "org_golang_x_exp",
importpath = "golang.org/x/exp",
sum = "h1:c2HOrn5iMezYjSlGPncknSEr/8x5LELb/ilJbXi9DEA=",
version = "v0.0.0-20190121172915-509febef88a4",
)
go_repository(
name = "org_uber_go_tools",
importpath = "go.uber.org/tools",
+2 -1
View File
@@ -26,7 +26,7 @@ require (
github.com/sirupsen/logrus v1.8.1
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54
golang.org/x/mod v0.7.0
golang.org/x/mod v0.11.0
golang.org/x/sync v0.1.0
golang.org/x/sys v0.4.0
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8
@@ -60,6 +60,7 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 // indirect
golang.org/x/net v0.5.0 // indirect
golang.org/x/oauth2 v0.4.0 // indirect
golang.org/x/term v0.4.0 // indirect
+4
View File
@@ -233,6 +233,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY=
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
@@ -241,6 +243,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+45
View File
@@ -1,3 +1,4 @@
load("//tools/go_generics:defs.bzl", "go_template_instance")
load("//tools:defs.bzl", "go_library")
licenses(["notice"])
@@ -6,21 +7,65 @@ go_library(
name = "accel",
srcs = [
"accel.go",
"accel_mmap.go",
"accel_unsafe.go",
"devaddr_range.go",
"devaddr_set.go",
"device.go",
"gasket.go",
"seccomp_filters.go",
],
visibility = ["//pkg/sentry:internal"],
deps = [
"//pkg/abi/gasket",
"//pkg/abi/linux",
"//pkg/cleanup",
"//pkg/context",
"//pkg/errors/linuxerr",
"//pkg/fdnotifier",
"//pkg/hostarch",
"//pkg/log",
"//pkg/safemem",
"//pkg/seccomp",
"//pkg/sentry/arch",
"//pkg/sentry/fsimpl/devtmpfs",
"//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_exp//constraints:go_default_library",
"@org_golang_x_sys//unix:go_default_library",
],
)
go_template_instance(
name = "devaddr_range",
out = "devaddr_range.go",
package = "accel",
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 = "accel",
prefix = "DevAddr",
template = "//pkg/segment:generic_set",
types = {
"Key": "uint64",
"Range": "DevAddrRange",
"Value": "pinnedAccelMem",
"Functions": "devAddrSetFuncs",
},
)
+141 -3
View File
@@ -16,9 +16,19 @@
package accel
import (
"fmt"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/gasket"
"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/log"
"gvisor.dev/gvisor/pkg/sentry/arch"
"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"
@@ -34,25 +44,63 @@ type accelFD struct {
vfs.DentryMetadataFileDescriptionImpl
vfs.NoLockFD
hostFD int32
hostFD int32
device *accelDevice
queue waiter.Queue
memmapFile accelFDMemmapFile
}
// Release implements vfs.FileDescriptionImpl.Release.
func (fd *accelFD) Release(context.Context) {
fd.device.mu.Lock()
defer fd.device.mu.Unlock()
fd.device.openWriteFDs--
if fd.device.openWriteFDs == 0 {
log.Infof("openWriteFDs is zero, unpinning all sentry memory mappings")
s := &fd.device.devAddrSet
seg := s.FirstSegment()
for seg.Ok() {
r, v := seg.Range(), seg.Value()
gpti := gasket.GasketPageTableIoctl{
PageTableIndex: v.pageTableIndex,
DeviceAddress: r.Start,
Size: r.End - r.Start,
HostAddress: 0,
}
_, err := ioctlInvokePtrArg(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)
}
mm.Unpin([]mm.PinnedRange{v.pinnedRange})
gap := s.Remove(seg)
seg = gap.NextSegment()
}
}
fdnotifier.RemoveFD(fd.hostFD)
unix.Close(int(fd.hostFD))
}
// EventRegister implements waiter.Waitable.EventRegister.
func (fd *accelFD) 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 *accelFD) 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 *accelFD) Readiness(mask waiter.EventMask) waiter.EventMask {
return waiter.EventErr
return fdnotifier.NonBlockingPoll(fd.hostFD, mask)
}
// Epollable implements vfs.FileDescriptionImpl.Epollable.
@@ -62,5 +110,95 @@ func (fd *accelFD) Epollable() bool {
// Ioctl implements vfs.FileDescriptionImpl.Ioctl.
func (fd *accelFD) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) {
return 0, linuxerr.ENOSYS
cmd := args[1].Uint()
argPtr := args[2].Pointer()
argSize := linux.IOC_SIZE(cmd)
t := kernel.TaskFromContext(ctx)
if t == nil {
panic("Ioctl should be called from a task context")
}
log.Infof("Accel ioctl %s called on fd %d with arg %v of size %d.", gasket.Ioctl(cmd), fd.hostFD, argPtr, argSize)
switch gasket.Ioctl(cmd) {
// Not yet implemented gasket ioctls.
case gasket.GASKET_IOCTL_SET_EVENTFD, gasket.GASKET_IOCTL_CLEAR_EVENTFD,
gasket.GASKET_IOCTL_NUMBER_PAGE_TABLES, gasket.GASKET_IOCTL_PAGE_TABLE_SIZE,
gasket.GASKET_IOCTL_SIMPLE_PAGE_TABLE_SIZE, gasket.GASKET_IOCTL_PARTITION_PAGE_TABLE,
gasket.GASKET_IOCTL_MAP_DMA_BUF:
return 0, linuxerr.ENOSYS
case gasket.GASKET_IOCTL_RESET:
return ioctlInvoke[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 ioctlInvoke(fd.hostFD, gasket.GASKET_IOCTL_CLEAR_INTERRUPT_COUNTS, 0)
case gasket.GASKET_IOCTL_REGISTER_INTERRUPT:
return gasketInterruptMappingIoctl(ctx, t, fd.hostFD, argPtr)
case gasket.GASKET_IOCTL_UNREGISTER_INTERRUPT:
return ioctlInvoke[uint64](fd.hostFD, gasket.GASKET_IOCTL_UNREGISTER_INTERRUPT, args[2].Uint64())
default:
return 0, linuxerr.EINVAL
}
}
type pinnedAccelMem struct {
pinnedRange mm.PinnedRange
pageTableIndex uint64
}
// 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 *pinnedAccelMem) {
*val = pinnedAccelMem{}
}
func (devAddrSetFuncs) Merge(r1 DevAddrRange, v1 pinnedAccelMem, r2 DevAddrRange, v2 pinnedAccelMem) (pinnedAccelMem, bool) {
// Do we have the same backing file?
if v1.pinnedRange.File != v2.pinnedRange.File {
return pinnedAccelMem{}, false
}
// Do we have contiguous offsets in the backing file?
if v1.pinnedRange.Offset+uint64(v1.pinnedRange.Source.Length()) != v2.pinnedRange.Offset {
return pinnedAccelMem{}, 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.pinnedRange.Source.End != v2.pinnedRange.Source.Start {
return pinnedAccelMem{}, false
}
// Extend v1 to account for the adjacent PinnedRange.
v1.pinnedRange.Source.End = v2.pinnedRange.Source.End
return v1, true
}
func (devAddrSetFuncs) Split(r DevAddrRange, val pinnedAccelMem, split uint64) (pinnedAccelMem, pinnedAccelMem) {
n := split - r.Start
left := val
left.pinnedRange.Source.End = left.pinnedRange.Source.Start + hostarch.Addr(n)
right := val
right.pinnedRange.Source.Start += hostarch.Addr(n)
right.pinnedRange.Offset += n
return left, right
}
+84
View File
@@ -0,0 +1,84 @@
// 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 (
"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 *accelFD) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error {
return vfs.GenericConfigureMMap(&fd.vfsfd, fd, opts)
}
// AddMapping implements memmap.Mappable.AddMapping.
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 *accelFD) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) {
}
// CopyMapping implements memmap.Mappable.CopyMapping.
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 *accelFD) 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 *accelFD) InvalidateUnsavable(ctx context.Context) error {
return nil
}
type accelFDMemmapFile struct {
fd *accelFD
}
// IncRef implements memmap.File.IncRef.
func (mf *accelFDMemmapFile) IncRef(memmap.FileRange, uint32) {
}
// DecRef implements memmap.File.DecRef.
func (mf *accelFDMemmapFile) DecRef(fr memmap.FileRange) {
}
// MapInternal implements memmap.File.MapInternal.
func (mf *accelFDMemmapFile) MapInternal(fr memmap.FileRange, at hostarch.AccessType) (safemem.BlockSeq, error) {
log.Traceback("accel: rejecting accelFDMemmapFile.MapInternal")
return safemem.BlockSeq{}, linuxerr.EINVAL
}
// FD implements memmap.File.FD.
func (mf *accelFDMemmapFile) FD() int {
return int(mf.fd.hostFD)
}
+35
View File
@@ -0,0 +1,35 @@
// 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 (
"unsafe"
"golang.org/x/exp/constraints"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/gasket"
)
func ioctlInvokePtrArg[Params any](hostFd int32, cmd gasket.Ioctl, params *Params) (uintptr, error) {
return ioctlInvoke[uintptr](hostFd, cmd, uintptr(unsafe.Pointer(params)))
}
func ioctlInvoke[Arg constraints.Integer](hostFd int32, cmd gasket.Ioctl, arg Arg) (uintptr, error) {
n, _, errno := unix.RawSyscall(unix.SYS_IOCTL, uintptr(hostFd), uintptr(cmd), uintptr(arg))
if errno != 0 {
return n, errno
}
return n, nil
}
+19
View File
@@ -20,18 +20,28 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/fdnotifier"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/devtmpfs"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/sync"
)
// accelDevice implements vfs.Device for /dev/accel[0-9]+.
//
// +stateify savable
type accelDevice struct {
mu sync.Mutex
minor uint32
// +checklocks:mu
openWriteFDs uint32
// +checklocks:mu
devAddrSet DevAddrSet
}
func (dev *accelDevice) Open(ctx context.Context, mnt *vfs.Mount, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
dev.mu.Lock()
defer dev.mu.Unlock()
hostPath := fmt.Sprintf("/dev/accel%d", dev.minor)
hostFD, err := unix.Openat(-1, hostPath, int((opts.Flags&unix.O_ACCMODE)|unix.O_NOFOLLOW), 0)
if err != nil {
@@ -40,6 +50,7 @@ func (dev *accelDevice) Open(ctx context.Context, mnt *vfs.Mount, vfsd *vfs.Dent
}
fd := &accelFD{
hostFD: int32(hostFD),
device: dev,
}
if err := fd.vfsfd.Init(fd, opts.Flags, mnt, vfsd, &vfs.FileDescriptionOptions{
UseDentryMetadata: true,
@@ -47,6 +58,14 @@ func (dev *accelDevice) Open(ctx context.Context, mnt *vfs.Mount, vfsd *vfs.Dent
unix.Close(hostFD)
return nil, err
}
if err := fdnotifier.AddFD(int32(hostFD), &fd.queue); err != nil {
unix.Close(hostFD)
return nil, err
}
fd.memmapFile.fd = fd
if vfs.MayWriteFileWithOpenFlags(opts.Flags) {
dev.openWriteFDs++
}
return &fd.vfsfd, nil
}
+167
View File
@@ -0,0 +1,167 @@
// 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 (
"fmt"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/gasket"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/cleanup"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"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 *accelFD, paramsAddr hostarch.Addr) (uintptr, error) {
var userIoctlParams gasket.GasketPageTableIoctl
if _, err := userIoctlParams.CopyIn(t, paramsAddr); err != nil {
return 0, err
}
tmm := t.MemoryManager()
ar, ok := tmm.CheckIORange(hostarch.Addr(userIoctlParams.HostAddress), int64(userIoctlParams.Size))
if !ok {
return 0, linuxerr.EFAULT
}
if !ar.IsPageAligned() || (userIoctlParams.Size/hostarch.PageSize) == 0 {
return 0, linuxerr.EINVAL
}
// Reserve a range in our address space.
m, _, errno := unix.RawSyscall6(unix.SYS_MMAP, 0 /* addr */, uintptr(ar.Length()), unix.PROT_NONE, unix.MAP_PRIVATE|unix.MAP_ANONYMOUS, ^uintptr(0) /* fd */, 0 /* offset */)
if errno != 0 {
return 0, errno
}
cu := cleanup.Make(func() {
unix.RawSyscall(unix.SYS_MUNMAP, m, uintptr(ar.Length()), 0)
})
defer cu.Clean()
// Mirror application mappings into the reserved range.
prs, err := t.MemoryManager().Pin(ctx, ar, hostarch.ReadWrite, false /* ignorePermissions */)
cu.Add(func() {
mm.Unpin(prs)
})
if err != nil {
return 0, err
}
sentryAddr := uintptr(m)
for _, pr := range prs {
ims, err := pr.File.MapInternal(memmap.FileRange{pr.Offset, pr.Offset + uint64(pr.Source.Length())}, hostarch.ReadWrite)
if err != nil {
return 0, err
}
for !ims.IsEmpty() {
im := ims.Head()
if _, _, errno := unix.RawSyscall6(unix.SYS_MREMAP, im.Addr(), 0 /* old_size */, uintptr(im.Len()), linux.MREMAP_MAYMOVE|linux.MREMAP_FIXED, sentryAddr, 0); errno != 0 {
return 0, errno
}
sentryAddr += uintptr(im.Len())
ims = ims.Tail()
}
}
sentryIoctlParams := userIoctlParams
sentryIoctlParams.HostAddress = uint64(m)
n, err := ioctlInvokePtrArg(hostFd, gasket.GASKET_IOCTL_MAP_BUFFER, &sentryIoctlParams)
if err != nil {
return n, err
}
cu.Release()
// Unmap the reserved range, which is no longer required.
unix.RawSyscall(unix.SYS_MUNMAP, m, uintptr(ar.Length()), 0)
fd.device.mu.Lock()
defer fd.device.mu.Unlock()
devAddr := userIoctlParams.DeviceAddress
for _, pr := range prs {
rlen := uint64(pr.Source.Length())
if !fd.device.devAddrSet.Add(DevAddrRange{
devAddr,
devAddr + rlen,
}, pinnedAccelMem{pinnedRange: pr, pageTableIndex: userIoctlParams.PageTableIndex}) {
panic(fmt.Sprintf("unexpected overlap of devaddr range [%#x-%#x)", devAddr, devAddr+rlen))
}
devAddr += rlen
}
return n, nil
}
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
}
sentryIoctlParams := userIoctlParams
sentryIoctlParams.HostAddress = 0 // clobber this value, it's unused.
n, err := ioctlInvokePtrArg(hostFd, gasket.GASKET_IOCTL_UNMAP_BUFFER, &sentryIoctlParams)
if err != nil {
return n, err
}
fd.device.mu.Lock()
defer fd.device.mu.Unlock()
s := &fd.device.devAddrSet
r := DevAddrRange{userIoctlParams.DeviceAddress, userIoctlParams.DeviceAddress + userIoctlParams.Size}
seg := s.LowerBoundSegment(r.Start)
for seg.Ok() && seg.Start() < r.End {
seg = s.Isolate(seg, r)
v := seg.Value()
mm.Unpin([]mm.PinnedRange{v.pinnedRange})
gap := s.Remove(seg)
seg = gap.NextSegment()
}
return n, nil
}
func gasketInterruptMappingIoctl(ctx context.Context, t *kernel.Task, hostFd int32, paramsAddr hostarch.Addr) (uintptr, error) {
var userIoctlParams gasket.GasketInterruptMapping
if _, err := userIoctlParams.CopyIn(t, paramsAddr); err != nil {
return 0, err
}
// Check that 'userEventFD.Eventfd' is an eventfd.
eventFileGeneric, _ := t.FDTable().Get(int32(userIoctlParams.EventFD))
if eventFileGeneric == nil {
return 0, linuxerr.EBADF
}
defer eventFileGeneric.DecRef(ctx)
eventFile, ok := eventFileGeneric.Impl().(*eventfd.EventFileDescription)
if !ok {
return 0, linuxerr.EINVAL
}
eventfd, err := eventFile.HostFD()
if err != nil {
return 0, err
}
sentryIoctlParams := userIoctlParams
sentryIoctlParams.EventFD = uint64(eventfd)
n, err := ioctlInvokePtrArg(hostFd, gasket.GASKET_IOCTL_REGISTER_INTERRUPT, &sentryIoctlParams)
if err != nil {
return n, err
}
outIoctlParams := sentryIoctlParams
outIoctlParams.EventFD = userIoctlParams.EventFD
if _, err := outIoctlParams.CopyOut(t, paramsAddr); err != nil {
return n, err
}
return n, nil
}