gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/sentry/devices/nvproxy/uvm.go (about) 1 // Copyright 2023 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 nvproxy 16 17 import ( 18 "fmt" 19 20 "golang.org/x/sys/unix" 21 "gvisor.dev/gvisor/pkg/abi/nvgpu" 22 "gvisor.dev/gvisor/pkg/context" 23 "gvisor.dev/gvisor/pkg/devutil" 24 "gvisor.dev/gvisor/pkg/errors/linuxerr" 25 "gvisor.dev/gvisor/pkg/fdnotifier" 26 "gvisor.dev/gvisor/pkg/hostarch" 27 "gvisor.dev/gvisor/pkg/log" 28 "gvisor.dev/gvisor/pkg/sentry/arch" 29 "gvisor.dev/gvisor/pkg/sentry/kernel" 30 "gvisor.dev/gvisor/pkg/sentry/vfs" 31 "gvisor.dev/gvisor/pkg/usermem" 32 "gvisor.dev/gvisor/pkg/waiter" 33 ) 34 35 // uvmDevice implements vfs.Device for /dev/nvidia-uvm. 36 // 37 // +stateify savable 38 type uvmDevice struct { 39 nvp *nvproxy 40 } 41 42 // Open implements vfs.Device.Open. 43 func (dev *uvmDevice) Open(ctx context.Context, mnt *vfs.Mount, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) { 44 devClient := devutil.GoferClientFromContext(ctx) 45 if devClient == nil { 46 log.Warningf("devutil.CtxDevGoferClient is not set") 47 return nil, linuxerr.ENOENT 48 } 49 hostFD, err := devClient.OpenAt(ctx, "nvidia-uvm", opts.Flags) 50 if err != nil { 51 ctx.Warningf("nvproxy: failed to open host /dev/nvidia-uvm: %v", err) 52 return nil, err 53 } 54 fd := &uvmFD{ 55 dev: dev, 56 containerName: devClient.ContainerName(), 57 hostFD: int32(hostFD), 58 } 59 if err := fd.vfsfd.Init(fd, opts.Flags, mnt, vfsd, &vfs.FileDescriptionOptions{ 60 UseDentryMetadata: true, 61 }); err != nil { 62 unix.Close(hostFD) 63 return nil, err 64 } 65 if err := fdnotifier.AddFD(int32(hostFD), &fd.queue); err != nil { 66 unix.Close(hostFD) 67 return nil, err 68 } 69 fd.memmapFile.fd = fd 70 return &fd.vfsfd, nil 71 } 72 73 // uvmFD implements vfs.FileDescriptionImpl for /dev/nvidia-uvm. 74 // 75 // +stateify savable 76 type uvmFD struct { 77 vfsfd vfs.FileDescription 78 vfs.FileDescriptionDefaultImpl 79 vfs.DentryMetadataFileDescriptionImpl 80 vfs.NoLockFD 81 82 dev *uvmDevice 83 containerName string 84 hostFD int32 85 memmapFile uvmFDMemmapFile 86 87 queue waiter.Queue 88 } 89 90 // Release implements vfs.FileDescriptionImpl.Release. 91 func (fd *uvmFD) Release(context.Context) { 92 fdnotifier.RemoveFD(fd.hostFD) 93 fd.queue.Notify(waiter.EventHUp) 94 unix.Close(int(fd.hostFD)) 95 } 96 97 // EventRegister implements waiter.Waitable.EventRegister. 98 func (fd *uvmFD) EventRegister(e *waiter.Entry) error { 99 fd.queue.EventRegister(e) 100 if err := fdnotifier.UpdateFD(fd.hostFD); err != nil { 101 fd.queue.EventUnregister(e) 102 return err 103 } 104 return nil 105 } 106 107 // EventUnregister implements waiter.Waitable.EventUnregister. 108 func (fd *uvmFD) EventUnregister(e *waiter.Entry) { 109 fd.queue.EventUnregister(e) 110 if err := fdnotifier.UpdateFD(fd.hostFD); err != nil { 111 panic(fmt.Sprint("UpdateFD:", err)) 112 } 113 } 114 115 // Readiness implements waiter.Waitable.Readiness. 116 func (fd *uvmFD) Readiness(mask waiter.EventMask) waiter.EventMask { 117 return fdnotifier.NonBlockingPoll(fd.hostFD, mask) 118 } 119 120 // Epollable implements vfs.FileDescriptionImpl.Epollable. 121 func (fd *uvmFD) Epollable() bool { 122 return true 123 } 124 125 // Ioctl implements vfs.FileDescriptionImpl.Ioctl. 126 func (fd *uvmFD) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { 127 cmd := args[1].Uint() 128 argPtr := args[2].Pointer() 129 130 t := kernel.TaskFromContext(ctx) 131 if t == nil { 132 panic("Ioctl should be called from a task context") 133 } 134 135 if ctx.IsLogging(log.Debug) { 136 ctx.Debugf("nvproxy: uvm ioctl %d = %#x", cmd, cmd) 137 } 138 139 ui := uvmIoctlState{ 140 fd: fd, 141 ctx: ctx, 142 t: t, 143 cmd: cmd, 144 ioctlParamsAddr: argPtr, 145 } 146 handler := fd.dev.nvp.abi.uvmIoctl[cmd] 147 if handler == nil { 148 ctx.Warningf("nvproxy: unknown uvm ioctl %d = %#x", cmd, cmd) 149 return 0, linuxerr.EINVAL 150 } 151 return handler(&ui) 152 } 153 154 // uvmIoctlState holds the state of a call to uvmFD.Ioctl(). 155 type uvmIoctlState struct { 156 fd *uvmFD 157 ctx context.Context 158 t *kernel.Task 159 cmd uint32 160 ioctlParamsAddr hostarch.Addr 161 } 162 163 func uvmIoctlNoParams(ui *uvmIoctlState) (uintptr, error) { 164 return uvmIoctlInvoke[byte](ui, nil) 165 } 166 167 func uvmIoctlSimple[Params any, PtrParams marshalPtr[Params]](ui *uvmIoctlState) (uintptr, error) { 168 var ioctlParamsValue Params 169 ioctlParams := PtrParams(&ioctlParamsValue) 170 if _, err := ioctlParams.CopyIn(ui.t, ui.ioctlParamsAddr); err != nil { 171 return 0, err 172 } 173 n, err := uvmIoctlInvoke(ui, ioctlParams) 174 if err != nil { 175 return n, err 176 } 177 if _, err := ioctlParams.CopyOut(ui.t, ui.ioctlParamsAddr); err != nil { 178 return n, err 179 } 180 return n, nil 181 } 182 183 func uvmInitialize(ui *uvmIoctlState) (uintptr, error) { 184 var ioctlParams nvgpu.UVM_INITIALIZE_PARAMS 185 if _, err := ioctlParams.CopyIn(ui.t, ui.ioctlParamsAddr); err != nil { 186 return 0, err 187 } 188 origFlags := ioctlParams.Flags 189 // This is necessary to share the host UVM FD between sentry and 190 // application processes. 191 ioctlParams.Flags = ioctlParams.Flags | nvgpu.UVM_INIT_FLAGS_MULTI_PROCESS_SHARING_MODE 192 n, err := uvmIoctlInvoke(ui, &ioctlParams) 193 // Only expose the MULTI_PROCESS_SHARING_MODE flag if it was already present. 194 ioctlParams.Flags &^= ^origFlags & nvgpu.UVM_INIT_FLAGS_MULTI_PROCESS_SHARING_MODE 195 if err != nil { 196 return n, err 197 } 198 if _, err := ioctlParams.CopyOut(ui.t, ui.ioctlParamsAddr); err != nil { 199 return n, err 200 } 201 return n, nil 202 } 203 204 func uvmMMInitialize(ui *uvmIoctlState) (uintptr, error) { 205 var ioctlParams nvgpu.UVM_MM_INITIALIZE_PARAMS 206 if _, err := ioctlParams.CopyIn(ui.t, ui.ioctlParamsAddr); err != nil { 207 return 0, err 208 } 209 210 failWithStatus := func(status uint32) error { 211 outIoctlParams := ioctlParams 212 outIoctlParams.Status = status 213 _, err := outIoctlParams.CopyOut(ui.t, ui.ioctlParamsAddr) 214 return err 215 } 216 217 uvmFileGeneric, _ := ui.t.FDTable().Get(ioctlParams.UvmFD) 218 if uvmFileGeneric == nil { 219 return 0, failWithStatus(nvgpu.NV_ERR_INVALID_ARGUMENT) 220 } 221 defer uvmFileGeneric.DecRef(ui.ctx) 222 uvmFile, ok := uvmFileGeneric.Impl().(*uvmFD) 223 if !ok { 224 return 0, failWithStatus(nvgpu.NV_ERR_INVALID_ARGUMENT) 225 } 226 227 origFD := ioctlParams.UvmFD 228 ioctlParams.UvmFD = uvmFile.hostFD 229 n, err := uvmIoctlInvoke(ui, &ioctlParams) 230 ioctlParams.UvmFD = origFD 231 if err != nil { 232 return n, err 233 } 234 if _, err := ioctlParams.CopyOut(ui.t, ui.ioctlParamsAddr); err != nil { 235 return n, err 236 } 237 return n, nil 238 } 239 240 func uvmIoctlHasFrontendFD[Params any, PtrParams hasFrontendFDPtr[Params]](ui *uvmIoctlState) (uintptr, error) { 241 var ioctlParamsValue Params 242 ioctlParams := PtrParams(&ioctlParamsValue) 243 if _, err := ioctlParams.CopyIn(ui.t, ui.ioctlParamsAddr); err != nil { 244 return 0, err 245 } 246 247 origFD := ioctlParams.GetFrontendFD() 248 if origFD < 0 { 249 n, err := uvmIoctlInvoke(ui, ioctlParams) 250 if err != nil { 251 return n, err 252 } 253 if _, err := ioctlParams.CopyOut(ui.t, ui.ioctlParamsAddr); err != nil { 254 return n, err 255 } 256 return n, nil 257 } 258 259 ctlFileGeneric, _ := ui.t.FDTable().Get(origFD) 260 if ctlFileGeneric == nil { 261 return 0, linuxerr.EINVAL 262 } 263 defer ctlFileGeneric.DecRef(ui.ctx) 264 ctlFile, ok := ctlFileGeneric.Impl().(*frontendFD) 265 if !ok { 266 return 0, linuxerr.EINVAL 267 } 268 269 ioctlParams.SetFrontendFD(ctlFile.hostFD) 270 n, err := uvmIoctlInvoke(ui, ioctlParams) 271 ioctlParams.SetFrontendFD(origFD) 272 if err != nil { 273 return n, err 274 } 275 if _, err := ioctlParams.CopyOut(ui.t, ui.ioctlParamsAddr); err != nil { 276 return n, err 277 } 278 return n, nil 279 }