Files
Luis Capelo f83fe832d0 Add CUDA GPU coredump support to nvproxy
Adds the five missing `ioctls` required to generate [CUDA GPU coredumps](https://docs.nvidia.com/cuda/cuda-gdb/index.html#gpu-core-dump-support) (`CUDA_ENABLE_COREDUMP_ON_EXCEPTION=1`) under nvproxy, all on the `GT200_DEBUGGER` (`0x83de`) class:

- `NV83DE_CTRL_CMD_DEBUG_SET_MODE_MMU_DEBUG` (`0x83de0307`) control command — enables MMU debug mode during exception handling.
- `NV83DE_CTRL_CMD_DEBUG_READ_MEMORY` (`0x83de0315`) control command, with its `NV83DE_CTRL_DEBUG_READ_MEMORY_PARAMS` struct — reads a block of memory from the debugged context into an embedded pointer (`buffer`), which requires a custom handler. Note that the driver passes `RMAPI_PARAM_COPY_FLAGS_DISABLE_MAX_SIZE_CHECK` for this control (see `src/nvidia/src/kernel/rmapi/embedded_param_copy.c`), so unlike most embedded pointers the buffer is *not* bounded by `RMAPI_PARAM_COPY_MAX_PARAMS_SIZE`; the handler mirrors this.
- `NV83DE_CTRL_CMD_DEBUG_SUSPEND_CONTEXT` (`0x83de0317`) control command — suspends the faulting SM context so its state can be captured.
- `NV83DE_CTRL_CMD_DEBUG_RESUME_CONTEXT` (`0x83de0318`) control command — resumes the context after capture. No params.
- `NV83DE_CTRL_CMD_DEBUG_SET_MODE_ERRBAR_DEBUG` (`0x83de031f`) control command — enables error-barrier debug mode (Volta+), a prerequisite for capturing SM exception state.

All are simple pass-through controls except `DEBUG_READ_MEMORY`. They are gated on `compute,utility`, consistent with the existing `NV83DE` debug controls (`SET_EXCEPTION_MASK`, `READ_ALL_SM_ERROR_STATES`, `CLEAR_ALL_SM_ERROR_STATES`).

Coredump generation additionally uses `NV2080_CTRL_CMD_GPU_EXEC_REG_OPS` (already supported), so the feature requires running with `--nvproxy-allowed-driver-capabilities=all,profiling`.

Tested on an A10G with a faulting Triton kernel: the generated `.nvcudmp` matches native (runc) byte-for-byte in size, and `cuda-gdb` fully decodes it (`CUDA Exception: Warp Illegal Address`, faulting PC/kernel/warp/lane and backtrace). Without this change, coredump generation silently produces an empty dump (`No exception was found on any device`).

x
2026-08-04 18:26:56 +00:00

536 lines
20 KiB
Go

// 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 nvproxy
import (
"runtime"
"unsafe"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/nvgpu"
"gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/marshal/primitive"
)
func frontendIoctlInvoke[Params any, PtrParams hasStatusPtr[Params]](fi *frontendIoctlState, ioctlParams PtrParams) (uintptr, error) {
n, err := frontendIoctlInvokeNoStatus(fi, ioctlParams)
if err == nil && log.IsLogging(log.Debug) {
if status := ioctlParams.GetStatus(); status != nvgpu.NV_OK {
fi.ctx.Debugf("nvproxy: frontend ioctl failed: status=%#x", status)
}
}
return n, err
}
func frontendIoctlInvokeNoStatus[Params any](fi *frontendIoctlState, ioctlParams *Params) (uintptr, error) {
n, _, errno := unix.RawSyscall(unix.SYS_IOCTL, uintptr(fi.fd.hostFD), frontendIoctlCmd(fi.nr, fi.ioctlParamsSize), uintptr(unsafe.Pointer(ioctlParams)))
if errno != 0 {
return n, errno
}
return n, nil
}
func rmControlInvoke[Params any](fi *frontendIoctlState, ioctlParams *nvgpu.NVOS54_PARAMETERS, ctrlParams *Params) (uintptr, error) {
defer runtime.KeepAlive(ctrlParams) // since we convert to non-pointer-typed P64
origParams := ioctlParams.Params
ioctlParams.Params = p64FromPtr(unsafe.Pointer(ctrlParams))
n, err := frontendIoctlInvoke(fi, ioctlParams)
ioctlParams.Params = origParams
if err != nil {
return n, err
}
if _, err := ioctlParams.CopyOut(fi.t, fi.ioctlParamsAddr); err != nil {
return n, err
}
return n, nil
}
func ctrlClientSystemGetBuildVersionInvoke(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS54_PARAMETERS, ctrlParams *nvgpu.NV0000_CTRL_SYSTEM_GET_BUILD_VERSION_PARAMS, driverVersionBuf, versionBuf, titleBuf *byte) (uintptr, error) {
// *Buf arguments don't need runtime.KeepAlive() since our caller
// ctrlClientSystemGetBuildVersion() copies them out, keeping them alive
// during this function.
origPDriverVersionBuffer := ctrlParams.PDriverVersionBuffer
origPVersionBuffer := ctrlParams.PVersionBuffer
origPTitleBuffer := ctrlParams.PTitleBuffer
ctrlParams.PDriverVersionBuffer = p64FromPtr(unsafe.Pointer(driverVersionBuf))
ctrlParams.PVersionBuffer = p64FromPtr(unsafe.Pointer(versionBuf))
ctrlParams.PTitleBuffer = p64FromPtr(unsafe.Pointer(titleBuf))
n, err := rmControlInvoke(fi, ioctlParams, ctrlParams)
ctrlParams.PDriverVersionBuffer = origPDriverVersionBuffer
ctrlParams.PVersionBuffer = origPVersionBuffer
ctrlParams.PTitleBuffer = origPTitleBuffer
if err != nil {
return n, err
}
if _, err := ctrlParams.CopyOut(fi.t, addrFromP64(ioctlParams.Params)); err != nil {
return n, err
}
return n, nil
}
func ctrlIoctlHasInfoList[Params any, PtrParams hasCtrlInfoListPtr[Params]](fi *frontendIoctlState, ioctlParams *nvgpu.NVOS54_PARAMETERS) (uintptr, error) {
var ctrlParamsValue Params
ctrlParams := PtrParams(&ctrlParamsValue)
if ctrlParams.SizeBytes() != int(ioctlParams.ParamsSize) {
return 0, linuxerr.EINVAL
}
if _, err := ctrlParams.CopyIn(fi.t, addrFromP64(ioctlParams.Params)); err != nil {
return 0, err
}
var infoList []byte
if listSize := ctrlParams.ListSize(); listSize > 0 {
if !rmapiParamsSizeCheck(listSize, nvgpu.CtrlXxxInfoSize) {
return 0, frontendFailWithStatus(fi, ioctlParams, nvgpu.NV_ERR_INVALID_ARGUMENT)
}
infoList = make([]byte, listSize*nvgpu.CtrlXxxInfoSize)
if _, err := fi.t.CopyInBytes(addrFromP64(ctrlParams.CtrlInfoList()), infoList); err != nil {
return 0, err
}
}
origInfoList := ctrlParams.CtrlInfoList()
if infoList == nil {
ctrlParams.SetCtrlInfoList(p64FromPtr(unsafe.Pointer(nil)))
} else {
ctrlParams.SetCtrlInfoList(p64FromPtr(unsafe.Pointer(&infoList[0])))
}
n, err := rmControlInvoke(fi, ioctlParams, ctrlParams)
ctrlParams.SetCtrlInfoList(origInfoList)
if err != nil {
return n, err
}
if infoList != nil {
if _, err := fi.t.CopyOutBytes(addrFromP64(origInfoList), infoList); err != nil {
return n, err
}
}
if _, err := ctrlParams.CopyOut(fi.t, addrFromP64(ioctlParams.Params)); err != nil {
return n, err
}
return n, nil
}
func ctrlGetNvU32ListInvoke(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS54_PARAMETERS, ctrlParams *nvgpu.RmapiParamNvU32List, list []uint32) (uintptr, error) {
origList := ctrlParams.List
ctrlParams.List = 0
if len(list) != 0 {
ctrlParams.List = p64FromPtr(unsafe.Pointer(&list[0]))
}
n, err := rmControlInvoke(fi, ioctlParams, ctrlParams)
ctrlParams.List = origList
if err != nil {
return n, err
}
if len(list) != 0 {
if _, err := primitive.CopyUint32SliceOut(fi.t, addrFromP64(ctrlParams.List), list); err != nil {
return n, err
}
}
if _, err := ctrlParams.CopyOut(fi.t, addrFromP64(ioctlParams.Params)); err != nil {
return n, err
}
return n, nil
}
func ctrlGpuExecRegOps(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS54_PARAMETERS) (uintptr, error) {
var ctrlParams nvgpu.NV2080_CTRL_GPU_EXEC_REG_OPS_PARAMS
if ctrlParams.SizeBytes() != int(ioctlParams.ParamsSize) {
return 0, linuxerr.EINVAL
}
if _, err := ctrlParams.CopyIn(fi.t, addrFromP64(ioctlParams.Params)); err != nil {
return 0, err
}
var regOps []byte
if ctrlParams.RegOps != 0 {
if !rmapiParamsSizeCheck(ctrlParams.RegOpCount, nvgpu.CtrlGpuRegOpSize) {
return 0, frontendFailWithStatus(fi, ioctlParams, nvgpu.NV_ERR_INVALID_ARGUMENT)
}
regOps = make([]byte, uint64(ctrlParams.RegOpCount)*uint64(nvgpu.CtrlGpuRegOpSize))
if _, err := fi.t.CopyInBytes(addrFromP64(ctrlParams.RegOps), regOps); err != nil {
return 0, err
}
}
origRegOps := ctrlParams.RegOps
ctrlParams.RegOps = 0
if len(regOps) != 0 {
ctrlParams.RegOps = p64FromPtr(unsafe.Pointer(&regOps[0]))
}
n, err := rmControlInvoke(fi, ioctlParams, &ctrlParams)
ctrlParams.RegOps = origRegOps
if err != nil {
return n, err
}
if len(regOps) != 0 {
if _, err := fi.t.CopyOutBytes(addrFromP64(ctrlParams.RegOps), regOps); err != nil {
return n, err
}
}
if _, err := ctrlParams.CopyOut(fi.t, addrFromP64(ioctlParams.Params)); err != nil {
return n, err
}
return n, nil
}
// debugReadMemoryMaxLength bounds the size of the sentry buffer allocated to
// proxy a single NV83DE_CTRL_CMD_DEBUG_READ_MEMORY request, preventing a
// malicious application from forcing an arbitrarily large (up to ^uint32(0)
// bytes) sentry allocation. The driver itself does not impose a limit below
// the size of the memory object being read (it passes
// RMAPI_PARAM_COPY_FLAGS_DISABLE_MAX_SIZE_CHECK for this control). In
// practice, CUDA GPU coredump generation is expected to stay comfortably
// within 1 GiB.
const debugReadMemoryMaxLength = 1 << 30 // 1 GiB
// ctrlDebugReadMemory implements NV83DE_CTRL_CMD_DEBUG_READ_MEMORY, whose
// parameters contain an embedded pointer (Buffer) that the driver fills with
// Length bytes read from the debugged context. Compare
// src/nvidia/src/kernel/rmapi/embedded_param_copy.c:embeddedParamCopyIn();
// note that the driver passes RMAPI_PARAM_COPY_FLAGS_DISABLE_MAX_SIZE_CHECK
// for this control, so Buffer's size is not limited by
// RMAPI_PARAM_COPY_MAX_PARAMS_SIZE; we bound it by debugReadMemoryMaxLength
// instead.
func ctrlDebugReadMemory(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS54_PARAMETERS) (uintptr, error) {
var ctrlParams nvgpu.NV83DE_CTRL_DEBUG_READ_MEMORY_PARAMS
if ctrlParams.SizeBytes() != int(ioctlParams.ParamsSize) {
return 0, linuxerr.EINVAL
}
if _, err := ctrlParams.CopyIn(fi.t, addrFromP64(ioctlParams.Params)); err != nil {
return 0, err
}
if ctrlParams.Length > debugReadMemoryMaxLength {
// NV_ERR_INVALID_ARGUMENT is consistent with the status returned by
// src/nvidia/src/kernel/rmapi/param_copy.c:rmapiParamsAcquire() when
// RMAPI_PARAM_COPY_MAX_PARAMS_SIZE is exceeded, for controls where
// that check is enabled.
fi.ctx.Warningf("nvproxy: NV83DE_CTRL_CMD_DEBUG_READ_MEMORY length %d exceeds limit %d", ctrlParams.Length, uint32(debugReadMemoryMaxLength))
return 0, frontendFailWithStatus(fi, ioctlParams, nvgpu.NV_ERR_INVALID_ARGUMENT)
}
var buffer []byte
if ctrlParams.Buffer != 0 && ctrlParams.Length != 0 {
buffer = make([]byte, ctrlParams.Length)
}
origBuffer := ctrlParams.Buffer
ctrlParams.Buffer = 0
if len(buffer) != 0 {
ctrlParams.Buffer = p64FromPtr(unsafe.Pointer(&buffer[0]))
}
n, err := rmControlInvoke(fi, ioctlParams, &ctrlParams)
ctrlParams.Buffer = origBuffer
if err != nil {
return n, err
}
// The driver copies the buffer out unconditionally (even if the control
// returned a non-OK status); see
// src/nvidia/src/kernel/rmapi/embedded_param_copy.c:embeddedParamCopyOut().
if len(buffer) != 0 {
if _, err := fi.t.CopyOutBytes(addrFromP64(origBuffer), buffer); err != nil {
return n, err
}
}
if _, err := ctrlParams.CopyOut(fi.t, addrFromP64(ioctlParams.Params)); err != nil {
return n, err
}
return n, nil
}
func ctrlDevGRGetCapsInvoke(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS54_PARAMETERS, ctrlParams *nvgpu.NV0080_CTRL_GET_CAPS_PARAMS, capsTbl []byte) (uintptr, error) {
origCapsTbl := ctrlParams.CapsTbl
ctrlParams.CapsTbl = p64FromPtr(unsafe.Pointer(&capsTbl[0]))
n, err := rmControlInvoke(fi, ioctlParams, ctrlParams)
ctrlParams.CapsTbl = origCapsTbl
if err != nil {
return n, err
}
if _, err := primitive.CopyByteSliceOut(fi.t, addrFromP64(ctrlParams.CapsTbl), capsTbl); err != nil {
return n, err
}
if _, err := ctrlParams.CopyOut(fi.t, addrFromP64(ioctlParams.Params)); err != nil {
return n, err
}
return n, nil
}
func ctrlDevFIFOGetChannelList(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS54_PARAMETERS) (uintptr, error) {
var ctrlParams nvgpu.NV0080_CTRL_FIFO_GET_CHANNELLIST_PARAMS
if ctrlParams.SizeBytes() != int(ioctlParams.ParamsSize) {
return 0, linuxerr.EINVAL
}
if _, err := ctrlParams.CopyIn(fi.t, addrFromP64(ioctlParams.Params)); err != nil {
return 0, err
}
if !rmapiParamsSizeCheck(ctrlParams.NumChannels, 4 /* sizeof(NvU32) */) {
// Compare
// src/nvidia/src/kernel/gpu/fifo/kernel_fifo_ctrl.c:deviceCtrlCmdFifoGetChannelList_IMPL().
return 0, linuxerr.EINVAL
}
channelHandleList := make([]uint32, ctrlParams.NumChannels)
if _, err := primitive.CopyUint32SliceIn(fi.t, addrFromP64(ctrlParams.PChannelHandleList), channelHandleList); err != nil {
return 0, err
}
channelList := make([]uint32, ctrlParams.NumChannels)
if _, err := primitive.CopyUint32SliceIn(fi.t, addrFromP64(ctrlParams.PChannelList), channelList); err != nil {
return 0, err
}
origPChannelHandleList := ctrlParams.PChannelHandleList
origPChannelList := ctrlParams.PChannelList
ctrlParams.PChannelHandleList = p64FromPtr(unsafe.Pointer(&channelHandleList[0]))
ctrlParams.PChannelList = p64FromPtr(unsafe.Pointer(&channelList[0]))
n, err := rmControlInvoke(fi, ioctlParams, &ctrlParams)
ctrlParams.PChannelHandleList = origPChannelHandleList
ctrlParams.PChannelList = origPChannelList
if err != nil {
return n, err
}
if _, err := primitive.CopyUint32SliceOut(fi.t, addrFromP64(origPChannelHandleList), channelHandleList); err != nil {
return n, err
}
if _, err := primitive.CopyUint32SliceOut(fi.t, addrFromP64(origPChannelList), channelList); err != nil {
return n, err
}
if _, err := ctrlParams.CopyOut(fi.t, addrFromP64(ioctlParams.Params)); err != nil {
return n, err
}
return n, nil
}
func ctrlClientSystemGetP2PCapsInitializeArray(origArr nvgpu.P64, gpuCount uint32) (nvgpu.P64, []uint32, bool) {
// The driver doesn't try and copy memory if the array is null. See
// src/nvidia/src/kernel/rmapi/embedded_param_copy.c::embeddedParamCopyIn(),
// case NV0000_CTRL_CMD_SYSTEM_GET_P2P_CAPS.
if origArr == 0 {
return 0, nil, true
}
// Params size is gpuCount * gpuCount * sizeof(NvU32).
// Use uint64 to handle overflow. In the driver, this is handled by
// portSafeMulU32(). See
// src/nvidia/src/kernel/rmapi/embedded_param_copy.c::embeddedParamCopyIn().
numEntries := uint64(gpuCount) * uint64(gpuCount)
if numEntries == 0 || numEntries > nvgpu.RMAPI_PARAM_COPY_MAX_PARAMS_SIZE/4 {
return 0, nil, false
}
arr := make([]uint32, numEntries)
return p64FromPtr(unsafe.Pointer(&arr[0])), arr, true
}
func ctrlClientSystemGetP2PCaps(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS54_PARAMETERS) (uintptr, error) {
var ctrlParams nvgpu.NV0000_CTRL_SYSTEM_GET_P2P_CAPS_PARAMS
if ctrlParams.SizeBytes() != int(ioctlParams.ParamsSize) {
return 0, linuxerr.EINVAL
}
if _, err := ctrlParams.CopyIn(fi.t, addrFromP64(ioctlParams.Params)); err != nil {
return 0, err
}
origBusPeerIDs := ctrlParams.BusPeerIDs
busPeerIDs, busPeerIDsBuf, ok := ctrlClientSystemGetP2PCapsInitializeArray(origBusPeerIDs, ctrlParams.GpuCount)
if !ok {
return 0, frontendFailWithStatus(fi, ioctlParams, nvgpu.NV_ERR_INVALID_ARGUMENT)
}
ctrlParams.BusPeerIDs = busPeerIDs
n, err := rmControlInvoke(fi, ioctlParams, &ctrlParams)
ctrlParams.BusPeerIDs = origBusPeerIDs
if err != nil {
return n, err
}
if _, err := primitive.CopyUint32SliceOut(fi.t, addrFromP64(origBusPeerIDs), busPeerIDsBuf); err != nil {
return n, err
}
_, err = ctrlParams.CopyOut(fi.t, addrFromP64(ioctlParams.Params))
return n, err
}
func ctrlClientSystemGetP2PCapsV550(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS54_PARAMETERS) (uintptr, error) {
var ctrlParams nvgpu.NV0000_CTRL_SYSTEM_GET_P2P_CAPS_PARAMS_V550
if ctrlParams.SizeBytes() != int(ioctlParams.ParamsSize) {
return 0, linuxerr.EINVAL
}
if _, err := ctrlParams.CopyIn(fi.t, addrFromP64(ioctlParams.Params)); err != nil {
return 0, err
}
origBusPeerIDs := ctrlParams.BusPeerIDs
busPeerIDs, busPeerIDsBuf, ok := ctrlClientSystemGetP2PCapsInitializeArray(origBusPeerIDs, ctrlParams.GpuCount)
if !ok {
return 0, frontendFailWithStatus(fi, ioctlParams, nvgpu.NV_ERR_INVALID_ARGUMENT)
}
ctrlParams.BusPeerIDs = busPeerIDs
origBusEgmPeerIDs := ctrlParams.BusEgmPeerIDs
busEgmPeerIDs, busEgmPeerIDsBuf, ok := ctrlClientSystemGetP2PCapsInitializeArray(origBusEgmPeerIDs, ctrlParams.GpuCount)
if !ok {
return 0, frontendFailWithStatus(fi, ioctlParams, nvgpu.NV_ERR_INVALID_ARGUMENT)
}
ctrlParams.BusEgmPeerIDs = busEgmPeerIDs
n, err := rmControlInvoke(fi, ioctlParams, &ctrlParams)
ctrlParams.BusPeerIDs = origBusPeerIDs
ctrlParams.BusEgmPeerIDs = origBusEgmPeerIDs
if err != nil {
return n, err
}
// If origBufPeerIDS or origBusEgmPeerIDs is null, the corresponding buffer will be nil
// and CopyUint32SliceOut() will be a no-op.
if _, err := primitive.CopyUint32SliceOut(fi.t, addrFromP64(origBusPeerIDs), busPeerIDsBuf); err != nil {
return n, err
}
if _, err := primitive.CopyUint32SliceOut(fi.t, addrFromP64(origBusEgmPeerIDs), busEgmPeerIDsBuf); err != nil {
return n, err
}
_, err = ctrlParams.CopyOut(fi.t, addrFromP64(ioctlParams.Params))
return n, err
}
func rmAllocInvoke[Params any](fi *frontendIoctlState, ioctlParams *nvgpu.NVOS64_PARAMETERS, allocParams *Params, isNVOS64 bool, addObjLocked func(fi *frontendIoctlState, client *rootClient, ioctlParams *nvgpu.NVOS64_PARAMETERS, rightsRequested nvgpu.RS_ACCESS_MASK, allocParams *Params)) (uintptr, error) {
defer runtime.KeepAlive(allocParams) // since we convert to non-pointer-typed P64
// Temporarily replace application pointers with sentry pointers.
origPAllocParms := ioctlParams.PAllocParms
origPRightsRequested := ioctlParams.PRightsRequested
var rightsRequested nvgpu.RS_ACCESS_MASK
if ioctlParams.PRightsRequested != 0 {
if _, err := rightsRequested.CopyIn(fi.t, addrFromP64(ioctlParams.PRightsRequested)); err != nil {
return 0, err
}
ioctlParams.PRightsRequested = p64FromPtr(unsafe.Pointer(&rightsRequested))
}
ioctlParams.PAllocParms = p64FromPtr(unsafe.Pointer(allocParams))
var (
client *rootClient
unlock = func() {}
)
if !ioctlParams.HClass.IsRootClient() {
client, unlock = fi.fd.dev.nvp.getClientWithLock(fi.ctx, ioctlParams.HRoot)
if client == nil {
return 0, frontendFailWithStatus(fi, ioctlParams, nvgpu.NV_ERR_INVALID_CLIENT)
}
}
// Invoke the driver ioctl and restore application pointers. We always pass
// NVOS64Parameters to the driver even if !isNVOS64, as this is handled
// identically to the equivalent NVOS21Parameters; compare
// src/nvidia/src/kernel/rmapi/entry_points.c:_nv04AllocWithSecInfo() and
// _nv04AllocWithAccessSecInfo().
origParamsSize := fi.ioctlParamsSize
fi.ioctlParamsSize = nvgpu.SizeofNVOS64Parameters
n, err := frontendIoctlInvoke(fi, ioctlParams)
fi.ioctlParamsSize = origParamsSize
if err == nil && ioctlParams.Status == nvgpu.NV_OK {
addObjLocked(fi, client, ioctlParams, rightsRequested, allocParams)
}
unlock()
ioctlParams.PAllocParms = origPAllocParms
ioctlParams.PRightsRequested = origPRightsRequested
if err != nil {
return n, err
}
// Copy updated params out to the application.
outIoctlParams := nvgpu.GetRmAllocParamObj(isNVOS64)
outIoctlParams.FromOS64(*ioctlParams)
if ioctlParams.PRightsRequested != 0 {
if _, err := rightsRequested.CopyOut(fi.t, addrFromP64(ioctlParams.PRightsRequested)); err != nil {
return n, err
}
}
if _, err := outIoctlParams.CopyOut(fi.t, fi.ioctlParamsAddr); err != nil {
return n, err
}
return n, nil
}
func rmIdleChannelsInvoke(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS30_PARAMETERS, clientsBuf, devicesBuf, channelsBuf *byte) (uintptr, error) {
origClients := ioctlParams.Clients
origDevices := ioctlParams.Devices
origChannels := ioctlParams.Channels
ioctlParams.Clients = p64FromPtr(unsafe.Pointer(clientsBuf))
ioctlParams.Devices = p64FromPtr(unsafe.Pointer(devicesBuf))
ioctlParams.Channels = p64FromPtr(unsafe.Pointer(channelsBuf))
n, err := frontendIoctlInvoke(fi, ioctlParams)
ioctlParams.Clients = origClients
ioctlParams.Devices = origDevices
ioctlParams.Channels = origChannels
if err != nil {
return n, err
}
if _, err := ioctlParams.CopyOut(fi.t, fi.ioctlParamsAddr); err != nil {
return n, err
}
return n, nil
}
func rmVidHeapControlAllocSize(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS32_PARAMETERS) (uintptr, error) {
allocSizeParams := (*nvgpu.NVOS32AllocSize)(unsafe.Pointer(&ioctlParams.Data))
origAddress := allocSizeParams.Address
var addr uint64
if allocSizeParams.Address != 0 {
if _, err := primitive.CopyUint64In(fi.t, addrFromP64(allocSizeParams.Address), &addr); err != nil {
return 0, err
}
allocSizeParams.Address = p64FromPtr(unsafe.Pointer(&addr))
}
client, unlock := fi.fd.dev.nvp.getClientWithLock(fi.ctx, ioctlParams.HRoot)
if client == nil {
return 0, frontendFailWithStatus(fi, ioctlParams, nvgpu.NV_ERR_INVALID_CLIENT)
}
n, err := frontendIoctlInvoke(fi, ioctlParams)
if err == nil && ioctlParams.Status == nvgpu.NV_OK {
// src/nvidia/interface/deprecated/rmapi_deprecated_vidheapctrl.c:_rmVidHeapControlAllocCommon()
if allocSizeParams.Flags&nvgpu.NVOS32_ALLOC_FLAGS_VIRTUAL != 0 {
// src/nvidia/src/kernel/mem_mgr/virtual_mem.c:virtmemConstruct_IMPL() => refAddDependant()
fi.fd.dev.nvp.objAdd(fi.ctx, client, allocSizeParams.HMemory, nvgpu.NV50_MEMORY_VIRTUAL, &miscObject{}, ioctlParams.HObjectParent, ioctlParams.HVASpace)
} else {
classID := nvgpu.ClassID(nvgpu.NV01_MEMORY_SYSTEM)
if (allocSizeParams.Attr2>>nvgpu.NVOS32_ATTR2_USE_EGM_SHIFT)&nvgpu.NVOS32_ATTR2_USE_EGM_MASK == nvgpu.NVOS32_ATTR2_USE_EGM_TRUE {
classID = nvgpu.NV_MEMORY_EXTENDED_USER
} else if (allocSizeParams.Attr>>nvgpu.NVOS32_ATTR_LOCATION_SHIFT)&nvgpu.NVOS32_ATTR_LOCATION_MASK == nvgpu.NVOS32_ATTR_LOCATION_VIDMEM {
classID = nvgpu.NV01_MEMORY_LOCAL_USER
}
fi.fd.dev.nvp.objAdd(fi.ctx, client, allocSizeParams.HMemory, classID, &miscObject{}, ioctlParams.HObjectParent)
}
}
unlock()
allocSizeParams.Address = origAddress
if err != nil {
return n, err
}
if allocSizeParams.Address != 0 {
if _, err := primitive.CopyUint64Out(fi.t, addrFromP64(allocSizeParams.Address), addr); err != nil {
return n, err
}
}
if _, err := ioctlParams.CopyOut(fi.t, fi.ioctlParamsAddr); err != nil {
return n, err
}
return n, nil
}