diff --git a/pkg/sentry/devices/nvproxy/BUILD b/pkg/sentry/devices/nvproxy/BUILD index 30d4a0290..fce7f032b 100644 --- a/pkg/sentry/devices/nvproxy/BUILD +++ b/pkg/sentry/devices/nvproxy/BUILD @@ -1,4 +1,4 @@ -load("//tools:defs.bzl", "go_library") +load("//tools:defs.bzl", "go_library", "go_test") load("//pkg/sync/locking:locking.bzl", "declare_mutex") package(default_applicable_licenses = ["//:license"]) @@ -25,6 +25,7 @@ go_library( "uvm.go", "uvm_mmap.go", "uvm_unsafe.go", + "version.go", ], visibility = ["//pkg/sentry:internal"], deps = [ @@ -53,3 +54,9 @@ go_library( "@org_golang_x_sys//unix:go_default_library", ], ) + +go_test( + name = "nvproxy_test", + srcs = ["nvproxy_test.go"], + library = ":nvproxy", +) diff --git a/pkg/sentry/devices/nvproxy/frontend.go b/pkg/sentry/devices/nvproxy/frontend.go index 36be253a7..07428ab04 100644 --- a/pkg/sentry/devices/nvproxy/frontend.go +++ b/pkg/sentry/devices/nvproxy/frontend.go @@ -167,44 +167,12 @@ func (fd *frontendFD) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, // - Add symbol and parameter type definitions to //pkg/abi/nvgpu. // - Add filter to seccomp_filters.go. // - Add handling below. - switch nr { - case - nvgpu.NV_ESC_CARD_INFO, // nv_ioctl_card_info_t - nvgpu.NV_ESC_CHECK_VERSION_STR, // nv_rm_api_version_t - nvgpu.NV_ESC_SYS_PARAMS, // nv_ioctl_sys_params_t - nvgpu.NV_ESC_RM_DUP_OBJECT, // NVOS55_PARAMETERS - nvgpu.NV_ESC_RM_SHARE, // NVOS57_PARAMETERS - nvgpu.NV_ESC_RM_UNMAP_MEMORY, // NVOS34_PARAMETERS - nvgpu.NV_ESC_RM_UPDATE_DEVICE_MAPPING_INFO: // NVOS56_PARAMETERS - return frontendIoctlSimple(&fi) - case nvgpu.NV_ESC_REGISTER_FD: - return frontendRegisterFD(&fi) - case nvgpu.NV_ESC_ALLOC_OS_EVENT: - return rmAllocOSEvent(&fi) - case nvgpu.NV_ESC_FREE_OS_EVENT: - return rmFreeOSEvent(&fi) - case nvgpu.NV_ESC_NUMA_INFO: - // The CPU topology seen by the host driver differs from the CPU - // topology presented by the sentry to the application, so reject this - // ioctl; doing so is non-fatal. - ctx.Debugf("nvproxy: ignoring NV_ESC_NUMA_INFO") - return 0, linuxerr.EINVAL - case nvgpu.NV_ESC_RM_ALLOC_MEMORY: - return rmAllocMemory(&fi) - case nvgpu.NV_ESC_RM_FREE: - return rmFree(&fi) - case nvgpu.NV_ESC_RM_CONTROL: - return rmControl(&fi) - case nvgpu.NV_ESC_RM_ALLOC: - return rmAlloc(&fi) - case nvgpu.NV_ESC_RM_VID_HEAP_CONTROL: - return rmVidHeapControl(&fi) - case nvgpu.NV_ESC_RM_MAP_MEMORY: - return rmMapMemory(&fi) - default: + handler := fd.nvp.abi.frontendIoctl[nr] + if handler == nil { ctx.Warningf("nvproxy: unknown frontend ioctl %d == %#x (argSize=%d, cmd=%#x)", nr, nr, argSize, cmd) return 0, linuxerr.EINVAL } + return handler(&fi) } func frontendIoctlCmd(nr, argSize uint32) uintptr { @@ -243,6 +211,14 @@ func frontendIoctlSimple(fi *frontendIoctlState) (uintptr, error) { return n, nil } +func rmNumaInfo(fi *frontendIoctlState) (uintptr, error) { + // The CPU topology seen by the host driver differs from the CPU + // topology presented by the sentry to the application, so reject this + // ioctl; doing so is non-fatal. + log.Debugf("nvproxy: ignoring NV_ESC_NUMA_INFO") + return 0, linuxerr.EINVAL +} + func frontendRegisterFD(fi *frontendIoctlState) (uintptr, error) { var ioctlParams nvgpu.IoctlRegisterFD if fi.ioctlParamsSize != nvgpu.SizeofIoctlRegisterFD { @@ -532,95 +508,12 @@ func rmControl(fi *frontendIoctlState) (uintptr, error) { // - Add symbol definition to //pkg/abi/nvgpu. Parameter type definition is // only required for non-simple commands. // - Add handling below. - switch ioctlParams.Cmd { - case - nvgpu.NV0000_CTRL_CMD_CLIENT_GET_ADDR_SPACE_TYPE, - nvgpu.NV0000_CTRL_CMD_CLIENT_SET_INHERITED_SHARE_POLICY, - nvgpu.NV0000_CTRL_CMD_GPU_GET_ATTACHED_IDS, - nvgpu.NV0000_CTRL_CMD_GPU_GET_ID_INFO, - nvgpu.NV0000_CTRL_CMD_GPU_GET_ID_INFO_V2, - nvgpu.NV0000_CTRL_CMD_GPU_GET_PROBED_IDS, - nvgpu.NV0000_CTRL_CMD_GPU_ATTACH_IDS, - nvgpu.NV0000_CTRL_CMD_GPU_DETACH_IDS, - nvgpu.NV0000_CTRL_CMD_GPU_GET_PCI_INFO, - nvgpu.NV0000_CTRL_CMD_GPU_QUERY_DRAIN_STATE, - nvgpu.NV0000_CTRL_CMD_GPU_GET_MEMOP_ENABLE, - nvgpu.NV0000_CTRL_CMD_SYNC_GPU_BOOST_GROUP_INFO, - nvgpu.NV0000_CTRL_CMD_SYSTEM_GET_P2P_CAPS, - nvgpu.NV0000_CTRL_CMD_SYSTEM_GET_FABRIC_STATUS, - nvgpu.NV0000_CTRL_CMD_SYSTEM_GET_P2P_CAPS_MATRIX, - nvgpu.NV0080_CTRL_CMD_FB_GET_CAPS_V2, - nvgpu.NV0080_CTRL_CMD_GPU_GET_NUM_SUBDEVICES, - nvgpu.NV0080_CTRL_CMD_GPU_QUERY_SW_STATE_PERSISTENCE, - nvgpu.NV0080_CTRL_CMD_GPU_GET_VIRTUALIZATION_MODE, - 0x80028b, // unknown, paramsSize == 1 - nvgpu.NV0080_CTRL_CMD_GPU_GET_CLASSLIST_V2, - nvgpu.NV0080_CTRL_CMD_HOST_GET_CAPS_V2, - nvgpu.NV2080_CTRL_CMD_BUS_GET_PCI_INFO, - nvgpu.NV2080_CTRL_CMD_BUS_GET_PCI_BAR_INFO, - nvgpu.NV2080_CTRL_CMD_BUS_GET_INFO_V2, - nvgpu.NV2080_CTRL_CMD_BUS_GET_PCIE_SUPPORTED_GPU_ATOMICS, - nvgpu.NV2080_CTRL_CMD_CE_GET_ALL_CAPS, - nvgpu.NV2080_CTRL_CMD_FB_GET_INFO_V2, - nvgpu.NV2080_CTRL_CMD_GPU_GET_INFO_V2, - nvgpu.NV2080_CTRL_CMD_GPU_GET_NAME_STRING, - nvgpu.NV2080_CTRL_CMD_GPU_GET_SHORT_NAME_STRING, - nvgpu.NV2080_CTRL_CMD_GPU_GET_SIMULATION_INFO, - nvgpu.NV2080_CTRL_CMD_GPU_QUERY_ECC_STATUS, - nvgpu.NV2080_CTRL_CMD_GPU_QUERY_COMPUTE_MODE_RULES, - nvgpu.NV2080_CTRL_CMD_GPU_ACQUIRE_COMPUTE_MODE_RESERVATION, - nvgpu.NV2080_CTRL_CMD_GPU_RELEASE_COMPUTE_MODE_RESERVATION, - nvgpu.NV2080_CTRL_CMD_GPU_GET_GID_INFO, - nvgpu.NV2080_CTRL_CMD_GPU_GET_ENGINES_V2, - nvgpu.NV2080_CTRL_CMD_GPU_GET_ACTIVE_PARTITION_IDS, - nvgpu.NV2080_CTRL_CMD_GPU_GET_COMPUTE_POLICY_CONFIG, - nvgpu.NV2080_CTRL_CMD_GET_GPU_FABRIC_PROBE_INFO, - nvgpu.NV2080_CTRL_CMD_GR_SET_CTXSW_PREEMPTION_MODE, - nvgpu.NV2080_CTRL_CMD_GR_GET_CTX_BUFFER_SIZE, - nvgpu.NV2080_CTRL_CMD_GR_GET_GLOBAL_SM_ORDER, - nvgpu.NV2080_CTRL_CMD_GR_GET_CAPS_V2, - nvgpu.NV2080_CTRL_CMD_GR_GET_GPC_MASK, - nvgpu.NV2080_CTRL_CMD_GR_GET_TPC_MASK, - nvgpu.NV2080_CTRL_CMD_GSP_GET_FEATURES, - nvgpu.NV2080_CTRL_CMD_MC_GET_ARCH_INFO, - nvgpu.NV2080_CTRL_CMD_MC_SERVICE_INTERRUPTS, - nvgpu.NV2080_CTRL_CMD_NVLINK_GET_NVLINK_STATUS, - nvgpu.NV2080_CTRL_CMD_PERF_BOOST, - nvgpu.NV2080_CTRL_CMD_RC_GET_WATCHDOG_INFO, - nvgpu.NV2080_CTRL_CMD_RC_RELEASE_WATCHDOG_REQUESTS, - nvgpu.NV2080_CTRL_CMD_RC_SOFT_DISABLE_WATCHDOG, - nvgpu.NV2080_CTRL_CMD_TIMER_GET_GPU_CPU_TIME_CORRELATION_INFO, - nvgpu.NV503C_CTRL_CMD_REGISTER_VA_SPACE, - nvgpu.NV503C_CTRL_CMD_REGISTER_VIDMEM, - nvgpu.NV503C_CTRL_CMD_UNREGISTER_VIDMEM, - nvgpu.NV83DE_CTRL_CMD_DEBUG_SET_EXCEPTION_MASK, - nvgpu.NV83DE_CTRL_CMD_DEBUG_READ_ALL_SM_ERROR_STATES, - nvgpu.NV83DE_CTRL_CMD_DEBUG_CLEAR_ALL_SM_ERROR_STATES, - nvgpu.NV906F_CTRL_CMD_RESET_CHANNEL, - nvgpu.NV90E6_CTRL_CMD_MASTER_GET_VIRTUAL_FUNCTION_ERROR_CONT_INTR_MASK, - nvgpu.NVC36F_CTRL_GET_CLASS_ENGINEID, - nvgpu.NVC36F_CTRL_CMD_GPFIFO_GET_WORK_SUBMIT_TOKEN, - nvgpu.NVA06C_CTRL_CMD_GPFIFO_SCHEDULE, - nvgpu.NVA06C_CTRL_CMD_SET_TIMESLICE, - nvgpu.NVA06C_CTRL_CMD_PREEMPT: - return rmControlSimple(fi, &ioctlParams) - - case nvgpu.NV0000_CTRL_CMD_SYSTEM_GET_BUILD_VERSION: - return ctrlClientSystemGetBuildVersion(fi, &ioctlParams) - - case nvgpu.NV0080_CTRL_CMD_FIFO_GET_CHANNELLIST: - return ctrlDevFIFOGetChannelList(fi, &ioctlParams) - - case nvgpu.NV2080_CTRL_CMD_FIFO_DISABLE_CHANNELS: - return ctrlSubdevFIFODisableChannels(fi, &ioctlParams) - - case nvgpu.NV2080_CTRL_CMD_GR_GET_INFO: - return ctrlSubdevGRGetInfo(fi, &ioctlParams) - - default: + handler := fi.fd.nvp.abi.controlCmd[ioctlParams.Cmd] + if handler == nil { fi.ctx.Warningf("nvproxy: unknown control command %#x (paramsSize=%d)", ioctlParams.Cmd, ioctlParams.ParamsSize) return 0, linuxerr.EINVAL } + return handler(fi, &ioctlParams) } func rmControlSimple(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS54Parameters) (uintptr, error) { @@ -752,41 +645,12 @@ func rmAlloc(fi *frontendIoctlState) (uintptr, error) { // the class whose constructor interprets it ("Internal Class"). // - Add symbol and parameter type definitions to //pkg/abi/nvgpu. // - Add handling below. - switch ioctlParams.HClass { - case nvgpu.NV01_ROOT, nvgpu.NV01_ROOT_NON_PRIV, nvgpu.NV01_ROOT_CLIENT: - return rmAllocSimple[nvgpu.Handle](fi, &ioctlParams, isNVOS64) - case nvgpu.NV01_EVENT_OS_EVENT: - return rmAllocEventOSEvent(fi, &ioctlParams, isNVOS64) - case nvgpu.NV01_DEVICE_0: - return rmAllocSimple[nvgpu.NV0080_ALLOC_PARAMETERS](fi, &ioctlParams, isNVOS64) - case nvgpu.NV20_SUBDEVICE_0: - return rmAllocSimple[nvgpu.NV2080_ALLOC_PARAMETERS](fi, &ioctlParams, isNVOS64) - case nvgpu.NV50_THIRD_PARTY_P2P: - return rmAllocSimple[nvgpu.NV503C_ALLOC_PARAMETERS](fi, &ioctlParams, isNVOS64) - case nvgpu.GT200_DEBUGGER: - return rmAllocSimple[nvgpu.NV83DE_ALLOC_PARAMETERS](fi, &ioctlParams, isNVOS64) - case nvgpu.FERMI_CONTEXT_SHARE_A: - return rmAllocSimple[nvgpu.NV_CTXSHARE_ALLOCATION_PARAMETERS](fi, &ioctlParams, isNVOS64) - case nvgpu.FERMI_VASPACE_A: - return rmAllocSimple[nvgpu.NV_VASPACE_ALLOCATION_PARAMETERS](fi, &ioctlParams, isNVOS64) - case nvgpu.KEPLER_CHANNEL_GROUP_A: - return rmAllocSimple[nvgpu.NV_CHANNEL_GROUP_ALLOCATION_PARAMETERS](fi, &ioctlParams, isNVOS64) - case nvgpu.TURING_CHANNEL_GPFIFO_A, nvgpu.AMPERE_CHANNEL_GPFIFO_A: - return rmAllocSimple[nvgpu.NV_CHANNEL_ALLOC_PARAMS](fi, &ioctlParams, isNVOS64) - case nvgpu.TURING_DMA_COPY_A, nvgpu.AMPERE_DMA_COPY_A, nvgpu.AMPERE_DMA_COPY_B, nvgpu.HOPPER_DMA_COPY_A: - return rmAllocSimple[nvgpu.NVB0B5_ALLOCATION_PARAMETERS](fi, &ioctlParams, isNVOS64) - case nvgpu.TURING_COMPUTE_A, nvgpu.AMPERE_COMPUTE_A, nvgpu.AMPERE_COMPUTE_B, nvgpu.ADA_COMPUTE_A, nvgpu.HOPPER_COMPUTE_A: - return rmAllocSimple[nvgpu.NV_GR_ALLOCATION_PARAMETERS](fi, &ioctlParams, isNVOS64) - case nvgpu.HOPPER_USERMODE_A: - return rmAllocSimple[nvgpu.NV_HOPPER_USERMODE_A_PARAMS](fi, &ioctlParams, isNVOS64) - case nvgpu.GF100_SUBDEVICE_MASTER, nvgpu.TURING_USERMODE_A: - return rmAllocNoParams(fi, &ioctlParams, isNVOS64) - case nvgpu.NV_MEMORY_FABRIC: - return rmAllocSimple[nvgpu.NV00F8_ALLOCATION_PARAMETERS](fi, &ioctlParams, isNVOS64) - default: + handler := fi.fd.nvp.abi.allocationClass[ioctlParams.HClass] + if handler == nil { fi.ctx.Warningf("nvproxy: unknown allocation class %#08x", ioctlParams.HClass) return 0, linuxerr.EINVAL } + return handler(fi, &ioctlParams, isNVOS64) } // Unlike frontendIoctlSimple and rmControlSimple, rmAllocSimple requires the diff --git a/pkg/sentry/devices/nvproxy/nvproxy.go b/pkg/sentry/devices/nvproxy/nvproxy.go index 7903165e1..bd58cc4d3 100644 --- a/pkg/sentry/devices/nvproxy/nvproxy.go +++ b/pkg/sentry/devices/nvproxy/nvproxy.go @@ -35,22 +35,22 @@ import ( func Register(vfsObj *vfs.VirtualFilesystem, uvmDevMajor uint32) error { // The kernel driver's interface is unstable, so only allow versions of the // driver that are known to be supported. - version, err := hostDriverVersion() + versionStr, err := hostDriverVersion() if err != nil { return fmt.Errorf("failed to get Nvidia driver version: %w", err) } - switch version { - case - "525.60.13", - "525.105.17", - "525.125.06": - log.Infof("Nvidia driver version: %s", version) - default: - return fmt.Errorf("unsupported Nvidia driver version: %s", version) + version, err := driverVersionFrom(versionStr) + if err != nil { + return fmt.Errorf("failed to parse Nvidia driver version %s: %w", versionStr, err) } - + abiCons, ok := abis[version] + if !ok { + return fmt.Errorf("unsupported Nvidia driver version: %s", versionStr) + } + log.Infof("Nvidia driver version: %s", versionStr) nvp := &nvproxy{ objsLive: make(map[nvgpu.Handle]*object), + abi: abiCons(), } for minor := uint32(0); minor <= nvgpu.NV_CONTROL_DEVICE_MINOR; minor++ { if err := vfsObj.RegisterDevice(vfs.CharDevice, nvgpu.NV_MAJOR_DEVICE_NUMBER, minor, &frontendDevice{ @@ -95,6 +95,7 @@ func CreateIndexDevtmpfsFile(ctx context.Context, dev *devtmpfs.Accessor, minor type nvproxy struct { objsMu objsMutex `state:"nosave"` objsLive map[nvgpu.Handle]*object + abi *driverABI } // object tracks an object allocated through the driver. diff --git a/pkg/sentry/devices/nvproxy/nvproxy_test.go b/pkg/sentry/devices/nvproxy/nvproxy_test.go new file mode 100644 index 000000000..a819200b5 --- /dev/null +++ b/pkg/sentry/devices/nvproxy/nvproxy_test.go @@ -0,0 +1,27 @@ +// 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 ( + "testing" +) + +func TestInit(t *testing.T) { + // Test that initializing all driverABI works (does not panic or anything). + Init() + for _, cons := range abis { + cons() + } +} diff --git a/pkg/sentry/devices/nvproxy/uvm.go b/pkg/sentry/devices/nvproxy/uvm.go index 108091e4a..74085348a 100644 --- a/pkg/sentry/devices/nvproxy/uvm.go +++ b/pkg/sentry/devices/nvproxy/uvm.go @@ -131,46 +131,12 @@ func (fd *uvmFD) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args cmd: cmd, ioctlParamsAddr: argPtr, } - - switch cmd { - case nvgpu.UVM_INITIALIZE: - return uvmInitialize(&ui) - case nvgpu.UVM_DEINITIALIZE: - return uvmIoctlInvoke[byte](&ui, nil) - case nvgpu.UVM_CREATE_RANGE_GROUP: - return uvmIoctlSimple[nvgpu.UVM_CREATE_RANGE_GROUP_PARAMS](&ui) - case nvgpu.UVM_DESTROY_RANGE_GROUP: - return uvmIoctlSimple[nvgpu.UVM_DESTROY_RANGE_GROUP_PARAMS](&ui) - case nvgpu.UVM_REGISTER_GPU_VASPACE: - return uvmIoctlHasRMCtrlFD[nvgpu.UVM_REGISTER_GPU_VASPACE_PARAMS](&ui) - case nvgpu.UVM_UNREGISTER_GPU_VASPACE: - return uvmIoctlSimple[nvgpu.UVM_UNREGISTER_GPU_VASPACE_PARAMS](&ui) - case nvgpu.UVM_REGISTER_CHANNEL: - return uvmIoctlHasRMCtrlFD[nvgpu.UVM_REGISTER_CHANNEL_PARAMS](&ui) - case nvgpu.UVM_UNREGISTER_CHANNEL: - return uvmIoctlSimple[nvgpu.UVM_UNREGISTER_CHANNEL_PARAMS](&ui) - case nvgpu.UVM_MAP_EXTERNAL_ALLOCATION: - return uvmIoctlHasRMCtrlFD[nvgpu.UVM_MAP_EXTERNAL_ALLOCATION_PARAMS](&ui) - case nvgpu.UVM_FREE: - return uvmIoctlSimple[nvgpu.UVM_FREE_PARAMS](&ui) - case nvgpu.UVM_REGISTER_GPU: - return uvmIoctlHasRMCtrlFD[nvgpu.UVM_REGISTER_GPU_PARAMS](&ui) - case nvgpu.UVM_UNREGISTER_GPU: - return uvmIoctlSimple[nvgpu.UVM_UNREGISTER_GPU_PARAMS](&ui) - case nvgpu.UVM_PAGEABLE_MEM_ACCESS: - return uvmIoctlSimple[nvgpu.UVM_PAGEABLE_MEM_ACCESS_PARAMS](&ui) - case nvgpu.UVM_MAP_DYNAMIC_PARALLELISM_REGION: - return uvmIoctlSimple[nvgpu.UVM_MAP_DYNAMIC_PARALLELISM_REGION_PARAMS](&ui) - case nvgpu.UVM_ALLOC_SEMAPHORE_POOL: - return uvmIoctlSimple[nvgpu.UVM_ALLOC_SEMAPHORE_POOL_PARAMS](&ui) - case nvgpu.UVM_VALIDATE_VA_RANGE: - return uvmIoctlSimple[nvgpu.UVM_VALIDATE_VA_RANGE_PARAMS](&ui) - case nvgpu.UVM_CREATE_EXTERNAL_RANGE: - return uvmIoctlSimple[nvgpu.UVM_CREATE_EXTERNAL_RANGE_PARAMS](&ui) - default: + handler := fd.nvp.abi.uvmIoctl[cmd] + if handler == nil { ctx.Warningf("nvproxy: unknown uvm ioctl %d", cmd) return 0, linuxerr.EINVAL } + return handler(&ui) } // uvmIoctlState holds the state of a call to uvmFD.Ioctl(). @@ -182,6 +148,10 @@ type uvmIoctlState struct { ioctlParamsAddr hostarch.Addr } +func uvmIoctlNoParams(ui *uvmIoctlState) (uintptr, error) { + return uvmIoctlInvoke[byte](ui, nil) +} + func uvmIoctlSimple[Params any, PParams marshalPtr[Params]](ui *uvmIoctlState) (uintptr, error) { var ioctlParams Params if _, err := (PParams)(&ioctlParams).CopyIn(ui.t, ui.ioctlParamsAddr); err != nil { diff --git a/pkg/sentry/devices/nvproxy/version.go b/pkg/sentry/devices/nvproxy/version.go new file mode 100644 index 000000000..dad6708ff --- /dev/null +++ b/pkg/sentry/devices/nvproxy/version.go @@ -0,0 +1,257 @@ +// 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 ( + "fmt" + "strconv" + "strings" + + "gvisor.dev/gvisor/pkg/abi/nvgpu" + "gvisor.dev/gvisor/pkg/sync" +) + +type driverVersion struct { + major int + minor int + patch int +} + +func driverVersionFrom(version string) (driverVersion, error) { + parts := strings.Split(version, ".") + if len(parts) != 3 { + return driverVersion{}, fmt.Errorf("invalid format of version string %q", version) + } + var ( + res driverVersion + err error + ) + res.major, err = strconv.Atoi(parts[0]) + if err != nil { + return driverVersion{}, fmt.Errorf("invalid format for major version %q: %v", version, err) + } + res.minor, err = strconv.Atoi(parts[1]) + if err != nil { + return driverVersion{}, fmt.Errorf("invalid format for minor version %q: %v", version, err) + } + res.patch, err = strconv.Atoi(parts[2]) + if err != nil { + return driverVersion{}, fmt.Errorf("invalid format for patch version %q: %v", version, err) + } + return res, nil +} + +func (v driverVersion) String() string { + return fmt.Sprintf("%02d.%02d.%02d", v.major, v.minor, v.patch) +} + +type frontendIoctlHandler func(fi *frontendIoctlState) (uintptr, error) +type controlCmdHandler func(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS54Parameters) (uintptr, error) +type allocationClassHandler func(fi *frontendIoctlState, ioctlParams *nvgpu.NVOS64Parameters, isNVOS64 bool) (uintptr, error) +type uvmIoctlHandler func(ui *uvmIoctlState) (uintptr, error) + +// A driverABIFunc constructs and returns a driverABI. +// This indirection exists to avoid memory usage from unused driver ABIs. +type driverABIFunc func() *driverABI + +// driverABI defines the Nvidia kernel driver ABI proxied at a given version. +// +// The Nvidia driver's ioctl interface branches widely at various places in the +// kernel driver. As for now, versioning is only supported for the following +// points of branching: +// 1. frontend device ioctls (based on IOC_NR(cmd)). +// 2. uvm device ioctls (based on cmd). +// 3. control commands within NV_ESC_RM_CONTROL in frontend device (based on +// NVOS54_PARAMETERS.Cmd). Note that commands that have RM_GSS_LEGACY_MASK +// set are not versioned. +// 4. allocation classes within NV_ESC_RM_ALLOC in frontend device (based on +// NVOS64_PARAMETERS.HClass). +type driverABI struct { + frontendIoctl map[uint32]frontendIoctlHandler + uvmIoctl map[uint32]uvmIoctlHandler + controlCmd map[uint32]controlCmdHandler + allocationClass map[uint32]allocationClassHandler +} + +// abis is a global map containing all supported Nvidia driver ABIs. This is +// initialized on Init() and is immutable henceforth. +var abis map[driverVersion]driverABIFunc +var abisOnce sync.Once + +func addDriverABI(major, minor, patch int, cons driverABIFunc) driverABIFunc { + if abis == nil { + abis = make(map[driverVersion]driverABIFunc) + } + abis[driverVersion{major, minor, patch}] = cons + return cons +} + +// Init initializes abis global map. +func Init() { + abisOnce.Do(func() { + v525_60_13 := addDriverABI(525, 60, 13, func() *driverABI { + // 525.60.13 is the earliest driver version supported by nvproxy. Since + // there is no parent to inherit from, the driverABI needs to be constructed + // with the entirety of the nvproxy functionality at this version. + return &driverABI{ + frontendIoctl: map[uint32]frontendIoctlHandler{ + nvgpu.NV_ESC_CARD_INFO: frontendIoctlSimple, // nv_ioctl_card_info_t + nvgpu.NV_ESC_CHECK_VERSION_STR: frontendIoctlSimple, // nv_rm_api_version_t + nvgpu.NV_ESC_SYS_PARAMS: frontendIoctlSimple, // nv_ioctl_sys_params_t + nvgpu.NV_ESC_RM_DUP_OBJECT: frontendIoctlSimple, // NVOS55_PARAMETERS + nvgpu.NV_ESC_RM_SHARE: frontendIoctlSimple, // NVOS57_PARAMETERS + nvgpu.NV_ESC_RM_UNMAP_MEMORY: frontendIoctlSimple, // NVOS34_PARAMETERS + nvgpu.NV_ESC_RM_UPDATE_DEVICE_MAPPING_INFO: frontendIoctlSimple, // NVOS56_PARAMETERS + nvgpu.NV_ESC_REGISTER_FD: frontendRegisterFD, + nvgpu.NV_ESC_ALLOC_OS_EVENT: rmAllocOSEvent, + nvgpu.NV_ESC_FREE_OS_EVENT: rmFreeOSEvent, + nvgpu.NV_ESC_NUMA_INFO: rmNumaInfo, + nvgpu.NV_ESC_RM_ALLOC_MEMORY: rmAllocMemory, + nvgpu.NV_ESC_RM_FREE: rmFree, + nvgpu.NV_ESC_RM_CONTROL: rmControl, + nvgpu.NV_ESC_RM_ALLOC: rmAlloc, + nvgpu.NV_ESC_RM_VID_HEAP_CONTROL: rmVidHeapControl, + nvgpu.NV_ESC_RM_MAP_MEMORY: rmMapMemory, + }, + uvmIoctl: map[uint32]uvmIoctlHandler{ + nvgpu.UVM_INITIALIZE: uvmInitialize, + nvgpu.UVM_DEINITIALIZE: uvmIoctlNoParams, + nvgpu.UVM_CREATE_RANGE_GROUP: uvmIoctlSimple[nvgpu.UVM_CREATE_RANGE_GROUP_PARAMS], + nvgpu.UVM_DESTROY_RANGE_GROUP: uvmIoctlSimple[nvgpu.UVM_DESTROY_RANGE_GROUP_PARAMS], + nvgpu.UVM_REGISTER_GPU_VASPACE: uvmIoctlHasRMCtrlFD[nvgpu.UVM_REGISTER_GPU_VASPACE_PARAMS], + nvgpu.UVM_UNREGISTER_GPU_VASPACE: uvmIoctlSimple[nvgpu.UVM_UNREGISTER_GPU_VASPACE_PARAMS], + nvgpu.UVM_REGISTER_CHANNEL: uvmIoctlHasRMCtrlFD[nvgpu.UVM_REGISTER_CHANNEL_PARAMS], + nvgpu.UVM_UNREGISTER_CHANNEL: uvmIoctlSimple[nvgpu.UVM_UNREGISTER_CHANNEL_PARAMS], + nvgpu.UVM_MAP_EXTERNAL_ALLOCATION: uvmIoctlHasRMCtrlFD[nvgpu.UVM_MAP_EXTERNAL_ALLOCATION_PARAMS], + nvgpu.UVM_FREE: uvmIoctlSimple[nvgpu.UVM_FREE_PARAMS], + nvgpu.UVM_REGISTER_GPU: uvmIoctlHasRMCtrlFD[nvgpu.UVM_REGISTER_GPU_PARAMS], + nvgpu.UVM_UNREGISTER_GPU: uvmIoctlSimple[nvgpu.UVM_UNREGISTER_GPU_PARAMS], + nvgpu.UVM_PAGEABLE_MEM_ACCESS: uvmIoctlSimple[nvgpu.UVM_PAGEABLE_MEM_ACCESS_PARAMS], + nvgpu.UVM_MAP_DYNAMIC_PARALLELISM_REGION: uvmIoctlSimple[nvgpu.UVM_MAP_DYNAMIC_PARALLELISM_REGION_PARAMS], + nvgpu.UVM_ALLOC_SEMAPHORE_POOL: uvmIoctlSimple[nvgpu.UVM_ALLOC_SEMAPHORE_POOL_PARAMS], + nvgpu.UVM_VALIDATE_VA_RANGE: uvmIoctlSimple[nvgpu.UVM_VALIDATE_VA_RANGE_PARAMS], + nvgpu.UVM_CREATE_EXTERNAL_RANGE: uvmIoctlSimple[nvgpu.UVM_CREATE_EXTERNAL_RANGE_PARAMS], + }, + controlCmd: map[uint32]controlCmdHandler{ + nvgpu.NV0000_CTRL_CMD_CLIENT_GET_ADDR_SPACE_TYPE: rmControlSimple, + nvgpu.NV0000_CTRL_CMD_CLIENT_SET_INHERITED_SHARE_POLICY: rmControlSimple, + nvgpu.NV0000_CTRL_CMD_GPU_GET_ATTACHED_IDS: rmControlSimple, + nvgpu.NV0000_CTRL_CMD_GPU_GET_ID_INFO: rmControlSimple, + nvgpu.NV0000_CTRL_CMD_GPU_GET_ID_INFO_V2: rmControlSimple, + nvgpu.NV0000_CTRL_CMD_GPU_GET_PROBED_IDS: rmControlSimple, + nvgpu.NV0000_CTRL_CMD_GPU_ATTACH_IDS: rmControlSimple, + nvgpu.NV0000_CTRL_CMD_GPU_DETACH_IDS: rmControlSimple, + nvgpu.NV0000_CTRL_CMD_GPU_GET_PCI_INFO: rmControlSimple, + nvgpu.NV0000_CTRL_CMD_GPU_QUERY_DRAIN_STATE: rmControlSimple, + nvgpu.NV0000_CTRL_CMD_GPU_GET_MEMOP_ENABLE: rmControlSimple, + nvgpu.NV0000_CTRL_CMD_SYNC_GPU_BOOST_GROUP_INFO: rmControlSimple, + nvgpu.NV0000_CTRL_CMD_SYSTEM_GET_P2P_CAPS: rmControlSimple, + nvgpu.NV0000_CTRL_CMD_SYSTEM_GET_FABRIC_STATUS: rmControlSimple, + nvgpu.NV0000_CTRL_CMD_SYSTEM_GET_P2P_CAPS_MATRIX: rmControlSimple, + nvgpu.NV0080_CTRL_CMD_FB_GET_CAPS_V2: rmControlSimple, + nvgpu.NV0080_CTRL_CMD_GPU_GET_NUM_SUBDEVICES: rmControlSimple, + nvgpu.NV0080_CTRL_CMD_GPU_QUERY_SW_STATE_PERSISTENCE: rmControlSimple, + nvgpu.NV0080_CTRL_CMD_GPU_GET_VIRTUALIZATION_MODE: rmControlSimple, + 0x80028b: rmControlSimple, // unknown, paramsSize == 1 + nvgpu.NV0080_CTRL_CMD_GPU_GET_CLASSLIST_V2: rmControlSimple, + nvgpu.NV0080_CTRL_CMD_HOST_GET_CAPS_V2: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_BUS_GET_PCI_INFO: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_BUS_GET_PCI_BAR_INFO: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_BUS_GET_INFO_V2: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_BUS_GET_PCIE_SUPPORTED_GPU_ATOMICS: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_CE_GET_ALL_CAPS: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_FB_GET_INFO_V2: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_GPU_GET_INFO_V2: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_GPU_GET_NAME_STRING: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_GPU_GET_SHORT_NAME_STRING: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_GPU_GET_SIMULATION_INFO: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_GPU_QUERY_ECC_STATUS: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_GPU_QUERY_COMPUTE_MODE_RULES: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_GPU_ACQUIRE_COMPUTE_MODE_RESERVATION: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_GPU_RELEASE_COMPUTE_MODE_RESERVATION: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_GPU_GET_GID_INFO: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_GPU_GET_ENGINES_V2: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_GPU_GET_ACTIVE_PARTITION_IDS: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_GPU_GET_COMPUTE_POLICY_CONFIG: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_GET_GPU_FABRIC_PROBE_INFO: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_GR_SET_CTXSW_PREEMPTION_MODE: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_GR_GET_CTX_BUFFER_SIZE: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_GR_GET_GLOBAL_SM_ORDER: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_GR_GET_CAPS_V2: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_GR_GET_GPC_MASK: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_GR_GET_TPC_MASK: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_GSP_GET_FEATURES: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_MC_GET_ARCH_INFO: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_MC_SERVICE_INTERRUPTS: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_NVLINK_GET_NVLINK_STATUS: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_PERF_BOOST: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_RC_GET_WATCHDOG_INFO: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_RC_RELEASE_WATCHDOG_REQUESTS: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_RC_SOFT_DISABLE_WATCHDOG: rmControlSimple, + nvgpu.NV2080_CTRL_CMD_TIMER_GET_GPU_CPU_TIME_CORRELATION_INFO: rmControlSimple, + nvgpu.NV503C_CTRL_CMD_REGISTER_VA_SPACE: rmControlSimple, + nvgpu.NV503C_CTRL_CMD_REGISTER_VIDMEM: rmControlSimple, + nvgpu.NV503C_CTRL_CMD_UNREGISTER_VIDMEM: rmControlSimple, + nvgpu.NV83DE_CTRL_CMD_DEBUG_SET_EXCEPTION_MASK: rmControlSimple, + nvgpu.NV83DE_CTRL_CMD_DEBUG_READ_ALL_SM_ERROR_STATES: rmControlSimple, + nvgpu.NV83DE_CTRL_CMD_DEBUG_CLEAR_ALL_SM_ERROR_STATES: rmControlSimple, + nvgpu.NV906F_CTRL_CMD_RESET_CHANNEL: rmControlSimple, + nvgpu.NV90E6_CTRL_CMD_MASTER_GET_VIRTUAL_FUNCTION_ERROR_CONT_INTR_MASK: rmControlSimple, + nvgpu.NVC36F_CTRL_GET_CLASS_ENGINEID: rmControlSimple, + nvgpu.NVC36F_CTRL_CMD_GPFIFO_GET_WORK_SUBMIT_TOKEN: rmControlSimple, + nvgpu.NVA06C_CTRL_CMD_GPFIFO_SCHEDULE: rmControlSimple, + nvgpu.NVA06C_CTRL_CMD_SET_TIMESLICE: rmControlSimple, + nvgpu.NVA06C_CTRL_CMD_PREEMPT: rmControlSimple, + nvgpu.NV0000_CTRL_CMD_SYSTEM_GET_BUILD_VERSION: ctrlClientSystemGetBuildVersion, + nvgpu.NV0080_CTRL_CMD_FIFO_GET_CHANNELLIST: ctrlDevFIFOGetChannelList, + nvgpu.NV2080_CTRL_CMD_FIFO_DISABLE_CHANNELS: ctrlSubdevFIFODisableChannels, + nvgpu.NV2080_CTRL_CMD_GR_GET_INFO: ctrlSubdevGRGetInfo, + }, + allocationClass: map[uint32]allocationClassHandler{ + nvgpu.NV01_ROOT: rmAllocSimple[nvgpu.Handle], + nvgpu.NV01_ROOT_NON_PRIV: rmAllocSimple[nvgpu.Handle], + nvgpu.NV01_ROOT_CLIENT: rmAllocSimple[nvgpu.Handle], + nvgpu.NV01_EVENT_OS_EVENT: rmAllocEventOSEvent, + nvgpu.NV01_DEVICE_0: rmAllocSimple[nvgpu.NV0080_ALLOC_PARAMETERS], + nvgpu.NV20_SUBDEVICE_0: rmAllocSimple[nvgpu.NV2080_ALLOC_PARAMETERS], + nvgpu.NV50_THIRD_PARTY_P2P: rmAllocSimple[nvgpu.NV503C_ALLOC_PARAMETERS], + nvgpu.GT200_DEBUGGER: rmAllocSimple[nvgpu.NV83DE_ALLOC_PARAMETERS], + nvgpu.FERMI_CONTEXT_SHARE_A: rmAllocSimple[nvgpu.NV_CTXSHARE_ALLOCATION_PARAMETERS], + nvgpu.FERMI_VASPACE_A: rmAllocSimple[nvgpu.NV_VASPACE_ALLOCATION_PARAMETERS], + nvgpu.KEPLER_CHANNEL_GROUP_A: rmAllocSimple[nvgpu.NV_CHANNEL_GROUP_ALLOCATION_PARAMETERS], + nvgpu.TURING_CHANNEL_GPFIFO_A: rmAllocSimple[nvgpu.NV_CHANNEL_ALLOC_PARAMS], + nvgpu.AMPERE_CHANNEL_GPFIFO_A: rmAllocSimple[nvgpu.NV_CHANNEL_ALLOC_PARAMS], + nvgpu.TURING_DMA_COPY_A: rmAllocSimple[nvgpu.NVB0B5_ALLOCATION_PARAMETERS], + nvgpu.AMPERE_DMA_COPY_A: rmAllocSimple[nvgpu.NVB0B5_ALLOCATION_PARAMETERS], + nvgpu.AMPERE_DMA_COPY_B: rmAllocSimple[nvgpu.NVB0B5_ALLOCATION_PARAMETERS], + nvgpu.HOPPER_DMA_COPY_A: rmAllocSimple[nvgpu.NVB0B5_ALLOCATION_PARAMETERS], + nvgpu.TURING_COMPUTE_A: rmAllocSimple[nvgpu.NV_GR_ALLOCATION_PARAMETERS], + nvgpu.AMPERE_COMPUTE_A: rmAllocSimple[nvgpu.NV_GR_ALLOCATION_PARAMETERS], + nvgpu.AMPERE_COMPUTE_B: rmAllocSimple[nvgpu.NV_GR_ALLOCATION_PARAMETERS], + nvgpu.ADA_COMPUTE_A: rmAllocSimple[nvgpu.NV_GR_ALLOCATION_PARAMETERS], + nvgpu.HOPPER_COMPUTE_A: rmAllocSimple[nvgpu.NV_GR_ALLOCATION_PARAMETERS], + nvgpu.HOPPER_USERMODE_A: rmAllocSimple[nvgpu.NV_HOPPER_USERMODE_A_PARAMS], + nvgpu.GF100_SUBDEVICE_MASTER: rmAllocNoParams, + nvgpu.TURING_USERMODE_A: rmAllocNoParams, + nvgpu.NV_MEMORY_FABRIC: rmAllocSimple[nvgpu.NV00F8_ALLOCATION_PARAMETERS], + }, + } + }) + + v525_105_17 := addDriverABI(525, 105, 17, v525_60_13) + + _ = addDriverABI(525, 125, 06, v525_105_17) + }) +} diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index 200a3369e..51d3e4374 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -38,6 +38,7 @@ import ( "gvisor.dev/gvisor/pkg/rand" "gvisor.dev/gvisor/pkg/refs" "gvisor.dev/gvisor/pkg/sentry/control" + "gvisor.dev/gvisor/pkg/sentry/devices/nvproxy" "gvisor.dev/gvisor/pkg/sentry/fdimport" "gvisor.dev/gvisor/pkg/sentry/fsimpl/host" "gvisor.dev/gvisor/pkg/sentry/fsimpl/tmpfs" @@ -304,6 +305,10 @@ func New(args Args) (*Loader, error) { return nil, fmt.Errorf("setting up memory usage: %w", err) } + if args.Conf.NVProxy { + nvproxy.Init() + } + kernel.IOUringEnabled = args.Conf.IOUring info := containerInfo{