2022-05-09 22:18:59 +02:00
|
|
|
/*******************************************************************************
|
|
|
|
Copyright (c) 2013-2021 NVIDIA Corporation
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU General Public License
|
|
|
|
as published by the Free Software Foundation; either version 2
|
|
|
|
of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
|
|
02110-1301, USA.
|
|
|
|
*******************************************************************************/
|
|
|
|
|
|
|
|
#include "uvm_common.h"
|
|
|
|
#include "uvm_linux.h"
|
|
|
|
#include "uvm_forward_decl.h"
|
|
|
|
|
|
|
|
// TODO: Bug 1710855: Tweak this number through benchmarks
|
|
|
|
#define UVM_SPIN_LOOP_SCHEDULE_TIMEOUT_NS (10*1000ULL)
|
|
|
|
#define UVM_SPIN_LOOP_PRINT_TIMEOUT_SEC 30ULL
|
|
|
|
|
|
|
|
// Default to debug prints being enabled for debug and develop builds and
|
|
|
|
// disabled for release builds.
|
|
|
|
static int uvm_debug_prints = UVM_IS_DEBUG() || UVM_IS_DEVELOP();
|
|
|
|
|
|
|
|
// Make the module param writable so that prints can be enabled or disabled at
|
|
|
|
// any time by modifying the module parameter.
|
|
|
|
module_param(uvm_debug_prints, int, S_IRUGO|S_IWUSR);
|
|
|
|
MODULE_PARM_DESC(uvm_debug_prints, "Enable uvm debug prints.");
|
|
|
|
|
|
|
|
bool uvm_debug_prints_enabled()
|
|
|
|
{
|
|
|
|
return uvm_debug_prints != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This parameter allows a program in user mode to call the kernel tests
|
|
|
|
// defined in this module. This parameter should only be used for testing and
|
|
|
|
// must not be set to true otherwise since it breaks security when it is
|
|
|
|
// enabled. By default and for safety reasons this parameter is set to false.
|
|
|
|
int uvm_enable_builtin_tests __read_mostly = 0;
|
|
|
|
module_param(uvm_enable_builtin_tests, int, S_IRUGO);
|
|
|
|
MODULE_PARM_DESC(uvm_enable_builtin_tests,
|
|
|
|
"Enable the UVM built-in tests. (This is a security risk)");
|
|
|
|
|
2022-09-20 22:54:59 +02:00
|
|
|
// Default to release asserts being enabled.
|
|
|
|
int uvm_release_asserts __read_mostly = 1;
|
|
|
|
|
|
|
|
// Make the module param writable so that release asserts can be enabled or
|
|
|
|
// disabled at any time by modifying the module parameter.
|
|
|
|
module_param(uvm_release_asserts, int, S_IRUGO|S_IWUSR);
|
|
|
|
MODULE_PARM_DESC(uvm_release_asserts, "Enable uvm asserts included in release builds.");
|
|
|
|
|
|
|
|
// Default to failed release asserts not dumping stack.
|
|
|
|
int uvm_release_asserts_dump_stack __read_mostly = 0;
|
|
|
|
|
|
|
|
// Make the module param writable so that dumping the stack can be enabled and
|
|
|
|
// disabled at any time by modifying the module parameter.
|
|
|
|
module_param(uvm_release_asserts_dump_stack, int, S_IRUGO|S_IWUSR);
|
|
|
|
MODULE_PARM_DESC(uvm_release_asserts_dump_stack, "dump_stack() on failed UVM release asserts.");
|
|
|
|
|
|
|
|
// Default to failed release asserts not setting the global UVM error.
|
|
|
|
int uvm_release_asserts_set_global_error __read_mostly = 0;
|
|
|
|
|
|
|
|
// Make the module param writable so that setting the global fatal error can be
|
|
|
|
// enabled and disabled at any time by modifying the module parameter.
|
|
|
|
module_param(uvm_release_asserts_set_global_error, int, S_IRUGO|S_IWUSR);
|
|
|
|
MODULE_PARM_DESC(uvm_release_asserts_set_global_error, "Set UVM global fatal error on failed release asserts.");
|
|
|
|
|
|
|
|
// A separate flag to enable setting global error, to be used by tests only.
|
|
|
|
bool uvm_release_asserts_set_global_error_for_tests __read_mostly = false;
|
|
|
|
|
2022-05-09 22:18:59 +02:00
|
|
|
//
|
|
|
|
// Convert kernel errno codes to corresponding NV_STATUS
|
|
|
|
//
|
|
|
|
NV_STATUS errno_to_nv_status(int errnoCode)
|
|
|
|
{
|
|
|
|
if (errnoCode < 0)
|
|
|
|
errnoCode = -errnoCode;
|
|
|
|
|
|
|
|
switch (errnoCode)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
return NV_OK;
|
|
|
|
|
|
|
|
case E2BIG:
|
|
|
|
case EINVAL:
|
|
|
|
return NV_ERR_INVALID_ARGUMENT;
|
|
|
|
|
|
|
|
case EACCES:
|
|
|
|
return NV_ERR_INVALID_ACCESS_TYPE;
|
|
|
|
|
|
|
|
case EADDRINUSE:
|
|
|
|
case EADDRNOTAVAIL:
|
|
|
|
return NV_ERR_UVM_ADDRESS_IN_USE;
|
|
|
|
|
|
|
|
case EFAULT:
|
|
|
|
return NV_ERR_INVALID_ADDRESS;
|
|
|
|
|
|
|
|
case EOVERFLOW:
|
|
|
|
return NV_ERR_OUT_OF_RANGE;
|
|
|
|
|
|
|
|
case EINTR:
|
|
|
|
case EBUSY:
|
|
|
|
case EAGAIN:
|
|
|
|
return NV_ERR_BUSY_RETRY;
|
|
|
|
|
|
|
|
case ENXIO:
|
|
|
|
case ENODEV:
|
|
|
|
return NV_ERR_MODULE_LOAD_FAILED;
|
|
|
|
|
|
|
|
case ENOMEM:
|
|
|
|
return NV_ERR_NO_MEMORY;
|
|
|
|
|
|
|
|
case EPERM:
|
|
|
|
return NV_ERR_INSUFFICIENT_PERMISSIONS;
|
|
|
|
|
|
|
|
case ESRCH:
|
|
|
|
return NV_ERR_PID_NOT_FOUND;
|
|
|
|
|
|
|
|
case ETIMEDOUT:
|
|
|
|
return NV_ERR_TIMEOUT;
|
|
|
|
|
|
|
|
case EEXIST:
|
|
|
|
return NV_ERR_IN_USE;
|
|
|
|
|
|
|
|
case ENOSYS:
|
|
|
|
case EOPNOTSUPP:
|
|
|
|
return NV_ERR_NOT_SUPPORTED;
|
|
|
|
|
|
|
|
case ENOENT:
|
|
|
|
return NV_ERR_NO_VALID_PATH;
|
|
|
|
|
|
|
|
case EIO:
|
|
|
|
return NV_ERR_RC_ERROR;
|
|
|
|
|
|
|
|
case ENODATA:
|
|
|
|
return NV_ERR_OBJECT_NOT_FOUND;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return NV_ERR_GENERIC;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns POSITIVE errno
|
|
|
|
int nv_status_to_errno(NV_STATUS status)
|
|
|
|
{
|
|
|
|
switch (status) {
|
|
|
|
case NV_OK:
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case NV_ERR_BUSY_RETRY:
|
|
|
|
return EAGAIN;
|
|
|
|
|
|
|
|
case NV_ERR_INSUFFICIENT_PERMISSIONS:
|
|
|
|
return EPERM;
|
|
|
|
|
|
|
|
case NV_ERR_GPU_UUID_NOT_FOUND:
|
|
|
|
return ENODEV;
|
|
|
|
|
|
|
|
case NV_ERR_INSUFFICIENT_RESOURCES:
|
|
|
|
case NV_ERR_NO_MEMORY:
|
|
|
|
return ENOMEM;
|
|
|
|
|
|
|
|
case NV_ERR_INVALID_ACCESS_TYPE:
|
|
|
|
return EACCES;
|
|
|
|
|
|
|
|
case NV_ERR_INVALID_ADDRESS:
|
|
|
|
return EFAULT;
|
|
|
|
|
|
|
|
case NV_ERR_INVALID_ARGUMENT:
|
|
|
|
case NV_ERR_INVALID_DEVICE:
|
|
|
|
case NV_ERR_INVALID_PARAMETER:
|
|
|
|
case NV_ERR_INVALID_REQUEST:
|
|
|
|
case NV_ERR_INVALID_STATE:
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
case NV_ERR_NOT_SUPPORTED:
|
|
|
|
return ENOSYS;
|
|
|
|
|
|
|
|
case NV_ERR_OBJECT_NOT_FOUND:
|
|
|
|
return ENODATA;
|
|
|
|
|
|
|
|
case NV_ERR_MODULE_LOAD_FAILED:
|
|
|
|
return ENXIO;
|
|
|
|
|
|
|
|
case NV_ERR_OVERLAPPING_UVM_COMMIT:
|
|
|
|
case NV_ERR_UVM_ADDRESS_IN_USE:
|
|
|
|
return EADDRINUSE;
|
|
|
|
|
|
|
|
case NV_ERR_PID_NOT_FOUND:
|
|
|
|
return ESRCH;
|
|
|
|
|
|
|
|
case NV_ERR_TIMEOUT:
|
|
|
|
case NV_ERR_TIMEOUT_RETRY:
|
|
|
|
return ETIMEDOUT;
|
|
|
|
|
|
|
|
case NV_ERR_IN_USE:
|
|
|
|
return EEXIST;
|
|
|
|
|
|
|
|
case NV_ERR_NO_VALID_PATH:
|
|
|
|
return ENOENT;
|
|
|
|
|
|
|
|
case NV_ERR_RC_ERROR:
|
|
|
|
case NV_ERR_ECC_ERROR:
|
|
|
|
return EIO;
|
|
|
|
|
|
|
|
case NV_ERR_OUT_OF_RANGE:
|
|
|
|
return EOVERFLOW;
|
|
|
|
|
|
|
|
default:
|
|
|
|
UVM_ASSERT_MSG(0, "No errno conversion set up for NV_STATUS %s\n", nvstatusToString(status));
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// This routine retrieves the process ID of current, but makes no attempt to
|
|
|
|
// refcount or lock the pid in place.
|
|
|
|
//
|
|
|
|
unsigned uvm_get_stale_process_id(void)
|
|
|
|
{
|
|
|
|
return (unsigned)task_tgid_vnr(current);
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned uvm_get_stale_thread_id(void)
|
|
|
|
{
|
|
|
|
return (unsigned)task_pid_vnr(current);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// A simple security rule for allowing access to UVM user space memory: if you
|
|
|
|
// are the same user as the owner of the memory, or if you are root, then you
|
|
|
|
// are granted access. The idea is to allow debuggers and profilers to work, but
|
|
|
|
// without opening up any security holes.
|
|
|
|
//
|
|
|
|
NvBool uvm_user_id_security_check(uid_t euidTarget)
|
|
|
|
{
|
|
|
|
return (NV_CURRENT_EUID() == euidTarget) ||
|
|
|
|
(UVM_ROOT_UID == euidTarget);
|
|
|
|
}
|
|
|
|
|
|
|
|
void on_uvm_test_fail(void)
|
|
|
|
{
|
|
|
|
(void)NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void on_uvm_assert(void)
|
|
|
|
{
|
|
|
|
(void)NULL;
|
|
|
|
#ifdef __COVERITY__
|
|
|
|
__coverity_panic__()
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
NV_STATUS uvm_spin_loop(uvm_spin_loop_t *spin)
|
|
|
|
{
|
|
|
|
NvU64 curr = NV_GETTIME();
|
|
|
|
|
|
|
|
// This schedule() is required for functionality, not just system
|
|
|
|
// performance. It allows RM to run and unblock the UVM driver:
|
|
|
|
//
|
|
|
|
// - UVM must service faults in order for RM to idle/preempt a context
|
|
|
|
// - RM must service interrupts which stall UVM (SW methods, stalling CE
|
|
|
|
// interrupts, etc) in order for UVM to service faults
|
|
|
|
//
|
|
|
|
// Even though UVM's bottom half is preemptable, we have encountered cases
|
|
|
|
// in which a user thread running in RM won't preempt the UVM driver's
|
|
|
|
// thread unless the UVM driver thread gives up its timeslice. This is also
|
|
|
|
// theoretically possible if the RM thread has a low nice priority.
|
|
|
|
//
|
|
|
|
// TODO: Bug 1710855: Look into proper prioritization of these threads as a longer-term
|
|
|
|
// solution.
|
|
|
|
if (curr - spin->start_time_ns >= UVM_SPIN_LOOP_SCHEDULE_TIMEOUT_NS && NV_MAY_SLEEP()) {
|
|
|
|
schedule();
|
|
|
|
curr = NV_GETTIME();
|
|
|
|
}
|
|
|
|
|
|
|
|
cpu_relax();
|
|
|
|
|
|
|
|
// TODO: Bug 1710855: Also check fatal_signal_pending() here if the caller can handle it.
|
|
|
|
|
|
|
|
if (curr - spin->print_time_ns >= 1000*1000*1000*UVM_SPIN_LOOP_PRINT_TIMEOUT_SEC) {
|
|
|
|
spin->print_time_ns = curr;
|
|
|
|
return NV_ERR_TIMEOUT_RETRY;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NV_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This formats a GPU UUID, in a UVM-friendly way. That is, nearly the same as
|
|
|
|
// what nvidia-smi reports. It will always prefix the UUID with UVM-GPU so
|
|
|
|
// that we know that we have a real, binary formatted UUID that will work in
|
|
|
|
// the UVM APIs.
|
|
|
|
//
|
|
|
|
// It comes out like this:
|
|
|
|
//
|
|
|
|
// UVM-GPU-d802726c-df8d-a3c3-ec53-48bdec201c27
|
|
|
|
//
|
|
|
|
// This routine will always null-terminate the string for you. This is true
|
|
|
|
// even if the buffer was too small!
|
|
|
|
//
|
|
|
|
// Return value is the number of non-null characters written.
|
|
|
|
//
|
|
|
|
// Note that if you were to let the NV2080_CTRL_CMD_GPU_GET_GID_INFO command
|
|
|
|
// return it's default format, which is ascii, not binary, then you would get
|
|
|
|
// this back:
|
|
|
|
//
|
|
|
|
// GPU-d802726c-df8d-a3c3-ec53-48bdec201c27
|
|
|
|
//
|
|
|
|
// ...which is actually a character string, and won't work for UVM API calls.
|
|
|
|
// So it's very important to be able to see the difference.
|
|
|
|
//
|
|
|
|
static char uvm_digit_to_hex(unsigned value)
|
|
|
|
{
|
|
|
|
if (value >= 10)
|
|
|
|
return value - 10 + 'a';
|
|
|
|
else
|
|
|
|
return value + '0';
|
|
|
|
}
|
|
|
|
|
|
|
|
int format_uuid_to_buffer(char *buffer, unsigned bufferLength, const NvProcessorUuid *pUuidStruct)
|
|
|
|
{
|
|
|
|
char *str = buffer+8;
|
|
|
|
unsigned i;
|
|
|
|
unsigned dashMask = 1 << 4 | 1 << 6 | 1 << 8 | 1 << 10;
|
|
|
|
|
|
|
|
memcpy(buffer, "UVM-GPU-", 8);
|
|
|
|
if (bufferLength < (8 /*prefix*/+ 16 * 2 /*digits*/ + 4 * 1 /*dashes*/ + 1 /*null*/))
|
|
|
|
return *buffer = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < 16; i++) {
|
|
|
|
*str++ = uvm_digit_to_hex(pUuidStruct->uuid[i] >> 4);
|
|
|
|
*str++ = uvm_digit_to_hex(pUuidStruct->uuid[i] & 0xF);
|
|
|
|
|
|
|
|
if (dashMask & (1 << (i+1)))
|
|
|
|
*str++ = '-';
|
|
|
|
}
|
|
|
|
|
|
|
|
*str = 0;
|
|
|
|
|
|
|
|
return (int)(str-buffer);
|
|
|
|
}
|
|
|
|
|