mirror of
https://github.com/NVIDIA/open-gpu-kernel-modules.git
synced 2025-02-20 15:54:20 +01:00
545.23.06
This commit is contained in:
parent
f59818b751
commit
b5bf85a8e3
12
CHANGELOG.md
12
CHANGELOG.md
@ -1,5 +1,17 @@
|
||||
# Changelog
|
||||
|
||||
## Release 545 Entries
|
||||
|
||||
### [545.23.06] 2023-10-17
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Fix always-false conditional, [#493](https://github.com/NVIDIA/open-gpu-kernel-modules/pull/493) by @meme8383
|
||||
|
||||
#### Added
|
||||
|
||||
- Added beta-quality support for GeForce and Workstation GPUs. Please see the "Open Linux Kernel Modules" chapter in the NVIDIA GPU driver end user README for details.
|
||||
|
||||
## Release 535 Entries
|
||||
|
||||
### [535.113.01] 2023-09-21
|
||||
|
20
README.md
20
README.md
@ -1,7 +1,7 @@
|
||||
# NVIDIA Linux Open GPU Kernel Module Source
|
||||
|
||||
This is the source release of the NVIDIA Linux open GPU kernel modules,
|
||||
version 535.113.01.
|
||||
version 545.23.06.
|
||||
|
||||
|
||||
## How to Build
|
||||
@ -17,7 +17,7 @@ as root:
|
||||
|
||||
Note that the kernel modules built here must be used with GSP
|
||||
firmware and user-space NVIDIA GPU driver components from a corresponding
|
||||
535.113.01 driver release. This can be achieved by installing
|
||||
545.23.06 driver release. This can be achieved by installing
|
||||
the NVIDIA GPU driver from the .run file using the `--no-kernel-modules`
|
||||
option. E.g.,
|
||||
|
||||
@ -179,16 +179,16 @@ software applications.
|
||||
|
||||
## Compatible GPUs
|
||||
|
||||
The open-gpu-kernel-modules can be used on any Turing or later GPU
|
||||
(see the table below). However, in the 535.113.01 release,
|
||||
GeForce and Workstation support is still considered alpha-quality.
|
||||
The NVIDIA open kernel modules can be used on any Turing or later GPU
|
||||
(see the table below). However, in the __DRIVER_VERION__ release, GeForce and
|
||||
Workstation support is considered to be Beta quality. The open kernel modules
|
||||
are suitable for broad usage, and NVIDIA requests feedback on any issues
|
||||
encountered specific to them.
|
||||
|
||||
To enable use of the open kernel modules on GeForce and Workstation GPUs,
|
||||
set the "NVreg_OpenRmEnableUnsupportedGpus" nvidia.ko kernel module
|
||||
parameter to 1. For more details, see the NVIDIA GPU driver end user
|
||||
README here:
|
||||
For details on feature support and limitations, see the NVIDIA GPU driver
|
||||
end user README here:
|
||||
|
||||
https://us.download.nvidia.com/XFree86/Linux-x86_64/535.113.01/README/kernel_open.html
|
||||
https://us.download.nvidia.com/XFree86/Linux-x86_64/545.23.06/README/kernel_open.html
|
||||
|
||||
In the below table, if three IDs are listed, the first is the PCI Device
|
||||
ID, the second is the PCI Subsystem Vendor ID, and the third is the PCI
|
||||
|
@ -72,12 +72,24 @@ EXTRA_CFLAGS += -I$(src)/common/inc
|
||||
EXTRA_CFLAGS += -I$(src)
|
||||
EXTRA_CFLAGS += -Wall $(DEFINES) $(INCLUDES) -Wno-cast-qual -Wno-error -Wno-format-extra-args
|
||||
EXTRA_CFLAGS += -D__KERNEL__ -DMODULE -DNVRM
|
||||
EXTRA_CFLAGS += -DNV_VERSION_STRING=\"535.113.01\"
|
||||
EXTRA_CFLAGS += -DNV_VERSION_STRING=\"545.23.06\"
|
||||
|
||||
ifneq ($(SYSSRCHOST1X),)
|
||||
EXTRA_CFLAGS += -I$(SYSSRCHOST1X)
|
||||
endif
|
||||
|
||||
# Some Android kernels prohibit driver use of filesystem functions like
|
||||
# filp_open() and kernel_read(). Disable the NV_FILESYSTEM_ACCESS_AVAILABLE
|
||||
# functionality that uses those functions when building for Android.
|
||||
|
||||
PLATFORM_IS_ANDROID ?= 0
|
||||
|
||||
ifeq ($(PLATFORM_IS_ANDROID),1)
|
||||
EXTRA_CFLAGS += -DNV_FILESYSTEM_ACCESS_AVAILABLE=0
|
||||
else
|
||||
EXTRA_CFLAGS += -DNV_FILESYSTEM_ACCESS_AVAILABLE=1
|
||||
endif
|
||||
|
||||
EXTRA_CFLAGS += -Wno-unused-function
|
||||
|
||||
ifneq ($(NV_BUILD_TYPE),debug)
|
||||
@ -92,7 +104,6 @@ endif
|
||||
|
||||
ifeq ($(NV_BUILD_TYPE),debug)
|
||||
EXTRA_CFLAGS += -g
|
||||
EXTRA_CFLAGS += $(call cc-option,-gsplit-dwarf,)
|
||||
endif
|
||||
|
||||
EXTRA_CFLAGS += -ffreestanding
|
||||
@ -214,6 +225,7 @@ $(obj)/conftest/patches.h: $(NV_CONFTEST_SCRIPT)
|
||||
NV_HEADER_PRESENCE_TESTS = \
|
||||
asm/system.h \
|
||||
drm/drmP.h \
|
||||
drm/drm_aperture.h \
|
||||
drm/drm_auth.h \
|
||||
drm/drm_gem.h \
|
||||
drm/drm_crtc.h \
|
||||
@ -224,6 +236,7 @@ NV_HEADER_PRESENCE_TESTS = \
|
||||
drm/drm_encoder.h \
|
||||
drm/drm_atomic_uapi.h \
|
||||
drm/drm_drv.h \
|
||||
drm/drm_fbdev_generic.h \
|
||||
drm/drm_framebuffer.h \
|
||||
drm/drm_connector.h \
|
||||
drm/drm_probe_helper.h \
|
||||
@ -257,6 +270,7 @@ NV_HEADER_PRESENCE_TESTS = \
|
||||
linux/sched/task_stack.h \
|
||||
xen/ioemu.h \
|
||||
linux/fence.h \
|
||||
linux/dma-fence.h \
|
||||
linux/dma-resv.h \
|
||||
soc/tegra/chip-id.h \
|
||||
soc/tegra/fuse.h \
|
||||
@ -302,6 +316,7 @@ NV_HEADER_PRESENCE_TESTS = \
|
||||
linux/mdev.h \
|
||||
soc/tegra/bpmp-abi.h \
|
||||
soc/tegra/bpmp.h \
|
||||
linux/sync_file.h \
|
||||
linux/cc_platform.h \
|
||||
asm/cpufeature.h
|
||||
|
||||
|
43
kernel-open/common/inc/nv-chardev-numbers.h
Normal file
43
kernel-open/common/inc/nv-chardev-numbers.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NV_CHARDEV_NUMBERS_H_
|
||||
#define _NV_CHARDEV_NUMBERS_H_
|
||||
|
||||
// NVIDIA's reserved major character device number (Linux).
|
||||
#define NV_MAJOR_DEVICE_NUMBER 195
|
||||
|
||||
// Minor numbers 0 to 247 reserved for regular devices
|
||||
#define NV_MINOR_DEVICE_NUMBER_REGULAR_MAX 247
|
||||
|
||||
// Minor numbers 248 to 253 currently unused
|
||||
|
||||
// Minor number 254 reserved for the modeset device (provided by NVKMS)
|
||||
#define NV_MINOR_DEVICE_NUMBER_MODESET_DEVICE 254
|
||||
|
||||
// Minor number 255 reserved for the control device
|
||||
#define NV_MINOR_DEVICE_NUMBER_CONTROL_DEVICE 255
|
||||
|
||||
#endif // _NV_CHARDEV_NUMBERS_H_
|
||||
|
@ -25,14 +25,12 @@
|
||||
#ifndef NV_IOCTL_NUMA_H
|
||||
#define NV_IOCTL_NUMA_H
|
||||
|
||||
#if defined(NV_LINUX)
|
||||
|
||||
#include <nv-ioctl-numbers.h>
|
||||
|
||||
#if defined(NV_KERNEL_INTERFACE_LAYER)
|
||||
|
||||
#if defined(NV_KERNEL_INTERFACE_LAYER) && defined(NV_LINUX)
|
||||
#include <linux/types.h>
|
||||
|
||||
#elif defined (NV_KERNEL_INTERFACE_LAYER) && defined(NV_BSD)
|
||||
#include <sys/stdint.h>
|
||||
#else
|
||||
|
||||
#include <stdint.h>
|
||||
@ -81,5 +79,3 @@ typedef struct nv_ioctl_set_numa_status
|
||||
#define NV_IOCTL_NUMA_STATUS_OFFLINE_FAILED 6
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2012-2013 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2016 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
@ -21,27 +21,42 @@
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _NV_FRONTEND_H_
|
||||
#define _NV_FRONTEND_H_
|
||||
#ifndef __NV_KTHREAD_QUEUE_OS_H__
|
||||
#define __NV_KTHREAD_QUEUE_OS_H__
|
||||
|
||||
#include "nvtypes.h"
|
||||
#include "nv-linux.h"
|
||||
#include "nv-register-module.h"
|
||||
#include <linux/types.h> // atomic_t
|
||||
#include <linux/list.h> // list
|
||||
#include <linux/sched.h> // task_struct
|
||||
#include <linux/numa.h> // NUMA_NO_NODE
|
||||
#include <linux/semaphore.h>
|
||||
|
||||
#define NV_MAX_MODULE_INSTANCES 8
|
||||
#include "conftest.h"
|
||||
|
||||
#define NV_FRONTEND_MINOR_NUMBER(x) minor((x)->i_rdev)
|
||||
struct nv_kthread_q
|
||||
{
|
||||
struct list_head q_list_head;
|
||||
spinlock_t q_lock;
|
||||
|
||||
#define NV_FRONTEND_CONTROL_DEVICE_MINOR_MAX 255
|
||||
#define NV_FRONTEND_CONTROL_DEVICE_MINOR_MIN (NV_FRONTEND_CONTROL_DEVICE_MINOR_MAX - \
|
||||
NV_MAX_MODULE_INSTANCES)
|
||||
// This is a counting semaphore. It gets incremented and decremented
|
||||
// exactly once for each item that is added to the queue.
|
||||
struct semaphore q_sem;
|
||||
atomic_t main_loop_should_exit;
|
||||
|
||||
#define NV_FRONTEND_IS_CONTROL_DEVICE(x) ((x <= NV_FRONTEND_CONTROL_DEVICE_MINOR_MAX) && \
|
||||
(x > NV_FRONTEND_CONTROL_DEVICE_MINOR_MIN))
|
||||
struct task_struct *q_kthread;
|
||||
};
|
||||
|
||||
int nvidia_frontend_add_device(nvidia_module_t *, nv_linux_state_t *);
|
||||
int nvidia_frontend_remove_device(nvidia_module_t *, nv_linux_state_t *);
|
||||
struct nv_kthread_q_item
|
||||
{
|
||||
struct list_head q_list_node;
|
||||
nv_q_func_t function_to_run;
|
||||
void *function_args;
|
||||
};
|
||||
|
||||
extern nvidia_module_t *nv_minor_num_table[];
|
||||
|
||||
#ifndef NUMA_NO_NODE
|
||||
#define NUMA_NO_NODE (-1)
|
||||
#endif
|
||||
|
||||
#define NV_KTHREAD_NO_NODE NUMA_NO_NODE
|
||||
|
||||
#endif
|
@ -24,13 +24,14 @@
|
||||
#ifndef __NV_KTHREAD_QUEUE_H__
|
||||
#define __NV_KTHREAD_QUEUE_H__
|
||||
|
||||
#include <linux/types.h> // atomic_t
|
||||
#include <linux/list.h> // list
|
||||
#include <linux/sched.h> // task_struct
|
||||
#include <linux/numa.h> // NUMA_NO_NODE
|
||||
#include <linux/semaphore.h>
|
||||
struct nv_kthread_q;
|
||||
struct nv_kthread_q_item;
|
||||
typedef struct nv_kthread_q nv_kthread_q_t;
|
||||
typedef struct nv_kthread_q_item nv_kthread_q_item_t;
|
||||
|
||||
#include "conftest.h"
|
||||
typedef void (*nv_q_func_t)(void *args);
|
||||
|
||||
#include "nv-kthread-q-os.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nv_kthread_q:
|
||||
@ -85,38 +86,6 @@
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef struct nv_kthread_q nv_kthread_q_t;
|
||||
typedef struct nv_kthread_q_item nv_kthread_q_item_t;
|
||||
|
||||
typedef void (*nv_q_func_t)(void *args);
|
||||
|
||||
struct nv_kthread_q
|
||||
{
|
||||
struct list_head q_list_head;
|
||||
spinlock_t q_lock;
|
||||
|
||||
// This is a counting semaphore. It gets incremented and decremented
|
||||
// exactly once for each item that is added to the queue.
|
||||
struct semaphore q_sem;
|
||||
atomic_t main_loop_should_exit;
|
||||
|
||||
struct task_struct *q_kthread;
|
||||
};
|
||||
|
||||
struct nv_kthread_q_item
|
||||
{
|
||||
struct list_head q_list_node;
|
||||
nv_q_func_t function_to_run;
|
||||
void *function_args;
|
||||
};
|
||||
|
||||
|
||||
#ifndef NUMA_NO_NODE
|
||||
#define NUMA_NO_NODE (-1)
|
||||
#endif
|
||||
|
||||
#define NV_KTHREAD_NO_NODE NUMA_NO_NODE
|
||||
|
||||
//
|
||||
// The queue must not be used before calling this routine.
|
||||
//
|
||||
@ -155,10 +124,7 @@ int nv_kthread_q_init_on_node(nv_kthread_q_t *q,
|
||||
// This routine is the same as nv_kthread_q_init_on_node() with the exception
|
||||
// that the queue stack will be allocated on the NUMA node of the caller.
|
||||
//
|
||||
static inline int nv_kthread_q_init(nv_kthread_q_t *q, const char *qname)
|
||||
{
|
||||
return nv_kthread_q_init_on_node(q, qname, NV_KTHREAD_NO_NODE);
|
||||
}
|
||||
int nv_kthread_q_init(nv_kthread_q_t *q, const char *qname);
|
||||
|
||||
//
|
||||
// The caller is responsible for stopping all queues, by calling this routine
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2001-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2001-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
@ -248,7 +248,7 @@ NV_STATUS nvos_forward_error_to_cray(struct pci_dev *, NvU32,
|
||||
#undef NV_SET_PAGES_UC_PRESENT
|
||||
#endif
|
||||
|
||||
#if !defined(NVCPU_AARCH64) && !defined(NVCPU_PPC64LE)
|
||||
#if !defined(NVCPU_AARCH64) && !defined(NVCPU_PPC64LE) && !defined(NVCPU_RISCV64)
|
||||
#if !defined(NV_SET_MEMORY_UC_PRESENT) && !defined(NV_SET_PAGES_UC_PRESENT)
|
||||
#error "This driver requires the ability to change memory types!"
|
||||
#endif
|
||||
@ -430,6 +430,11 @@ extern NvBool nvos_is_chipset_io_coherent(void);
|
||||
#define CACHE_FLUSH() asm volatile("sync; \n" \
|
||||
"isync; \n" ::: "memory")
|
||||
#define WRITE_COMBINE_FLUSH() CACHE_FLUSH()
|
||||
#elif defined(NVCPU_RISCV64)
|
||||
#define CACHE_FLUSH() mb()
|
||||
#define WRITE_COMBINE_FLUSH() CACHE_FLUSH()
|
||||
#else
|
||||
#error "CACHE_FLUSH() and WRITE_COMBINE_FLUSH() need to be defined for this architecture."
|
||||
#endif
|
||||
|
||||
typedef enum
|
||||
@ -440,7 +445,7 @@ typedef enum
|
||||
NV_MEMORY_TYPE_DEVICE_MMIO, /* All kinds of MMIO referred by NVRM e.g. BARs and MCFG of device */
|
||||
} nv_memory_type_t;
|
||||
|
||||
#if defined(NVCPU_AARCH64) || defined(NVCPU_PPC64LE)
|
||||
#if defined(NVCPU_AARCH64) || defined(NVCPU_PPC64LE) || defined(NVCPU_RISCV64)
|
||||
#define NV_ALLOW_WRITE_COMBINING(mt) 1
|
||||
#elif defined(NVCPU_X86_64)
|
||||
#if defined(NV_ENABLE_PAT_SUPPORT)
|
||||
@ -753,7 +758,6 @@ static inline dma_addr_t nv_phys_to_dma(struct device *dev, NvU64 pa)
|
||||
#define NV_VMA_FILE(vma) ((vma)->vm_file)
|
||||
|
||||
#define NV_DEVICE_MINOR_NUMBER(x) minor((x)->i_rdev)
|
||||
#define NV_CONTROL_DEVICE_MINOR 255
|
||||
|
||||
#define NV_PCI_DISABLE_DEVICE(pci_dev) \
|
||||
{ \
|
||||
@ -1646,20 +1650,11 @@ typedef struct nvidia_event
|
||||
nv_event_t event;
|
||||
} nvidia_event_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
NV_FOPS_STACK_INDEX_MMAP,
|
||||
NV_FOPS_STACK_INDEX_IOCTL,
|
||||
NV_FOPS_STACK_INDEX_COUNT
|
||||
} nvidia_entry_point_index_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
nv_file_private_t nvfp;
|
||||
|
||||
nvidia_stack_t *sp;
|
||||
nvidia_stack_t *fops_sp[NV_FOPS_STACK_INDEX_COUNT];
|
||||
struct semaphore fops_sp_lock[NV_FOPS_STACK_INDEX_COUNT];
|
||||
nv_alloc_t *free_list;
|
||||
void *nvptr;
|
||||
nvidia_event_t *event_data_head, *event_data_tail;
|
||||
@ -1689,28 +1684,6 @@ static inline nv_linux_file_private_t *nv_get_nvlfp_from_nvfp(nv_file_private_t
|
||||
|
||||
#define NV_STATE_PTR(nvl) &(((nv_linux_state_t *)(nvl))->nv_state)
|
||||
|
||||
static inline nvidia_stack_t *nv_nvlfp_get_sp(nv_linux_file_private_t *nvlfp, nvidia_entry_point_index_t which)
|
||||
{
|
||||
#if defined(NVCPU_X86_64)
|
||||
if (rm_is_altstack_in_use())
|
||||
{
|
||||
down(&nvlfp->fops_sp_lock[which]);
|
||||
return nvlfp->fops_sp[which];
|
||||
}
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void nv_nvlfp_put_sp(nv_linux_file_private_t *nvlfp, nvidia_entry_point_index_t which)
|
||||
{
|
||||
#if defined(NVCPU_X86_64)
|
||||
if (rm_is_altstack_in_use())
|
||||
{
|
||||
up(&nvlfp->fops_sp_lock[which]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#define NV_ATOMIC_READ(data) atomic_read(&(data))
|
||||
#define NV_ATOMIC_SET(data,val) atomic_set(&(data), (val))
|
||||
#define NV_ATOMIC_INC(data) atomic_inc(&(data))
|
||||
|
@ -119,6 +119,13 @@ static inline pgprot_t pgprot_modify_writecombine(pgprot_t old_prot)
|
||||
#define NV_PGPROT_WRITE_COMBINED(old_prot) old_prot
|
||||
#define NV_PGPROT_READ_ONLY(old_prot) \
|
||||
__pgprot(pgprot_val((old_prot)) & ~NV_PAGE_RW)
|
||||
#elif defined(NVCPU_RISCV64)
|
||||
#define NV_PGPROT_WRITE_COMBINED_DEVICE(old_prot) \
|
||||
pgprot_writecombine(old_prot)
|
||||
/* Don't attempt to mark sysmem pages as write combined on riscv */
|
||||
#define NV_PGPROT_WRITE_COMBINED(old_prot) old_prot
|
||||
#define NV_PGPROT_READ_ONLY(old_prot) \
|
||||
__pgprot(pgprot_val((old_prot)) & ~_PAGE_WRITE)
|
||||
#else
|
||||
/* Writecombine is not supported */
|
||||
#undef NV_PGPROT_WRITE_COMBINED_DEVICE(old_prot)
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 1999-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-FileCopyrightText: Copyright (c) 1999-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
@ -25,10 +25,8 @@
|
||||
#define _NV_PROTO_H_
|
||||
|
||||
#include "nv-pci.h"
|
||||
#include "nv-register-module.h"
|
||||
|
||||
extern const char *nv_device_name;
|
||||
extern nvidia_module_t nv_fops;
|
||||
|
||||
void nv_acpi_register_notifier (nv_linux_state_t *);
|
||||
void nv_acpi_unregister_notifier (nv_linux_state_t *);
|
||||
@ -86,7 +84,7 @@ void nv_shutdown_adapter(nvidia_stack_t *, nv_state_t *, nv_linux_state
|
||||
void nv_dev_free_stacks(nv_linux_state_t *);
|
||||
NvBool nv_lock_init_locks(nvidia_stack_t *, nv_state_t *);
|
||||
void nv_lock_destroy_locks(nvidia_stack_t *, nv_state_t *);
|
||||
void nv_linux_add_device_locked(nv_linux_state_t *);
|
||||
int nv_linux_add_device_locked(nv_linux_state_t *);
|
||||
void nv_linux_remove_device_locked(nv_linux_state_t *);
|
||||
NvBool nv_acpi_power_resource_method_present(struct pci_dev *);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 1999-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-FileCopyrightText: Copyright (c) 1999-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
@ -42,6 +42,7 @@
|
||||
#include <nv-caps.h>
|
||||
#include <nv-firmware.h>
|
||||
#include <nv-ioctl.h>
|
||||
#include <nv-ioctl-numa.h>
|
||||
#include <nvmisc.h>
|
||||
|
||||
extern nv_cap_t *nvidia_caps_root;
|
||||
@ -50,9 +51,6 @@ extern const NvBool nv_is_rm_firmware_supported_os;
|
||||
|
||||
#include <nv-kernel-interface-api.h>
|
||||
|
||||
/* NVIDIA's reserved major character device number (Linux). */
|
||||
#define NV_MAJOR_DEVICE_NUMBER 195
|
||||
|
||||
#define GPU_UUID_LEN (16)
|
||||
|
||||
/*
|
||||
@ -478,8 +476,6 @@ typedef struct nv_state_t
|
||||
/* Bool to check if dma-buf is supported */
|
||||
NvBool dma_buf_supported;
|
||||
|
||||
NvBool printed_openrm_enable_unsupported_gpus_error;
|
||||
|
||||
/* Check if NVPCF DSM function is implemented under NVPCF or GPU device scope */
|
||||
NvBool nvpcf_dsm_in_gpu_scope;
|
||||
|
||||
@ -505,6 +501,7 @@ struct nv_file_private_t
|
||||
NvHandle *handles;
|
||||
NvU16 maxHandles;
|
||||
NvU32 deviceInstance;
|
||||
NvU32 gpuInstanceId;
|
||||
NvU8 metadata[64];
|
||||
|
||||
nv_file_private_t *ctl_nvfp;
|
||||
@ -765,7 +762,7 @@ nv_state_t* NV_API_CALL nv_get_ctl_state (void);
|
||||
void NV_API_CALL nv_set_dma_address_size (nv_state_t *, NvU32 );
|
||||
|
||||
NV_STATUS NV_API_CALL nv_alias_pages (nv_state_t *, NvU32, NvU32, NvU32, NvU64, NvU64 *, void **);
|
||||
NV_STATUS NV_API_CALL nv_alloc_pages (nv_state_t *, NvU32, NvBool, NvU32, NvBool, NvBool, NvS32, NvU64 *, void **);
|
||||
NV_STATUS NV_API_CALL nv_alloc_pages (nv_state_t *, NvU32, NvU64, NvBool, NvU32, NvBool, NvBool, NvS32, NvU64 *, void **);
|
||||
NV_STATUS NV_API_CALL nv_free_pages (nv_state_t *, NvU32, NvBool, NvU32, void *);
|
||||
|
||||
NV_STATUS NV_API_CALL nv_register_user_pages (nv_state_t *, NvU64, NvU64 *, void *, void **);
|
||||
@ -981,7 +978,7 @@ NV_STATUS NV_API_CALL rm_dma_buf_dup_mem_handle (nvidia_stack_t *, nv_state_t
|
||||
void NV_API_CALL rm_dma_buf_undup_mem_handle(nvidia_stack_t *, nv_state_t *, NvHandle, NvHandle);
|
||||
NV_STATUS NV_API_CALL rm_dma_buf_map_mem_handle (nvidia_stack_t *, nv_state_t *, NvHandle, NvHandle, NvU64, NvU64, void *, nv_phys_addr_range_t **, NvU32 *);
|
||||
void NV_API_CALL rm_dma_buf_unmap_mem_handle(nvidia_stack_t *, nv_state_t *, NvHandle, NvHandle, NvU64, nv_phys_addr_range_t **, NvU32);
|
||||
NV_STATUS NV_API_CALL rm_dma_buf_get_client_and_device(nvidia_stack_t *, nv_state_t *, NvHandle, NvHandle *, NvHandle *, NvHandle *, void **, NvBool *);
|
||||
NV_STATUS NV_API_CALL rm_dma_buf_get_client_and_device(nvidia_stack_t *, nv_state_t *, NvHandle, NvHandle, NvHandle *, NvHandle *, NvHandle *, void **, NvBool *);
|
||||
void NV_API_CALL rm_dma_buf_put_client_and_device(nvidia_stack_t *, nv_state_t *, NvHandle, NvHandle, NvHandle, void *);
|
||||
NV_STATUS NV_API_CALL rm_log_gpu_crash (nv_stack_t *, nv_state_t *);
|
||||
|
||||
@ -993,7 +990,7 @@ NvBool NV_API_CALL rm_gpu_need_4k_page_isolation(nv_state_t *);
|
||||
NvBool NV_API_CALL rm_is_chipset_io_coherent(nv_stack_t *);
|
||||
NvBool NV_API_CALL rm_init_event_locks(nvidia_stack_t *, nv_state_t *);
|
||||
void NV_API_CALL rm_destroy_event_locks(nvidia_stack_t *, nv_state_t *);
|
||||
NV_STATUS NV_API_CALL rm_get_gpu_numa_info(nvidia_stack_t *, nv_state_t *, NvS32 *, NvU64 *, NvU64 *, NvU64 *, NvU32 *);
|
||||
NV_STATUS NV_API_CALL rm_get_gpu_numa_info(nvidia_stack_t *, nv_state_t *, nv_ioctl_numa_info_t *);
|
||||
NV_STATUS NV_API_CALL rm_gpu_numa_online(nvidia_stack_t *, nv_state_t *);
|
||||
NV_STATUS NV_API_CALL rm_gpu_numa_offline(nvidia_stack_t *, nv_state_t *);
|
||||
NvBool NV_API_CALL rm_is_device_sequestered(nvidia_stack_t *, nv_state_t *);
|
||||
@ -1008,7 +1005,7 @@ void NV_API_CALL rm_cleanup_dynamic_power_management(nvidia_stack_t *, nv_
|
||||
void NV_API_CALL rm_enable_dynamic_power_management(nvidia_stack_t *, nv_state_t *);
|
||||
NV_STATUS NV_API_CALL rm_ref_dynamic_power(nvidia_stack_t *, nv_state_t *, nv_dynamic_power_mode_t);
|
||||
void NV_API_CALL rm_unref_dynamic_power(nvidia_stack_t *, nv_state_t *, nv_dynamic_power_mode_t);
|
||||
NV_STATUS NV_API_CALL rm_transition_dynamic_power(nvidia_stack_t *, nv_state_t *, NvBool);
|
||||
NV_STATUS NV_API_CALL rm_transition_dynamic_power(nvidia_stack_t *, nv_state_t *, NvBool, NvBool *);
|
||||
const char* NV_API_CALL rm_get_vidmem_power_status(nvidia_stack_t *, nv_state_t *);
|
||||
const char* NV_API_CALL rm_get_dynamic_power_management_status(nvidia_stack_t *, nv_state_t *);
|
||||
const char* NV_API_CALL rm_get_gpu_gcx_support(nvidia_stack_t *, nv_state_t *, NvBool);
|
||||
@ -1023,7 +1020,8 @@ NV_STATUS NV_API_CALL nv_vgpu_create_request(nvidia_stack_t *, nv_state_t *, c
|
||||
NV_STATUS NV_API_CALL nv_vgpu_delete(nvidia_stack_t *, const NvU8 *, NvU16);
|
||||
NV_STATUS NV_API_CALL nv_vgpu_get_type_ids(nvidia_stack_t *, nv_state_t *, NvU32 *, NvU32 *, NvBool, NvU8, NvBool);
|
||||
NV_STATUS NV_API_CALL nv_vgpu_get_type_info(nvidia_stack_t *, nv_state_t *, NvU32, char *, int, NvU8);
|
||||
NV_STATUS NV_API_CALL nv_vgpu_get_bar_info(nvidia_stack_t *, nv_state_t *, const NvU8 *, NvU64 *, NvU32, void *);
|
||||
NV_STATUS NV_API_CALL nv_vgpu_get_bar_info(nvidia_stack_t *, nv_state_t *, const NvU8 *, NvU64 *, NvU32, void *, NvBool *);
|
||||
NV_STATUS NV_API_CALL nv_vgpu_get_hbm_info(nvidia_stack_t *, nv_state_t *, const NvU8 *, NvU64 *, NvU64 *);
|
||||
NV_STATUS NV_API_CALL nv_vgpu_start(nvidia_stack_t *, const NvU8 *, void *, NvS32 *, NvU8 *, NvU32);
|
||||
NV_STATUS NV_API_CALL nv_vgpu_get_sparse_mmap(nvidia_stack_t *, nv_state_t *, const NvU8 *, NvU64 **, NvU64 **, NvU32 *);
|
||||
NV_STATUS NV_API_CALL nv_vgpu_process_vf_info(nvidia_stack_t *, nv_state_t *, NvU8, NvU32, NvU8, NvU8, NvU8, NvBool, void *);
|
||||
|
@ -86,7 +86,7 @@
|
||||
/* Not currently implemented for MSVC/ARM64. See bug 3366890. */
|
||||
# define nv_speculation_barrier()
|
||||
# define speculation_barrier() nv_speculation_barrier()
|
||||
#elif defined(NVCPU_NVRISCV64) && NVOS_IS_LIBOS
|
||||
#elif defined(NVCPU_IS_RISCV64)
|
||||
# define nv_speculation_barrier()
|
||||
#else
|
||||
#error "Unknown compiler/chip family"
|
||||
|
@ -104,6 +104,10 @@ typedef struct UvmGpuMemoryInfo_tag
|
||||
// Out: Set to TRUE, if the allocation is in sysmem.
|
||||
NvBool sysmem;
|
||||
|
||||
// Out: Set to TRUE, if this allocation is treated as EGM.
|
||||
// sysmem is also TRUE when egm is TRUE.
|
||||
NvBool egm;
|
||||
|
||||
// Out: Set to TRUE, if the allocation is a constructed
|
||||
// under a Device or Subdevice.
|
||||
// All permutations of sysmem and deviceDescendant are valid.
|
||||
@ -125,6 +129,8 @@ typedef struct UvmGpuMemoryInfo_tag
|
||||
|
||||
// Out: Uuid of the GPU to which the allocation belongs.
|
||||
// This is only valid if deviceDescendant is NV_TRUE.
|
||||
// When egm is NV_TRUE, this is also the UUID of the GPU
|
||||
// for which EGM is local.
|
||||
// Note: If the allocation is owned by a device in
|
||||
// an SLI group and the allocation is broadcast
|
||||
// across the SLI group, this UUID will be any one
|
||||
@ -332,7 +338,7 @@ typedef struct UvmGpuPagingChannelAllocParams_tag
|
||||
|
||||
// The max number of Copy Engines supported by a GPU.
|
||||
// The gpu ops build has a static assert that this is the correct number.
|
||||
#define UVM_COPY_ENGINE_COUNT_MAX 10
|
||||
#define UVM_COPY_ENGINE_COUNT_MAX 64
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@ -566,11 +572,8 @@ typedef struct UvmPlatformInfo_tag
|
||||
// Out: ATS (Address Translation Services) is supported
|
||||
NvBool atsSupported;
|
||||
|
||||
// Out: True if HW trusted execution, such as AMD's SEV-SNP or Intel's TDX,
|
||||
// is enabled in the VM, indicating that Confidential Computing must be
|
||||
// also enabled in the GPU(s); these two security features are either both
|
||||
// enabled, or both disabled.
|
||||
NvBool confComputingEnabled;
|
||||
// Out: AMD SEV (Secure Encrypted Virtualization) is enabled
|
||||
NvBool sevEnabled;
|
||||
} UvmPlatformInfo;
|
||||
|
||||
typedef struct UvmGpuClientInfo_tag
|
||||
@ -683,6 +686,10 @@ typedef struct UvmGpuInfo_tag
|
||||
// to NVSwitch peers.
|
||||
NvBool connectedToSwitch;
|
||||
NvU64 nvswitchMemoryWindowStart;
|
||||
|
||||
// local EGM properties
|
||||
NvBool egmEnabled;
|
||||
NvU8 egmPeerId;
|
||||
} UvmGpuInfo;
|
||||
|
||||
typedef struct UvmGpuFbInfo_tag
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2014-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2014-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
@ -45,6 +45,11 @@
|
||||
|
||||
#define NVKMS_DEVICE_ID_TEGRA 0x0000ffff
|
||||
|
||||
#define NVKMS_MAX_SUPERFRAME_VIEWS 4
|
||||
|
||||
#define NVKMS_LOG2_LUT_ARRAY_SIZE 10
|
||||
#define NVKMS_LUT_ARRAY_SIZE (1 << NVKMS_LOG2_LUT_ARRAY_SIZE)
|
||||
|
||||
typedef NvU32 NvKmsDeviceHandle;
|
||||
typedef NvU32 NvKmsDispHandle;
|
||||
typedef NvU32 NvKmsConnectorHandle;
|
||||
@ -179,6 +184,14 @@ enum NvKmsEventType {
|
||||
NVKMS_EVENT_TYPE_FLIP_OCCURRED,
|
||||
};
|
||||
|
||||
enum NvKmsFlipResult {
|
||||
NV_KMS_FLIP_RESULT_SUCCESS = 0, /* Success */
|
||||
NV_KMS_FLIP_RESULT_INVALID_PARAMS, /* Parameter validation failed */
|
||||
NV_KMS_FLIP_RESULT_IN_PROGRESS, /* Flip would fail because an outstanding
|
||||
flip containing changes that cannot be
|
||||
queued is in progress */
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
NV_EVO_SCALER_1TAP = 0,
|
||||
NV_EVO_SCALER_2TAPS = 1,
|
||||
@ -221,6 +234,16 @@ struct NvKmsUsageBounds {
|
||||
} layer[NVKMS_MAX_LAYERS_PER_HEAD];
|
||||
};
|
||||
|
||||
/*!
|
||||
* Per-component arrays of NvU16s describing the LUT; used for both the input
|
||||
* LUT and output LUT.
|
||||
*/
|
||||
struct NvKmsLutRamps {
|
||||
NvU16 red[NVKMS_LUT_ARRAY_SIZE]; /*! in */
|
||||
NvU16 green[NVKMS_LUT_ARRAY_SIZE]; /*! in */
|
||||
NvU16 blue[NVKMS_LUT_ARRAY_SIZE]; /*! in */
|
||||
};
|
||||
|
||||
/*
|
||||
* A 3x4 row-major colorspace conversion matrix.
|
||||
*
|
||||
@ -531,6 +554,18 @@ typedef struct {
|
||||
NvBool noncoherent;
|
||||
} NvKmsDispIOCoherencyModes;
|
||||
|
||||
enum NvKmsInputColorRange {
|
||||
/*
|
||||
* If DEFAULT is provided, driver will assume full range for RGB formats
|
||||
* and limited range for YUV formats.
|
||||
*/
|
||||
NVKMS_INPUT_COLORRANGE_DEFAULT = 0,
|
||||
|
||||
NVKMS_INPUT_COLORRANGE_LIMITED = 1,
|
||||
|
||||
NVKMS_INPUT_COLORRANGE_FULL = 2,
|
||||
};
|
||||
|
||||
enum NvKmsInputColorSpace {
|
||||
/* Unknown colorspace; no de-gamma will be applied */
|
||||
NVKMS_INPUT_COLORSPACE_NONE = 0,
|
||||
@ -542,6 +577,12 @@ enum NvKmsInputColorSpace {
|
||||
NVKMS_INPUT_COLORSPACE_BT2100_PQ = 2,
|
||||
};
|
||||
|
||||
enum NvKmsOutputColorimetry {
|
||||
NVKMS_OUTPUT_COLORIMETRY_DEFAULT = 0,
|
||||
|
||||
NVKMS_OUTPUT_COLORIMETRY_BT2100 = 1,
|
||||
};
|
||||
|
||||
enum NvKmsOutputTf {
|
||||
/*
|
||||
* NVKMS itself won't apply any OETF (clients are still
|
||||
@ -552,6 +593,17 @@ enum NvKmsOutputTf {
|
||||
NVKMS_OUTPUT_TF_PQ = 2,
|
||||
};
|
||||
|
||||
/*!
|
||||
* EOTF Data Byte 1 as per CTA-861-G spec.
|
||||
* This is expected to match exactly with the spec.
|
||||
*/
|
||||
enum NvKmsInfoFrameEOTF {
|
||||
NVKMS_INFOFRAME_EOTF_SDR_GAMMA = 0,
|
||||
NVKMS_INFOFRAME_EOTF_HDR_GAMMA = 1,
|
||||
NVKMS_INFOFRAME_EOTF_ST2084 = 2,
|
||||
NVKMS_INFOFRAME_EOTF_HLG = 3,
|
||||
};
|
||||
|
||||
/*!
|
||||
* HDR Static Metadata Type1 Descriptor as per CEA-861.3 spec.
|
||||
* This is expected to match exactly with the spec.
|
||||
@ -605,4 +657,29 @@ struct NvKmsHDRStaticMetadata {
|
||||
NvU16 maxFALL;
|
||||
};
|
||||
|
||||
/*!
|
||||
* A superframe is made of two or more video streams that are combined in
|
||||
* a specific way. A DP serializer (an external device connected to a Tegra
|
||||
* ARM SOC over DP or HDMI) can receive a video stream comprising multiple
|
||||
* videos combined into a single frame and then split it into multiple
|
||||
* video streams. The following structure describes the number of views
|
||||
* and dimensions of each view inside a superframe.
|
||||
*/
|
||||
struct NvKmsSuperframeInfo {
|
||||
NvU8 numViews;
|
||||
struct {
|
||||
/* x offset inside superframe at which this view starts */
|
||||
NvU16 x;
|
||||
|
||||
/* y offset inside superframe at which this view starts */
|
||||
NvU16 y;
|
||||
|
||||
/* Horizontal active width in pixels for this view */
|
||||
NvU16 width;
|
||||
|
||||
/* Vertical active height in lines for this view */
|
||||
NvU16 height;
|
||||
} view[NVKMS_MAX_SUPERFRAME_VIEWS];
|
||||
};
|
||||
|
||||
#endif /* NVKMS_API_TYPES_H */
|
||||
|
@ -49,6 +49,8 @@ struct NvKmsKapiDevice;
|
||||
struct NvKmsKapiMemory;
|
||||
struct NvKmsKapiSurface;
|
||||
struct NvKmsKapiChannelEvent;
|
||||
struct NvKmsKapiSemaphoreSurface;
|
||||
struct NvKmsKapiSemaphoreSurfaceCallback;
|
||||
|
||||
typedef NvU32 NvKmsKapiConnector;
|
||||
typedef NvU32 NvKmsKapiDisplay;
|
||||
@ -67,6 +69,14 @@ typedef NvU32 NvKmsKapiDisplay;
|
||||
*/
|
||||
typedef void NvKmsChannelEventProc(void *dataPtr, NvU32 dataU32);
|
||||
|
||||
/*
|
||||
* Note: Same as above, this function must not call back into NVKMS-KAPI, nor
|
||||
* directly into RM. Doing so could cause deadlocks given the notification
|
||||
* function will most likely be called from within RM's interrupt handler
|
||||
* callchain.
|
||||
*/
|
||||
typedef void NvKmsSemaphoreSurfaceCallbackProc(void *pData);
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
@ -126,6 +136,11 @@ struct NvKmsKapiDeviceResourcesInfo {
|
||||
NvU32 validCursorCompositionModes;
|
||||
NvU64 supportedCursorSurfaceMemoryFormats;
|
||||
|
||||
struct {
|
||||
NvU64 maxSubmittedOffset;
|
||||
NvU64 stride;
|
||||
} semsurf;
|
||||
|
||||
struct {
|
||||
NvU16 validRRTransforms;
|
||||
NvU32 validCompositionModes;
|
||||
@ -218,8 +233,10 @@ struct NvKmsKapiLayerConfig {
|
||||
struct NvKmsRRParams rrParams;
|
||||
struct NvKmsKapiSyncpt syncptParams;
|
||||
|
||||
struct NvKmsHDRStaticMetadata hdrMetadata;
|
||||
NvBool hdrMetadataSpecified;
|
||||
struct {
|
||||
struct NvKmsHDRStaticMetadata val;
|
||||
NvBool enabled;
|
||||
} hdrMetadata;
|
||||
|
||||
enum NvKmsOutputTf tf;
|
||||
|
||||
@ -233,16 +250,21 @@ struct NvKmsKapiLayerConfig {
|
||||
NvU16 dstWidth, dstHeight;
|
||||
|
||||
enum NvKmsInputColorSpace inputColorSpace;
|
||||
struct NvKmsCscMatrix csc;
|
||||
NvBool cscUseMain;
|
||||
};
|
||||
|
||||
struct NvKmsKapiLayerRequestedConfig {
|
||||
struct NvKmsKapiLayerConfig config;
|
||||
struct {
|
||||
NvBool surfaceChanged : 1;
|
||||
NvBool srcXYChanged : 1;
|
||||
NvBool srcWHChanged : 1;
|
||||
NvBool dstXYChanged : 1;
|
||||
NvBool dstWHChanged : 1;
|
||||
NvBool surfaceChanged : 1;
|
||||
NvBool srcXYChanged : 1;
|
||||
NvBool srcWHChanged : 1;
|
||||
NvBool dstXYChanged : 1;
|
||||
NvBool dstWHChanged : 1;
|
||||
NvBool cscChanged : 1;
|
||||
NvBool tfChanged : 1;
|
||||
NvBool hdrMetadataChanged : 1;
|
||||
} flags;
|
||||
};
|
||||
|
||||
@ -286,14 +308,41 @@ struct NvKmsKapiHeadModeSetConfig {
|
||||
struct NvKmsKapiDisplayMode mode;
|
||||
|
||||
NvBool vrrEnabled;
|
||||
|
||||
struct {
|
||||
NvBool enabled;
|
||||
enum NvKmsInfoFrameEOTF eotf;
|
||||
struct NvKmsHDRStaticMetadata staticMetadata;
|
||||
} hdrInfoFrame;
|
||||
|
||||
enum NvKmsOutputColorimetry colorimetry;
|
||||
|
||||
struct {
|
||||
struct {
|
||||
NvBool specified;
|
||||
NvU32 depth;
|
||||
NvU32 start;
|
||||
NvU32 end;
|
||||
struct NvKmsLutRamps *pRamps;
|
||||
} input;
|
||||
|
||||
struct {
|
||||
NvBool specified;
|
||||
NvBool enabled;
|
||||
struct NvKmsLutRamps *pRamps;
|
||||
} output;
|
||||
} lut;
|
||||
};
|
||||
|
||||
struct NvKmsKapiHeadRequestedConfig {
|
||||
struct NvKmsKapiHeadModeSetConfig modeSetConfig;
|
||||
struct {
|
||||
NvBool activeChanged : 1;
|
||||
NvBool displaysChanged : 1;
|
||||
NvBool modeChanged : 1;
|
||||
NvBool activeChanged : 1;
|
||||
NvBool displaysChanged : 1;
|
||||
NvBool modeChanged : 1;
|
||||
NvBool hdrInfoFrameChanged : 1;
|
||||
NvBool colorimetryChanged : 1;
|
||||
NvBool lutChanged : 1;
|
||||
} flags;
|
||||
|
||||
struct NvKmsKapiCursorRequestedConfig cursorRequestedConfig;
|
||||
@ -318,6 +367,7 @@ struct NvKmsKapiHeadReplyConfig {
|
||||
};
|
||||
|
||||
struct NvKmsKapiModeSetReplyConfig {
|
||||
enum NvKmsFlipResult flipResult;
|
||||
struct NvKmsKapiHeadReplyConfig
|
||||
headReplyConfig[NVKMS_KAPI_MAX_HEADS];
|
||||
};
|
||||
@ -434,6 +484,12 @@ enum NvKmsKapiAllocationType {
|
||||
NVKMS_KAPI_ALLOCATION_TYPE_OFFSCREEN = 2,
|
||||
};
|
||||
|
||||
typedef enum NvKmsKapiRegisterWaiterResultRec {
|
||||
NVKMS_KAPI_REG_WAITER_FAILED,
|
||||
NVKMS_KAPI_REG_WAITER_SUCCESS,
|
||||
NVKMS_KAPI_REG_WAITER_ALREADY_SIGNALLED,
|
||||
} NvKmsKapiRegisterWaiterResult;
|
||||
|
||||
struct NvKmsKapiFunctionsTable {
|
||||
|
||||
/*!
|
||||
@ -519,8 +575,8 @@ struct NvKmsKapiFunctionsTable {
|
||||
);
|
||||
|
||||
/*!
|
||||
* Revoke permissions previously granted. Only one (dispIndex, head,
|
||||
* display) is currently supported.
|
||||
* Revoke modeset permissions previously granted. Only one (dispIndex,
|
||||
* head, display) is currently supported.
|
||||
*
|
||||
* \param [in] device A device returned by allocateDevice().
|
||||
*
|
||||
@ -537,6 +593,34 @@ struct NvKmsKapiFunctionsTable {
|
||||
NvKmsKapiDisplay display
|
||||
);
|
||||
|
||||
/*!
|
||||
* Grant modeset sub-owner permissions to fd. This is used by clients to
|
||||
* convert drm 'master' permissions into nvkms sub-owner permission.
|
||||
*
|
||||
* \param [in] fd fd from opening /dev/nvidia-modeset.
|
||||
*
|
||||
* \param [in] device A device returned by allocateDevice().
|
||||
*
|
||||
* \return NV_TRUE on success, NV_FALSE on failure.
|
||||
*/
|
||||
NvBool (*grantSubOwnership)
|
||||
(
|
||||
NvS32 fd,
|
||||
struct NvKmsKapiDevice *device
|
||||
);
|
||||
|
||||
/*!
|
||||
* Revoke sub-owner permissions previously granted.
|
||||
*
|
||||
* \param [in] device A device returned by allocateDevice().
|
||||
*
|
||||
* \return NV_TRUE on success, NV_FALSE on failure.
|
||||
*/
|
||||
NvBool (*revokeSubOwnership)
|
||||
(
|
||||
struct NvKmsKapiDevice *device
|
||||
);
|
||||
|
||||
/*!
|
||||
* Registers for notification, via
|
||||
* NvKmsKapiAllocateDeviceParams::eventCallback, of the events specified
|
||||
@ -1122,6 +1206,199 @@ struct NvKmsKapiFunctionsTable {
|
||||
NvP64 dmaBuf,
|
||||
NvU32 limit);
|
||||
|
||||
/*!
|
||||
* Import a semaphore surface allocated elsewhere to NVKMS and return a
|
||||
* handle to the new object.
|
||||
*
|
||||
* \param [in] device A device allocated using allocateDevice().
|
||||
*
|
||||
* \param [in] nvKmsParamsUser Userspace pointer to driver-specific
|
||||
* parameters describing the semaphore
|
||||
* surface being imported.
|
||||
*
|
||||
* \param [in] nvKmsParamsSize Size of the driver-specific parameter
|
||||
* struct.
|
||||
*
|
||||
* \param [out] pSemaphoreMap Returns a CPU mapping of the semaphore
|
||||
* surface's semaphore memory to the client.
|
||||
*
|
||||
* \param [out] pMaxSubmittedMap Returns a CPU mapping of the semaphore
|
||||
* surface's semaphore memory to the client.
|
||||
*
|
||||
* \return struct NvKmsKapiSemaphoreSurface* on success, NULL on failure.
|
||||
*/
|
||||
struct NvKmsKapiSemaphoreSurface* (*importSemaphoreSurface)
|
||||
(
|
||||
struct NvKmsKapiDevice *device,
|
||||
NvU64 nvKmsParamsUser,
|
||||
NvU64 nvKmsParamsSize,
|
||||
void **pSemaphoreMap,
|
||||
void **pMaxSubmittedMap
|
||||
);
|
||||
|
||||
/*!
|
||||
* Free an imported semaphore surface.
|
||||
*
|
||||
* \param [in] device The device passed to
|
||||
* importSemaphoreSurface() when creating
|
||||
* semaphoreSurface.
|
||||
*
|
||||
* \param [in] semaphoreSurface A semaphore surface returned by
|
||||
* importSemaphoreSurface().
|
||||
*/
|
||||
void (*freeSemaphoreSurface)
|
||||
(
|
||||
struct NvKmsKapiDevice *device,
|
||||
struct NvKmsKapiSemaphoreSurface *semaphoreSurface
|
||||
);
|
||||
|
||||
/*!
|
||||
* Register a callback to be called when a semaphore reaches a value.
|
||||
*
|
||||
* The callback will be called when the semaphore at index in
|
||||
* semaphoreSurface reaches the value wait_value. The callback will
|
||||
* be called at most once and is automatically unregistered when called.
|
||||
* It may also be unregistered (i.e., cancelled) explicitly using the
|
||||
* unregisterSemaphoreSurfaceCallback() function. To avoid leaking the
|
||||
* memory used to track the registered callback, callers must ensure one
|
||||
* of these methods of unregistration is used for every successful
|
||||
* callback registration that returns a non-NULL pCallbackHandle.
|
||||
*
|
||||
* \param [in] device The device passed to
|
||||
* importSemaphoreSurface() when creating
|
||||
* semaphoreSurface.
|
||||
*
|
||||
* \param [in] semaphoreSurface A semaphore surface returned by
|
||||
* importSemaphoreSurface().
|
||||
*
|
||||
* \param [in] pCallback A pointer to the function to call when
|
||||
* the specified value is reached. NULL
|
||||
* means no callback.
|
||||
*
|
||||
* \param [in] pData Arbitrary data to be passed back to the
|
||||
* callback as its sole parameter.
|
||||
*
|
||||
* \param [in] index The index of the semaphore within
|
||||
* semaphoreSurface.
|
||||
*
|
||||
* \param [in] wait_value The value the semaphore must reach or
|
||||
* exceed before the callback is called.
|
||||
*
|
||||
* \param [in] new_value The value the semaphore will be set to
|
||||
* when it reaches or exceeds <wait_value>.
|
||||
* 0 means do not update the value.
|
||||
*
|
||||
* \param [out] pCallbackHandle On success, the value pointed to will
|
||||
* contain an opaque handle to the
|
||||
* registered callback that may be used to
|
||||
* cancel it if needed. Unused if pCallback
|
||||
* is NULL.
|
||||
*
|
||||
* \return NVKMS_KAPI_REG_WAITER_SUCCESS if the waiter was registered or if
|
||||
* no callback was requested and the semaphore at <index> has
|
||||
* already reached or exceeded <wait_value>
|
||||
*
|
||||
* NVKMS_KAPI_REG_WAITER_ALREADY_SIGNALLED if a callback was
|
||||
* requested and the semaphore at <index> has already reached or
|
||||
* exceeded <wait_value>
|
||||
*
|
||||
* NVKMS_KAPI_REG_WAITER_FAILED if waiter registration failed.
|
||||
*/
|
||||
NvKmsKapiRegisterWaiterResult
|
||||
(*registerSemaphoreSurfaceCallback)
|
||||
(
|
||||
struct NvKmsKapiDevice *device,
|
||||
struct NvKmsKapiSemaphoreSurface *semaphoreSurface,
|
||||
NvKmsSemaphoreSurfaceCallbackProc *pCallback,
|
||||
void *pData,
|
||||
NvU64 index,
|
||||
NvU64 wait_value,
|
||||
NvU64 new_value,
|
||||
struct NvKmsKapiSemaphoreSurfaceCallback **pCallbackHandle
|
||||
);
|
||||
|
||||
/*!
|
||||
* Unregister a callback registered via registerSemaphoreSurfaceCallback()
|
||||
*
|
||||
* If the callback has not yet been called, this function will cancel the
|
||||
* callback and free its associated resources.
|
||||
*
|
||||
* Note this function treats the callback handle as a pointer. While this
|
||||
* function does not dereference that pointer itself, the underlying call
|
||||
* to RM does within a properly guarded critical section that first ensures
|
||||
* it is not in the process of being used within a callback. This means
|
||||
* the callstack must take into consideration that pointers are not in
|
||||
* general unique handles if they may have been freed, since a subsequent
|
||||
* malloc could return the same pointer value at that point. This callchain
|
||||
* avoids that by leveraging the behavior of the underlying RM APIs:
|
||||
*
|
||||
* 1) A callback handle is referenced relative to its corresponding
|
||||
* (semaphore surface, index, wait_value) tuple here and within RM. It
|
||||
* is not a valid handle outside of that scope.
|
||||
*
|
||||
* 2) A callback can not be registered against an already-reached value
|
||||
* for a given semaphore surface index.
|
||||
*
|
||||
* 3) A given callback handle can not be registered twice against the same
|
||||
* (semaphore surface, index, wait_value) tuple, so unregistration will
|
||||
* never race with registration at the RM level, and would only race at
|
||||
* a higher level if used incorrectly. Since this is kernel code, we
|
||||
* can safely assume there won't be malicious clients purposely misuing
|
||||
* the API, but the burden is placed on the caller to ensure its usage
|
||||
* does not lead to races at higher levels.
|
||||
*
|
||||
* These factors considered together ensure any valid registered handle is
|
||||
* either still in the relevant waiter list and refers to the same event/
|
||||
* callback as when it was registered, or has been removed from the list
|
||||
* as part of a critical section that also destroys the list itself and
|
||||
* makes future lookups in that list impossible, and hence eliminates the
|
||||
* chance of comparing a stale handle with a new handle of the same value
|
||||
* as part of a lookup.
|
||||
*
|
||||
* \param [in] device The device passed to
|
||||
* importSemaphoreSurface() when creating
|
||||
* semaphoreSurface.
|
||||
*
|
||||
* \param [in] semaphoreSurface The semaphore surface passed to
|
||||
* registerSemaphoreSurfaceCallback() when
|
||||
* registering the callback.
|
||||
*
|
||||
* \param [in] index The index passed to
|
||||
* registerSemaphoreSurfaceCallback() when
|
||||
* registering the callback.
|
||||
*
|
||||
* \param [in] wait_value The wait_value passed to
|
||||
* registerSemaphoreSurfaceCallback() when
|
||||
* registering the callback.
|
||||
*
|
||||
* \param [in] callbackHandle The callback handle returned by
|
||||
* registerSemaphoreSurfaceCallback().
|
||||
*/
|
||||
NvBool
|
||||
(*unregisterSemaphoreSurfaceCallback)
|
||||
(
|
||||
struct NvKmsKapiDevice *device,
|
||||
struct NvKmsKapiSemaphoreSurface *semaphoreSurface,
|
||||
NvU64 index,
|
||||
NvU64 wait_value,
|
||||
struct NvKmsKapiSemaphoreSurfaceCallback *callbackHandle
|
||||
);
|
||||
|
||||
/*!
|
||||
* Update the value of a semaphore surface from the CPU.
|
||||
*
|
||||
* Update the semaphore value at the specified index from the CPU, then
|
||||
* wake up any pending CPU waiters associated with that index that are
|
||||
* waiting on it reaching a value <= the new value.
|
||||
*/
|
||||
NvBool
|
||||
(*setSemaphoreSurfaceValue)
|
||||
(
|
||||
struct NvKmsKapiDevice *device,
|
||||
struct NvKmsKapiSemaphoreSurface *semaphoreSurface,
|
||||
NvU64 index,
|
||||
NvU64 new_value
|
||||
);
|
||||
};
|
||||
|
||||
/** @} */
|
||||
|
@ -162,7 +162,7 @@ NvBool NV_API_CALL os_is_vgx_hyper (void);
|
||||
NV_STATUS NV_API_CALL os_inject_vgx_msi (NvU16, NvU64, NvU32);
|
||||
NvBool NV_API_CALL os_is_grid_supported (void);
|
||||
NvU32 NV_API_CALL os_get_grid_csp_support (void);
|
||||
void NV_API_CALL os_get_screen_info (NvU64 *, NvU16 *, NvU16 *, NvU16 *, NvU16 *, NvU64, NvU64);
|
||||
void NV_API_CALL os_get_screen_info (NvU64 *, NvU32 *, NvU32 *, NvU32 *, NvU32 *, NvU64, NvU64);
|
||||
void NV_API_CALL os_bug_check (NvU32, const char *);
|
||||
NV_STATUS NV_API_CALL os_lock_user_pages (void *, NvU64, void **, NvU32);
|
||||
NV_STATUS NV_API_CALL os_lookup_user_io_memory (void *, NvU64, NvU64 **, void**);
|
||||
@ -230,12 +230,14 @@ extern NvBool os_dma_buf_enabled;
|
||||
* ---------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#define NV_DBG_INFO 0x0
|
||||
#define NV_DBG_SETUP 0x1
|
||||
#define NV_DBG_USERERRORS 0x2
|
||||
#define NV_DBG_INFO 0x1
|
||||
#define NV_DBG_SETUP 0x2
|
||||
#define NV_DBG_WARNINGS 0x3
|
||||
#define NV_DBG_ERRORS 0x4
|
||||
#define NV_DBG_HW_ERRORS 0x5
|
||||
#define NV_DBG_FATAL 0x6
|
||||
|
||||
#define NV_DBG_FORCE_LEVEL(level) ((level) | (1 << 8))
|
||||
|
||||
void NV_API_CALL out_string(const char *str);
|
||||
int NV_API_CALL nv_printf(NvU32 debuglevel, const char *printf_format, ...);
|
||||
|
@ -316,7 +316,7 @@ export_symbol_present_conftest() {
|
||||
SYMBOL="$1"
|
||||
TAB=' '
|
||||
|
||||
if grep -e "${TAB}${SYMBOL}${TAB}.*${TAB}EXPORT_SYMBOL.*\$" \
|
||||
if grep -e "${TAB}${SYMBOL}${TAB}.*${TAB}EXPORT_SYMBOL\(_GPL\)\?\s*\$" \
|
||||
"$OUTPUT/Module.symvers" >/dev/null 2>&1; then
|
||||
echo "#define NV_IS_EXPORT_SYMBOL_PRESENT_$SYMBOL 1" |
|
||||
append_conftest "symbols"
|
||||
@ -337,7 +337,7 @@ export_symbol_gpl_conftest() {
|
||||
SYMBOL="$1"
|
||||
TAB=' '
|
||||
|
||||
if grep -e "${TAB}${SYMBOL}${TAB}.*${TAB}EXPORT_\(UNUSED_\)*SYMBOL_GPL\$" \
|
||||
if grep -e "${TAB}${SYMBOL}${TAB}.*${TAB}EXPORT_\(UNUSED_\)*SYMBOL_GPL\s*\$" \
|
||||
"$OUTPUT/Module.symvers" >/dev/null 2>&1; then
|
||||
echo "#define NV_IS_EXPORT_SYMBOL_GPL_$SYMBOL 1" |
|
||||
append_conftest "symbols"
|
||||
@ -549,10 +549,8 @@ compile_test() {
|
||||
# Determine if the set_pages_array_uc() function is present.
|
||||
# It does not exist on all architectures.
|
||||
#
|
||||
# set_pages_array_uc() was added by commit
|
||||
# 0f3507555f6fa4acbc85a646d6e8766230db38fc ("x86, CPA: Add
|
||||
# set_pages_arrayuc and set_pages_array_wb") in v2.6.30-rc1 (Thu Mar
|
||||
# 19 14:51:15 2009)
|
||||
# Added by commit 0f3507555f6f ("x86, CPA: Add set_pages_arrayuc
|
||||
# and set_pages_array_wb") in v2.6.30.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/types.h>
|
||||
@ -597,8 +595,8 @@ compile_test() {
|
||||
#
|
||||
# Added by commit 3c299dc22635 ("PCI: add
|
||||
# pci_get_domain_bus_and_slot function") in 2.6.33 but aarch64
|
||||
# support was added by commit d1e6dc91b532
|
||||
# ("arm64: Add architectural support for PCI") in 3.18-rc1
|
||||
# support was added by commit d1e6dc91b532 ("arm64: Add
|
||||
# architectural support for PCI") in 3.18.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/pci.h>
|
||||
@ -1242,26 +1240,6 @@ compile_test() {
|
||||
compile_check_conftest "$CODE" "NV_VFIO_DEVICE_GFX_PLANE_INFO_PRESENT" "" "types"
|
||||
;;
|
||||
|
||||
vfio_device_migration_has_start_pfn)
|
||||
#
|
||||
# Determine if the 'vfio_device_migration_info' structure has
|
||||
# a 'start_pfn' field.
|
||||
#
|
||||
# This member was present in proposed interface for vGPU Migration
|
||||
# ("[PATCH v3 0/5] Add migration support for VFIO device ")
|
||||
# https://lists.gnu.org/archive/html/qemu-devel/2019-02/msg05176.html
|
||||
# which is not present in upstreamed commit a8a24f3f6e38 (vfio: UAPI
|
||||
# for migration interface for device state) in v5.8 (2020-05-29)
|
||||
#
|
||||
CODE="
|
||||
#include <linux/vfio.h>
|
||||
int conftest_vfio_device_migration_has_start_pfn(void) {
|
||||
return offsetof(struct vfio_device_migration_info, start_pfn);
|
||||
}"
|
||||
|
||||
compile_check_conftest "$CODE" "NV_VFIO_DEVICE_MIGRATION_HAS_START_PFN" "" "types"
|
||||
;;
|
||||
|
||||
vfio_uninit_group_dev)
|
||||
#
|
||||
# Determine if vfio_uninit_group_dev() function is present or not.
|
||||
@ -1411,9 +1389,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if the pde_data() function is present.
|
||||
#
|
||||
# The commit c28198889c15 removed the function
|
||||
# 'PDE_DATA()', and replaced it with 'pde_data()'
|
||||
# ("proc: remove PDE_DATA() completely") in v5.17-rc1.
|
||||
# PDE_DATA() was replaced with pde_data() by commit 359745d78351
|
||||
# ("proc: remove PDE_DATA() completely") in v5.17.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/proc_fs.h>
|
||||
@ -1554,8 +1531,8 @@ compile_test() {
|
||||
# based implementation") in v4.5
|
||||
#
|
||||
# Commit 0a0f0d8be76d ("dma-mapping: split <linux/dma-mapping.h>")
|
||||
# in v5.10-rc1 (2020-09-22), moved get_dma_ops() function
|
||||
# prototype from <linux/dma-mapping.h> to <linux/dma-map-ops.h>.
|
||||
# in v5.10 moved get_dma_ops() function prototype from
|
||||
# <linux/dma-mapping.h> to <linux/dma-map-ops.h>.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_LINUX_DMA_MAP_OPS_H_PRESENT)
|
||||
@ -1725,9 +1702,8 @@ compile_test() {
|
||||
|
||||
kernel_write_has_pointer_pos_arg)
|
||||
#
|
||||
# Determine the pos argument type, which was changed by
|
||||
# commit e13ec939e96b1 (fs: fix kernel_write prototype) on
|
||||
# 9/1/2017.
|
||||
# Determine the pos argument type, which was changed by commit
|
||||
# e13ec939e96b ("fs: fix kernel_write prototype") in v4.14.
|
||||
#
|
||||
echo "$CONFTEST_PREAMBLE
|
||||
#include <linux/fs.h>
|
||||
@ -1750,9 +1726,8 @@ compile_test() {
|
||||
|
||||
kernel_read_has_pointer_pos_arg)
|
||||
#
|
||||
# Determine the pos argument type, which was changed by
|
||||
# commit bdd1d2d3d251c (fs: fix kernel_read prototype) on
|
||||
# 9/1/2017.
|
||||
# Determine the pos argument type, which was changed by commit
|
||||
# bdd1d2d3d251 ("fs: fix kernel_read prototype") in v4.14.
|
||||
#
|
||||
echo "$CONFTEST_PREAMBLE
|
||||
#include <linux/fs.h>
|
||||
@ -1777,8 +1752,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if vm_insert_pfn_prot function is present
|
||||
#
|
||||
# Added by commit 1745cbc5d0de ("mm: Add vm_insert_pfn_prot()") in
|
||||
# v3.16.59
|
||||
# Added by commit 1745cbc5d0de ("mm: Add vm_insert_pfn_prot()")
|
||||
# in v4.6.
|
||||
#
|
||||
# Removed by commit f5e6d1d5f8f3 ("mm: introduce
|
||||
# vmf_insert_pfn_prot()") in v4.20.
|
||||
@ -1995,7 +1970,7 @@ compile_test() {
|
||||
# attached drivers") in v3.14 (2013-12-11)
|
||||
#
|
||||
# The commit 57bb1ee60340 ("drm: Compile out legacy chunks from
|
||||
# struct drm_device") compiles out the legacy chunks like
|
||||
# struct drm_device") in v5.11 compiles out the legacy chunks like
|
||||
# drm_driver::legacy_dev_list.
|
||||
#
|
||||
CODE="
|
||||
@ -2018,14 +1993,14 @@ compile_test() {
|
||||
#
|
||||
# Determine if jiffies_to_timespec() is present
|
||||
#
|
||||
# removed by commit 751addac78b6
|
||||
# ("y2038: remove obsolete jiffies conversion functions")
|
||||
# in v5.6-rc1 (2019-12-13).
|
||||
CODE="
|
||||
#include <linux/jiffies.h>
|
||||
void conftest_jiffies_to_timespec(void){
|
||||
jiffies_to_timespec();
|
||||
}"
|
||||
# Removed by commit 751addac78b6 ("y2038: remove obsolete jiffies
|
||||
# conversion functions") in v5.6.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/jiffies.h>
|
||||
void conftest_jiffies_to_timespec(void){
|
||||
jiffies_to_timespec();
|
||||
}"
|
||||
compile_check_conftest "$CODE" "NV_JIFFIES_TO_TIMESPEC_PRESENT" "" "functions"
|
||||
;;
|
||||
|
||||
@ -2035,14 +2010,21 @@ compile_test() {
|
||||
# drm_universal_plane_init()
|
||||
# drm_crtc_init_with_planes()
|
||||
# drm_encoder_init()
|
||||
# have a 'name' argument, which was added by these commits:
|
||||
# drm_universal_plane_init: 2015-12-09 b0b3b7951114315d65398c27648705ca1c322faa
|
||||
# drm_crtc_init_with_planes: 2015-12-09 f98828769c8838f526703ef180b3088a714af2f9
|
||||
# drm_encoder_init: 2015-12-09 13a3d91f17a5f7ed2acd275d18b6acfdb131fb15
|
||||
# have a 'name' argument.
|
||||
#
|
||||
# Additionally determine whether drm_universal_plane_init() has a
|
||||
# 'format_modifiers' argument, which was added by:
|
||||
# 2017-07-23 e6fc3b68558e4c6d8d160b5daf2511b99afa8814
|
||||
# drm_universal_plane_init was updated by commit b0b3b7951114
|
||||
# ("drm: Pass 'name' to drm_universal_plane_init()") in v4.5.
|
||||
#
|
||||
# drm_crtc_init_with_planes was updated by commit f98828769c88
|
||||
# ("drm: Pass 'name' to drm_crtc_init_with_planes()") in v4.5.
|
||||
#
|
||||
# drm_encoder_init was updated by commit 13a3d91f17a5 ("drm: Pass
|
||||
# 'name' to drm_encoder_init()") in v4.5.
|
||||
#
|
||||
# Additionally, determine whether drm_universal_plane_init() has
|
||||
# a 'format_modifiers' argument, which was added by commit
|
||||
# e6fc3b68558e ("drm: Plumb modifiers through plane init") in
|
||||
# v4.14.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_DRM_DRMP_H_PRESENT)
|
||||
@ -2239,7 +2221,7 @@ compile_test() {
|
||||
# correction properties") in v4.6 (2016-03-08).
|
||||
#
|
||||
# Removed by commit f8ed34ac7b45 ("drm: drm_helper_crtc_enable_color_mgmt()
|
||||
# => drm_crtc_enable_color_mgmt()") in v4.8-rc1 (2016-06-07).
|
||||
# => drm_crtc_enable_color_mgmt()") in v4.8.
|
||||
#
|
||||
CODE="
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
@ -2257,11 +2239,11 @@ compile_test() {
|
||||
# present.
|
||||
#
|
||||
# Added by commit f8ed34ac7b45 ("drm: drm_helper_crtc_enable_color_mgmt()
|
||||
# => drm_crtc_enable_color_mgmt()") in v4.8-rc1 (2016-06-07), replacing
|
||||
# => drm_crtc_enable_color_mgmt()") in v4.8, replacing
|
||||
# drm_helper_crtc_enable_color_mgmt().
|
||||
#
|
||||
# Moved to drm_color_mgmt.[ch] by commit f1e2f66ce2d9 ("drm: Extract
|
||||
# drm_color_mgmt.[hc]") in v4.9-rc1 (2016-09-22)
|
||||
# drm_color_mgmt.[hc]") in v4.9.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_DRM_DRM_CRTC_H_PRESENT)
|
||||
@ -2288,8 +2270,7 @@ compile_test() {
|
||||
# Accidentally moved to drm_atomic_state_helper.[ch] by commit
|
||||
# 9ef8a9dc4b21 ("drm: Extract drm_atomic_state_helper.[ch]")
|
||||
# and moved back to drm_atomic_helper.[ch] by commit 1d8224e790c7
|
||||
# ("drm: Fix up drm_atomic_state_helper.[hc] extraction") in
|
||||
# v5.0-rc1
|
||||
# ("drm: Fix up drm_atomic_state_helper.[hc] extraction") in v5.0.
|
||||
#
|
||||
# Removed by commit 6ca2ab8086af ("drm: automatic legacy gamma
|
||||
# support") in v5.12 (2020-12-15)
|
||||
@ -2353,8 +2334,8 @@ compile_test() {
|
||||
#
|
||||
# Added by commit 210647af897a ("PCI: Rename pci_remove_bus_device
|
||||
# to pci_stop_and_remove_bus_device") in v3.4 (2012-02-25) but
|
||||
# aarch64 support was added by commit d1e6dc91b532
|
||||
# ("arm64: Add architectural support for PCI") in v3.18-rc1.
|
||||
# aarch64 support was added by commit d1e6dc91b532 ("arm64: Add
|
||||
# architectural support for PCI") in v3.18.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/types.h>
|
||||
@ -2451,8 +2432,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if the 'pci_dev' data type has a 'ats_enabled' member.
|
||||
#
|
||||
# Added by commit d544d75ac96aa ("PCI: Embed ATS info directly
|
||||
# into struct pci_dev") in v4.3-rc1 (2015-08-14)
|
||||
# Added by commit d544d75ac96a ("PCI: Embed ATS info directly
|
||||
# into struct pci_dev") in v4.3.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/pci.h>
|
||||
@ -2483,9 +2464,9 @@ compile_test() {
|
||||
# commit 768ae309a961 ("mm: replace get_user_pages() write/force
|
||||
# parameters with gup_flags") in v4.9 (2016-10-13)
|
||||
#
|
||||
# Removed vmas parameter from get_user_pages() by commit 7bbf9c8c99
|
||||
# Removed vmas parameter from get_user_pages() by commit 54d020692b34
|
||||
# ("mm/gup: remove unused vmas parameter from get_user_pages()")
|
||||
# in linux-next, expected in v6.5-rc1
|
||||
# in v6.5.
|
||||
#
|
||||
# linux-4.4.168 cherry-picked commit 768ae309a961 without
|
||||
# c12d2da56d0e which is covered in Conftest #3.
|
||||
@ -2653,11 +2634,11 @@ compile_test() {
|
||||
#
|
||||
# get_user_pages_remote() removed 'tsk' parameter by
|
||||
# commit 64019a2e467a ("mm/gup: remove task_struct pointer for
|
||||
# all gup code") in v5.9-rc1 (2020-08-11).
|
||||
# all gup code") in v5.9.
|
||||
#
|
||||
# Removed vmas parameter from get_user_pages_remote() by commit
|
||||
# a4bde14d549 ("mm/gup: remove vmas parameter from get_user_pages_remote()")
|
||||
# in linux-next, expected in v6.5-rc1
|
||||
# ca5e863233e8 ("mm/gup: remove vmas parameter from
|
||||
# get_user_pages_remote()") in v6.5.
|
||||
#
|
||||
|
||||
#
|
||||
@ -2856,15 +2837,14 @@ compile_test() {
|
||||
#
|
||||
# Determine if the function pin_user_pages() is present.
|
||||
# Presence of pin_user_pages() also implies the presence of
|
||||
# unpin-user_page(). Both were added in the v5.6-rc1
|
||||
# unpin-user_page().
|
||||
#
|
||||
# pin_user_pages() was added by commit eddb1c228f7951d399240
|
||||
# ("mm/gup: introduce pin_user_pages*() and FOLL_PIN") in
|
||||
# v5.6-rc1 (2020-01-30)
|
||||
# pin_user_pages() was added by commit eddb1c228f79 ("mm/gup:
|
||||
# introduce pin_user_pages*() and FOLL_PIN") in v5.6.
|
||||
#
|
||||
# Removed vmas parameter from pin_user_pages() by commit
|
||||
# 40896a02751("mm/gup: remove vmas parameter from pin_user_pages()")
|
||||
# in linux-next, expected in v6.5-rc1
|
||||
# 4c630f307455 ("mm/gup: remove vmas parameter from
|
||||
# pin_user_pages()") in v6.5.
|
||||
|
||||
set_pin_user_pages_defines () {
|
||||
if [ "$1" = "" ]; then
|
||||
@ -2929,13 +2909,13 @@ compile_test() {
|
||||
# ("mm/gup: introduce pin_user_pages*() and FOLL_PIN")
|
||||
# in v5.6 (2020-01-30)
|
||||
|
||||
# pin_user_pages_remote() removed 'tsk' parameter by
|
||||
# commit 64019a2e467a ("mm/gup: remove task_struct pointer for
|
||||
# all gup code") in v5.9-rc1 (2020-08-11).
|
||||
# pin_user_pages_remote() removed 'tsk' parameter by commit
|
||||
# 64019a2e467a ("mm/gup: remove task_struct pointer for all gup
|
||||
# code") in v5.9.
|
||||
#
|
||||
# Removed unused vmas parameter from pin_user_pages_remote() by
|
||||
# commit 83bcc2e132 ("mm/gup: remove unused vmas parameter from
|
||||
# pin_user_pages_remote()") in linux-next, expected in v6.5-rc1
|
||||
# commit 0b295316b3a9 ("mm/gup: remove unused vmas parameter from
|
||||
# pin_user_pages_remote()") in v6.5.
|
||||
|
||||
#
|
||||
# This function sets the NV_PIN_USER_PAGES_REMOTE_* macros as per
|
||||
@ -3098,8 +3078,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if enable_apicv boolean is exported by kernel.
|
||||
#
|
||||
# Added by commit fdf513e37a3bd ("KVM: x86: Use common 'enable_apicv'
|
||||
# variable for both APICv and AVIC")
|
||||
# Added by commit fdf513e37a3b ("KVM: x86: Use common
|
||||
# 'enable_apicv' variable for both APICv and AVIC") in v5.14.
|
||||
#
|
||||
CODE="
|
||||
$CONFTEST_PREAMBLE
|
||||
@ -4027,9 +4007,8 @@ compile_test() {
|
||||
# Determine if drm_connector_attach_vrr_capable_property and
|
||||
# drm_connector_set_vrr_capable_property is present
|
||||
#
|
||||
# Added by commit ba1b0f6c73d4ea1390f0d5381f715ffa20c75f09 ("drm:
|
||||
# Add vrr_capable property to the drm connector") in v5.0-rc1
|
||||
# (2018-11-28)
|
||||
# Added by commit ba1b0f6c73d4 ("drm: Add vrr_capable property to
|
||||
# the drm connector") in v5.0.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_DRM_DRM_CONNECTOR_H_PRESENT)
|
||||
@ -4339,16 +4318,21 @@ compile_test() {
|
||||
# with the logic of "functions" the presence of
|
||||
# *either*_alpha_property or _blend_mode_property would be enough
|
||||
# to cause NV_DRM_ALPHA_BLENDING_AVAILABLE to be defined.
|
||||
|
||||
# drm_plane_create_alpha_property was added by commit
|
||||
# ae0e28265e21 ("drm/blend: Add a generic alpha property") in
|
||||
# v4.18.
|
||||
#
|
||||
# drm_plane_create_blend_mode_property was added by commit
|
||||
# a5ec8332d428 ("drm: Add per-plane pixel blend mode property")
|
||||
# in v4.20.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_DRM_DRM_BLEND_H_PRESENT)
|
||||
#include <drm/drm_blend.h>
|
||||
#endif
|
||||
void conftest_drm_alpha_blending_available(void) {
|
||||
/* 2018-04-11 ae0e28265e216dad11d4cbde42fc15e92919af78 */
|
||||
(void)drm_plane_create_alpha_property;
|
||||
|
||||
/* 2018-08-23 a5ec8332d4280500544e316f76c04a7adc02ce03 */
|
||||
(void)drm_plane_create_blend_mode_property;
|
||||
}"
|
||||
|
||||
@ -4359,10 +4343,10 @@ compile_test() {
|
||||
#
|
||||
# Determine if the DRM subsystem supports rotation.
|
||||
#
|
||||
# drm_plane_create_rotation_property() was added on 2016-09-26 by
|
||||
# d138dd3c0c70979215f3184cf36f95875e37932e (drm: Add support for
|
||||
# optional per-plane rotation property) in linux kernel. Presence
|
||||
# of it is sufficient to say that DRM subsystem support rotation.
|
||||
# drm_plane_create_rotation_property() was added by commit
|
||||
# d138dd3c0c70 ("drm: Add support for optional per-plane rotation
|
||||
# property") in v4.10. Presence of it is sufficient to say that
|
||||
# DRM subsystem support rotation.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_DRM_DRM_BLEND_H_PRESENT)
|
||||
@ -4381,8 +4365,8 @@ compile_test() {
|
||||
#
|
||||
# The DRIVER_PRIME flag was added by commit 3248877ea179 (drm:
|
||||
# base prime/dma-buf support (v5)) in v3.4 (2011-11-25) and is
|
||||
# removed by commit 0424fdaf883a (drm/prime: Actually remove
|
||||
# DRIVER_PRIME everywhere) on 2019-06-17.
|
||||
# removed by commit 0424fdaf883a ("drm/prime: Actually remove
|
||||
# DRIVER_PRIME everywhere") in v5.4.
|
||||
#
|
||||
# DRIVER_PRIME definition moved from drmP.h to drm_drv.h by
|
||||
# commit 85e634bce01a (drm: Extract drm_drv.h) in v4.10
|
||||
@ -4415,10 +4399,10 @@ compile_test() {
|
||||
#
|
||||
# drm_connector_for_each_possible_encoder() is added by commit
|
||||
# 83aefbb887b5 (drm: Add drm_connector_for_each_possible_encoder())
|
||||
# in v4.19. The definition and prorotype is changed to take only
|
||||
# two arguments connector and encoder, by commit 62afb4ad425a
|
||||
# (drm/connector: Allow max possible encoders to attach to a
|
||||
# connector) in v5.5rc1.
|
||||
# in v4.19. The definition and prototype is changed to take only
|
||||
# two arguments connector and encoder by commit 62afb4ad425a
|
||||
# ("drm/connector: Allow max possible encoders to attach to a
|
||||
# connector") in v5.5.
|
||||
#
|
||||
echo "$CONFTEST_PREAMBLE
|
||||
#if defined(NV_DRM_DRMP_H_PRESENT)
|
||||
@ -4468,6 +4452,24 @@ compile_test() {
|
||||
compile_check_conftest "$CODE" "NV_MMU_NOTIFIER_OPS_HAS_INVALIDATE_RANGE" "" "types"
|
||||
;;
|
||||
|
||||
mmu_notifier_ops_arch_invalidate_secondary_tlbs)
|
||||
#
|
||||
# Determine if the mmu_notifier_ops struct has the
|
||||
# 'arch_invalidate_secondary_tlbs' member.
|
||||
#
|
||||
# struct mmu_notifier_ops.invalidate_range was renamed to
|
||||
# arch_invalidate_secondary_tlbs by commit 1af5a8109904
|
||||
# ("mmu_notifiers: rename invalidate_range notifier") due to be
|
||||
# added in v6.6
|
||||
CODE="
|
||||
#include <linux/mmu_notifier.h>
|
||||
int conftest_mmu_notifier_ops_arch_invalidate_secondary_tlbs(void) {
|
||||
return offsetof(struct mmu_notifier_ops, arch_invalidate_secondary_tlbs);
|
||||
}"
|
||||
|
||||
compile_check_conftest "$CODE" "NV_MMU_NOTIFIER_OPS_HAS_ARCH_INVALIDATE_SECONDARY_TLBS" "" "types"
|
||||
;;
|
||||
|
||||
drm_format_num_planes)
|
||||
#
|
||||
# Determine if drm_format_num_planes() function is present.
|
||||
@ -4523,8 +4525,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if the 'struct proc_ops' type is present.
|
||||
#
|
||||
# Added by commit d56c0d45f0e2 ("proc: decouple proc from VFS with
|
||||
# "struct proc_ops"") in 5.6-rc1
|
||||
# Added by commit d56c0d45f0e2 ("proc: decouple proc from VFS
|
||||
# with "struct proc_ops"") in v5.6.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/proc_fs.h>
|
||||
@ -4582,8 +4584,8 @@ compile_test() {
|
||||
# Determine if 'drm_crtc_state' structure has a
|
||||
# 'vrr_enabled' field.
|
||||
#
|
||||
# Added by commit 1398958cfd8d331342d657d37151791dd7256b40 ("drm:
|
||||
# Add vrr_enabled property to drm CRTC") in v5.0-rc1 (2018-11-28)
|
||||
# Added by commit 1398958cfd8d ("drm: Add vrr_enabled property to
|
||||
# drm CRTC") in v5.0.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_DRM_DRM_CRTC_H_PRESENT)
|
||||
@ -4604,11 +4606,11 @@ compile_test() {
|
||||
# Added by commit fb7fcc96a86cf ("timekeeping: Standardize on
|
||||
# ktime_get_*() naming") in 4.18 (2018-04-27)
|
||||
#
|
||||
CODE="
|
||||
#include <linux/ktime.h>
|
||||
void conftest_ktime_get_raw_ts64(void){
|
||||
ktime_get_raw_ts64();
|
||||
}"
|
||||
CODE="
|
||||
#include <linux/ktime.h>
|
||||
void conftest_ktime_get_raw_ts64(void){
|
||||
ktime_get_raw_ts64();
|
||||
}"
|
||||
compile_check_conftest "$CODE" "NV_KTIME_GET_RAW_TS64_PRESENT" "" "functions"
|
||||
;;
|
||||
|
||||
@ -4619,11 +4621,11 @@ compile_test() {
|
||||
# Added by commit d6d29896c665d ("timekeeping: Provide timespec64
|
||||
# based interfaces") in 3.17 (2014-07-16)
|
||||
#
|
||||
CODE="
|
||||
#include <linux/ktime.h>
|
||||
void conftest_ktime_get_real_ts64(void){
|
||||
ktime_get_real_ts64();
|
||||
}"
|
||||
CODE="
|
||||
#include <linux/ktime.h>
|
||||
void conftest_ktime_get_real_ts64(void){
|
||||
ktime_get_real_ts64();
|
||||
}"
|
||||
compile_check_conftest "$CODE" "NV_KTIME_GET_REAL_TS64_PRESENT" "" "functions"
|
||||
;;
|
||||
|
||||
@ -4643,8 +4645,9 @@ compile_test() {
|
||||
# -the "modifier[]" member of the AddFB2 ioctl's parameter
|
||||
# structure.
|
||||
#
|
||||
# All these were added by commit e3eb3250d84e (drm: add support for
|
||||
# tiled/compressed/etc modifier in addfb2) in 4.1-rc1 (2015-02-05).
|
||||
# All these were added by commit e3eb3250d84e ("drm: add support
|
||||
# for tiled/compressed/etc modifier in addfb2") in v4.1.
|
||||
#
|
||||
CODE="
|
||||
#include <drm/drm_mode.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
@ -4664,11 +4667,11 @@ compile_test() {
|
||||
# Added by commit 361a3bf00582 ("time64: Add time64.h header and
|
||||
# define struct timespec64") in 3.17 (2014-07-16)
|
||||
#
|
||||
CODE="
|
||||
#include <linux/time.h>
|
||||
CODE="
|
||||
#include <linux/time.h>
|
||||
|
||||
struct timespec64 ts64;
|
||||
"
|
||||
struct timespec64 ts64;
|
||||
"
|
||||
compile_check_conftest "$CODE" "NV_TIMESPEC64_PRESENT" "" "types"
|
||||
|
||||
;;
|
||||
@ -4680,15 +4683,15 @@ compile_test() {
|
||||
# The third argument to __vmalloc, page protection
|
||||
# 'pgprot_t prot', was removed by commit 88dca4ca5a93
|
||||
# (mm: remove the pgprot argument to __vmalloc)
|
||||
# in v5.8-rc1 (2020-06-01).
|
||||
CODE="
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
void conftest_vmalloc_has_pgprot_t_arg(void) {
|
||||
pgprot_t prot;
|
||||
(void)__vmalloc(0, 0, prot);
|
||||
}"
|
||||
# in v5.8.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
void conftest_vmalloc_has_pgprot_t_arg(void) {
|
||||
pgprot_t prot;
|
||||
(void)__vmalloc(0, 0, prot);
|
||||
}"
|
||||
compile_check_conftest "$CODE" "NV_VMALLOC_HAS_PGPROT_T_ARG" "" "types"
|
||||
|
||||
;;
|
||||
@ -4699,7 +4702,8 @@ compile_test() {
|
||||
#
|
||||
# Kernel commit da1c55f1b272 ("mmap locking API: rename mmap_sem
|
||||
# to mmap_lock") replaced the field 'mmap_sem' by 'mmap_lock'
|
||||
# in v5.8-rc1 (2020-06-08).
|
||||
# in v5.8.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/mm_types.h>
|
||||
|
||||
@ -4789,9 +4793,9 @@ compile_test() {
|
||||
;;
|
||||
|
||||
pci_enable_atomic_ops_to_root)
|
||||
# pci_enable_atomic_ops_to_root was added by
|
||||
# commit 430a23689dea ("PCI: Add pci_enable_atomic_ops_to_root()")
|
||||
# in v4.16-rc1 (2018-01-05)
|
||||
#
|
||||
# pci_enable_atomic_ops_to_root was added by commit 430a23689dea
|
||||
# ("PCI: Add pci_enable_atomic_ops_to_root()") in v4.16.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/pci.h>
|
||||
@ -4808,11 +4812,11 @@ compile_test() {
|
||||
# Added by commit a7c3e901a46ff54c016d040847eda598a9e3e653 ("mm:
|
||||
# introduce kv[mz]alloc helpers") in v4.12 (2017-05-08).
|
||||
#
|
||||
CODE="
|
||||
#include <linux/mm.h>
|
||||
void conftest_kvmalloc(void){
|
||||
kvmalloc();
|
||||
}"
|
||||
CODE="
|
||||
#include <linux/mm.h>
|
||||
void conftest_kvmalloc(void){
|
||||
kvmalloc();
|
||||
}"
|
||||
compile_check_conftest "$CODE" "NV_KVMALLOC_PRESENT" "" "functions"
|
||||
|
||||
;;
|
||||
@ -4821,12 +4825,11 @@ compile_test() {
|
||||
#
|
||||
# Determine if the function drm_gem_object_put_unlocked() is present.
|
||||
#
|
||||
# In v5.9-rc1, commit 2f4dd13d4bb8 ("drm/gem: add
|
||||
# drm_gem_object_put helper") removes drm_gem_object_put_unlocked()
|
||||
# function and replace its definition by transient macro. Commit
|
||||
# ab15d56e27be ("drm: remove transient
|
||||
# drm_gem_object_put_unlocked()") finally removes
|
||||
# drm_gem_object_put_unlocked() macro.
|
||||
# Replaced with a transient macro by commit 2f4dd13d4bb8 ("drm/gem:
|
||||
# add drm_gem_object_put helper") in v5.9.
|
||||
#
|
||||
# Finally removed by commit ab15d56e27be ("drm: remove transient
|
||||
# drm_gem_object_put_unlocked()") in v5.9.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_DRM_DRMP_H_PRESENT)
|
||||
@ -4849,7 +4852,7 @@ compile_test() {
|
||||
# field.
|
||||
#
|
||||
# Removed by commit 0425662fdf05 ("drm: Nuke mode->vrefresh") in
|
||||
# v5.9-rc1.
|
||||
# v5.9.
|
||||
#
|
||||
CODE="
|
||||
#include <drm/drm_modes.h>
|
||||
@ -4867,7 +4870,7 @@ compile_test() {
|
||||
# Determine if drm_driver::master_set() returns integer value
|
||||
#
|
||||
# Changed to void by commit 907f53200f98 ("drm: vmwgfx: remove
|
||||
# drm_driver::master_set() return type") in v5.9-rc1.
|
||||
# drm_driver::master_set() return type") in v5.9.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_DRM_DRMP_H_PRESENT)
|
||||
@ -4893,7 +4896,7 @@ compile_test() {
|
||||
# function pointer.
|
||||
#
|
||||
# drm_driver::gem_free_object is removed by commit 1a9458aeb8eb
|
||||
# ("drm: remove drm_driver::gem_free_object") in v5.9-rc1.
|
||||
# ("drm: remove drm_driver::gem_free_object") in v5.9.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_DRM_DRMP_H_PRESENT)
|
||||
@ -4916,7 +4919,7 @@ compile_test() {
|
||||
# Determine if vga_tryget() is present
|
||||
#
|
||||
# vga_tryget() was removed by commit f369bc3f9096 ("vgaarb: mark
|
||||
# vga_tryget static") in v5.9-rc1 (2020-08-01).
|
||||
# vga_tryget static") in v5.9.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/vgaarb.h>
|
||||
@ -4933,7 +4936,7 @@ compile_test() {
|
||||
#
|
||||
# pci_channel_state was removed by commit 16d79cd4e23b ("PCI: Use
|
||||
# 'pci_channel_state_t' instead of 'enum pci_channel_state'") in
|
||||
# v5.9-rc1 (2020-07-02).
|
||||
# v5.9.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/pci.h>
|
||||
@ -4949,7 +4952,8 @@ compile_test() {
|
||||
# Determine if 'cc_platform_has()' is present.
|
||||
#
|
||||
# Added by commit aa5a461171f9 ("x86/sev: Add an x86 version of
|
||||
# cc_platform_has()") in v5.15.3 (2021-10-04)
|
||||
# cc_platform_has()") in v5.16.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_LINUX_CC_PLATFORM_H_PRESENT)
|
||||
#include <linux/cc_platform.h>
|
||||
@ -4966,8 +4970,9 @@ compile_test() {
|
||||
#
|
||||
# Determine if drm_prime_pages_to_sg() has 'dev' argument.
|
||||
#
|
||||
# drm_prime_pages_to_sg() is updated to take 'dev' argument by commit
|
||||
# 707d561f77b5 ("drm: allow limiting the scatter list size.").
|
||||
# drm_prime_pages_to_sg() is updated to take 'dev' argument by
|
||||
# commit 707d561f77b5 ("drm: allow limiting the scatter list
|
||||
# size.") in v5.10.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_DRM_DRMP_H_PRESENT)
|
||||
@ -4991,9 +4996,9 @@ compile_test() {
|
||||
# Determine if drm_driver structure has the GEM and PRIME callback
|
||||
# function pointers.
|
||||
#
|
||||
# The GEM and PRIME callback are removed from drm_driver
|
||||
# structure, by commit d693def4fd1c ("drm: Remove obsolete GEM and
|
||||
# PRIME callbacks from struct drm_driver").
|
||||
# The GEM and PRIME callbacks are removed from drm_driver
|
||||
# structure by commit d693def4fd1c ("drm: Remove obsolete GEM and
|
||||
# PRIME callbacks from struct drm_driver") in v5.11.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_DRM_DRMP_H_PRESENT)
|
||||
@ -5022,8 +5027,8 @@ compile_test() {
|
||||
# Determine if drm_crtc_helper_funcs::atomic_check takes 'state'
|
||||
# argument of 'struct drm_atomic_state' type.
|
||||
#
|
||||
# The commit 29b77ad7b9ca ("drm/atomic: Pass the full state to CRTC
|
||||
# atomic_check") passed the full atomic state to
|
||||
# Commit 29b77ad7b9ca ("drm/atomic: Pass the full state to CRTC
|
||||
# atomic_check") in v5.11 passed the full atomic state to
|
||||
# drm_crtc_helper_funcs::atomic_check()
|
||||
#
|
||||
# To test the signature of drm_crtc_helper_funcs::atomic_check(),
|
||||
@ -5059,9 +5064,9 @@ compile_test() {
|
||||
# Determine if drm_gem_object_funcs::vmap takes 'map'
|
||||
# argument of 'struct dma_buf_map' type.
|
||||
#
|
||||
# The commit 49a3f51dfeee ("drm/gem: Use struct dma_buf_map in GEM
|
||||
# vmap ops and convert GEM backends") update
|
||||
# drm_gem_object_funcs::vmap to take 'map' argument.
|
||||
# drm_gem_object_funcs::vmap is updated to take 'map' argument by
|
||||
# commit 49a3f51dfeee ("drm/gem: Use struct dma_buf_map in GEM
|
||||
# vmap ops and convert GEM backends") in v5.11.
|
||||
#
|
||||
CODE="
|
||||
#include <drm/drm_gem.h>
|
||||
@ -5078,7 +5083,7 @@ compile_test() {
|
||||
# Determine if seq_read_iter() is present
|
||||
#
|
||||
# seq_read_iter() was added by commit d4d50710a8b4 ("seq_file:
|
||||
# add seq_read_iter") in v5.10-rc1 (2020-11-04).
|
||||
# add seq_read_iter") in v5.10.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/seq_file.h>
|
||||
@ -5096,7 +5101,7 @@ compile_test() {
|
||||
#
|
||||
# The commit 07f4f97d7b4b ("vga_switcheroo: Use device link for HDA
|
||||
# controller") has moved 'PCI_CLASS_MULTIMEDIA_HD_AUDIO' macro from
|
||||
# <sound/hdaudio.h> to <linux/pci_ids.h> in v4.17-rc1 (2018-03-03).
|
||||
# <sound/hdaudio.h> to <linux/pci_ids.h> in v4.17.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/pci_ids.h>
|
||||
@ -5114,6 +5119,9 @@ compile_test() {
|
||||
# unsafe_follow_pfn() was added by commit 69bacee7f9ad
|
||||
# ("mm: Add unsafe_follow_pfn") in v5.13-rc1.
|
||||
#
|
||||
# Note: this commit never made it to the linux kernel, so
|
||||
# unsafe_follow_pfn() never existed.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/mm.h>
|
||||
void conftest_unsafe_follow_pfn(void) {
|
||||
@ -5128,8 +5136,8 @@ compile_test() {
|
||||
# Determine if drm_plane_helper_funcs::atomic_check takes 'state'
|
||||
# argument of 'struct drm_atomic_state' type.
|
||||
#
|
||||
# The commit 7c11b99a8e58 ("drm/atomic: Pass the full state to
|
||||
# planes atomic_check") passed the full atomic state to
|
||||
# Commit 7c11b99a8e58 ("drm/atomic: Pass the full state to planes
|
||||
# atomic_check") in v5.13 passes the full atomic state to
|
||||
# drm_plane_helper_funcs::atomic_check()
|
||||
#
|
||||
# To test the signature of drm_plane_helper_funcs::atomic_check(),
|
||||
@ -5193,7 +5201,7 @@ compile_test() {
|
||||
# Determine if the add_memory_driver_managed function is present
|
||||
#
|
||||
# Added by commit 7b7b27214bba ("mm/memory_hotplug: introduce
|
||||
# add_memory_driver_managed()") in v5.8-rc1 (2020-06-05)
|
||||
# add_memory_driver_managed()") in v5.8.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/memory_hotplug.h>
|
||||
@ -5208,8 +5216,8 @@ compile_test() {
|
||||
#
|
||||
# Check if add_memory_driver_managed() has mhp_flags arg.
|
||||
#
|
||||
# Added by commit b6117199787c ("mm/memory_hotplug: prepare passing flags to
|
||||
# add_memory() and friends") in v5.10-rc1 (2020-10-16)
|
||||
# Added by commit b6117199787c ("mm/memory_hotplug: prepare
|
||||
# passing flags to add_memory() and friends") in v5.10.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/memory_hotplug.h>
|
||||
@ -5226,8 +5234,8 @@ compile_test() {
|
||||
#
|
||||
# Check if remove_memory() has nid parameter.
|
||||
#
|
||||
# Removed by commit e1c158e4956612e7 ("mm/memory_hotplug: remove nid
|
||||
# parameter from remove_memory() and friends") in v5.15-rc1 (2021-09-09)
|
||||
# Removed by commit e1c158e49566 ("mm/memory_hotplug: remove nid
|
||||
# parameter from remove_memory() and friends") in v5.15.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/memory_hotplug.h>
|
||||
@ -5242,8 +5250,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if the offline_and_remove_memory function is present.
|
||||
#
|
||||
# Added by commit 08b3acd7a68fc179 ("mm/memory_hotplug: Introduce
|
||||
# offline_and_remove_memory()") in v5.8-rc1 (2020-06-05)
|
||||
# Added by commit 08b3acd7a68f ("mm/memory_hotplug: Introduce
|
||||
# offline_and_remove_memory()") in v5.8.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/memory_hotplug.h>
|
||||
@ -5258,8 +5266,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if the device_property_read_u64 function is present
|
||||
#
|
||||
# Added by commit b31384fa5de37a1 ("Driver core: Unified device
|
||||
# properties interface for platform firmware") in v3.19-rc1 (2014-11-05)
|
||||
# Added by commit b31384fa5de3 ("Driver core: Unified device
|
||||
# properties interface for platform firmware") in v3.19.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/acpi.h>
|
||||
@ -5274,8 +5282,12 @@ compile_test() {
|
||||
#
|
||||
# Determine if of_property_count_elems_of_size is present
|
||||
#
|
||||
# Added by commit 1df09bcof (" Move OF property and graph API from
|
||||
# base.c to property.c"
|
||||
# Added by commit ad54a0cfbeb4 ("of: add functions to count
|
||||
# number of elements in a property") in v3.15.
|
||||
#
|
||||
# Moved from base.c to property.c by commit 1df09bc66f9b ("of:
|
||||
# Move OF property and graph API from base.c to property.c") in
|
||||
# v4.13.
|
||||
#
|
||||
# Test if linux/of.h header file inclusion is successful or not,
|
||||
# depending on that check, for of_property_count_elems_of_size
|
||||
@ -5306,8 +5318,12 @@ compile_test() {
|
||||
#
|
||||
# Determine if of_property_read_variable_u8_array is present
|
||||
#
|
||||
# Added by commit 1df09bcof (" Move OF property and graph API from
|
||||
# base.c to property.c"
|
||||
# Added by commit a67e9472da42 ("of: Add array read functions
|
||||
# with min/max size limits") in v4.9.
|
||||
#
|
||||
# Moved from base.c to property.c by commit 1df09bc66f9b ("of:
|
||||
# Move OF property and graph API from base.c to property.c") in
|
||||
# v4.13.
|
||||
#
|
||||
# Test if linux/of.h header file inclusion is successful or not,
|
||||
# depending on that, check for of_property_read_variable_u8_array
|
||||
@ -5338,8 +5354,15 @@ compile_test() {
|
||||
#
|
||||
# Determine if of_property_read_variable_u32_array is present
|
||||
#
|
||||
# Added by commit 1df09bcof (" Move OF property and graph API from
|
||||
# base.c to property.c"
|
||||
# Added by commit a67e9472da42 ("of: Add array read functions
|
||||
# with min/max size limits") in v4.9.
|
||||
#
|
||||
# Moved from base.c to property.c by commit 1df09bc66f9b ("of:
|
||||
# Move OF property and graph API from base.c to property.c") in
|
||||
# v4.13.
|
||||
#
|
||||
# Note: this can probably be combined with the
|
||||
# of_property_read_variable_u8_array conftest above.
|
||||
#
|
||||
# Test if linux/of.h header file inclusion is successful or not,
|
||||
# depending on that, check for of_property_read_variable_u32_array
|
||||
@ -5370,8 +5393,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if devm_of_platform_populate() function is present
|
||||
#
|
||||
# Added by commit 38b0b21of (add devm_ functions for populate and
|
||||
# depopulate")
|
||||
# Added by commit 38b0b219fbe8 ("of: add devm_ functions for
|
||||
# populate and depopulate") in v4.12.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_LINUX_OF_PLATFORM_H_PRESENT)
|
||||
@ -5389,8 +5412,13 @@ compile_test() {
|
||||
#
|
||||
# Determine if of_dma_configure() function is present
|
||||
#
|
||||
# Added by commit 591c1eeof ("configure the platform device
|
||||
# dma parameters")
|
||||
# Added by commit 591c1ee465ce ("of: configure the platform
|
||||
# device dma parameters") in v3.16. However, it was a static,
|
||||
# non-exported function at that time.
|
||||
#
|
||||
# It was moved from platform.c to device.c and made public by
|
||||
# commit 1f5c69aa51f9 ("of: Move of_dma_configure() to device.c
|
||||
# to help re-use") in v4.1.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_LINUX_OF_DEVICE_H_PRESENT)
|
||||
@ -5409,8 +5437,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if icc_get() function is present
|
||||
#
|
||||
# Added by commit 11f1cec ("interconnect: Add generic on-chip
|
||||
# interconnect API")
|
||||
# Added by commit 11f1ceca7031 ("interconnect: Add generic
|
||||
# on-chip interconnect API") in v5.1.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_LINUX_INTERCONNECT_H_PRESENT)
|
||||
@ -5429,8 +5457,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if icc_set_bw() function is present
|
||||
#
|
||||
# Added by commit 11f1cec ("interconnect: Add generic on-chip
|
||||
# interconnect API")
|
||||
# Added by commit 11f1ceca7031 ("interconnect: Add generic
|
||||
# on-chip interconnect API") in v5.1.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_LINUX_INTERCONNECT_H_PRESENT)
|
||||
@ -5449,8 +5477,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if icc_put() function is present
|
||||
#
|
||||
# Added by commit 11f1cec ("interconnect: Add generic on-chip
|
||||
# interconnect API")
|
||||
# Added by commit 11f1ceca7031 ("interconnect: Add generic
|
||||
# on-chip interconnect API") in v5.1.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_LINUX_INTERCONNECT_H_PRESENT)
|
||||
@ -5469,7 +5497,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if i2c_new_client_device() function is present
|
||||
#
|
||||
# Added by commit 390fd04i2c ("remove deprecated i2c_new_device API")
|
||||
# Added by commit 390fd0475af5 ("i2c: remove deprecated
|
||||
# i2c_new_device API") in v5.8.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/i2c.h>
|
||||
@ -5486,7 +5515,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if i2c_unregister_device() function is present
|
||||
#
|
||||
# Added by commit 9c1600ei2c ("Add i2c_board_info and i2c_new_device()")
|
||||
# Added by commit 9c1600eda42e ("i2c: Add i2c_board_info and
|
||||
# i2c_new_device()") in v2.6.22.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/i2c.h>
|
||||
@ -5503,8 +5533,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if of_get_named_gpio() function is present
|
||||
#
|
||||
# Added by commit a6b0919 ("of/gpio: Add new method for getting gpios
|
||||
# under different property names")
|
||||
# Added by commit a6b0919140b4 ("of/gpio: Add new method for
|
||||
# getting gpios under different property names") in v3.1.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_LINUX_OF_GPIO_H_PRESENT)
|
||||
@ -5523,7 +5553,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if devm_gpio_request_one() function is present
|
||||
#
|
||||
# Added by commit 09d71ff (gpiolib: Implement devm_gpio_request_one()")
|
||||
# Added by commit 09d71ff19404 ("gpiolib: Implement
|
||||
# devm_gpio_request_one()") in v3.5.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_LINUX_GPIO_H_PRESENT)
|
||||
@ -5542,7 +5573,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if gpio_direction_input() function is present
|
||||
#
|
||||
# Added by commit c7caf86 (gpio: remove gpio_ensure_requested()")
|
||||
# Added by commit c7caf86823c7 ("gpio: remove
|
||||
# gpio_ensure_requested()") in v3.17.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_LINUX_GPIO_H_PRESENT)
|
||||
@ -5561,7 +5593,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if gpio_direction_output() function is present
|
||||
#
|
||||
# Added by commit c7caf86 (gpio: remove gpio_ensure_requested()")
|
||||
# Added by commit c7caf86823c7 ("gpio: remove
|
||||
# gpio_ensure_requested()") in v3.17.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_LINUX_GPIO_H_PRESENT)
|
||||
@ -5580,8 +5613,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if gpio_get_value() function is present
|
||||
#
|
||||
# Added by commit 7563bbf ("gpiolib/arches: Centralise bolierplate
|
||||
# asm/gpio.h")
|
||||
# Added by commit 7563bbf89d06 ("gpiolib/arches: Centralise
|
||||
# bolierplate asm/gpio.h") in v3.5.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_LINUX_GPIO_H_PRESENT)
|
||||
@ -5600,8 +5633,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if gpio_set_value() function is present
|
||||
#
|
||||
# Added by commit 7563bbf ("gpiolib/arches: Centralise bolierplate
|
||||
# asm/gpio.h")
|
||||
# Added by commit 7563bbf89d06 ("gpiolib/arches: Centralise
|
||||
# bolierplate asm/gpio.h") in v3.5.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_LINUX_GPIO_H_PRESENT)
|
||||
@ -5620,8 +5653,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if gpio_to_irq() function is present
|
||||
#
|
||||
# Added by commit 7563bbf ("gpiolib/arches: Centralise bolierplate
|
||||
# asm/gpio.h")
|
||||
# Added by commit 7563bbf89d06 ("gpiolib/arches: Centralise
|
||||
# bolierplate asm/gpio.h") in v3.5.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_LINUX_GPIO_H_PRESENT)
|
||||
@ -5636,14 +5669,29 @@ compile_test() {
|
||||
compile_check_conftest "$CODE" "NV_GPIO_TO_IRQ_PRESENT" "" "functions"
|
||||
;;
|
||||
|
||||
migrate_vma_setup)
|
||||
#
|
||||
# Determine if migrate_vma_setup() function is present
|
||||
#
|
||||
# Added by commit a7d1f22bb74f ("mm: turn migrate_vma upside
|
||||
# down") in v5.4.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/migrate.h>
|
||||
int conftest_migrate_vma_setup(void) {
|
||||
migrate_vma_setup();
|
||||
}"
|
||||
|
||||
compile_check_conftest "$CODE" "NV_MIGRATE_VMA_SETUP_PRESENT" "" "functions"
|
||||
;;
|
||||
|
||||
migrate_vma_added_flags)
|
||||
#
|
||||
# Determine if migrate_vma structure has flags
|
||||
#
|
||||
# flags were added to struct migrate_vma by commit
|
||||
# 5143192cd410c4fc83be09a2e73423765aee072b ("mm/migrate: add a flags
|
||||
# parameter to_migrate_vma) in v5.9.
|
||||
# (2020-07-28).
|
||||
# Added by commit 5143192cd410 ("mm/migrate: add a flags
|
||||
# parameter to migrate_vma") in v5.9.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/migrate.h>
|
||||
int conftest_migrate_vma_added_flags(void) {
|
||||
@ -5658,7 +5706,7 @@ compile_test() {
|
||||
# Determine if the 'drm_device' structure has a 'pdev' field.
|
||||
#
|
||||
# Removed by commit b347e04452ff ("drm: Remove pdev field from
|
||||
# struct drm_device") in v5.14-rc1.
|
||||
# struct drm_device") in v5.14.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_DRM_DRMP_H_PRESENT)
|
||||
@ -5712,9 +5760,9 @@ compile_test() {
|
||||
#
|
||||
# Determine if ioasid_get() function is present
|
||||
#
|
||||
# ioasid_get() function was added by commit
|
||||
# cb4789b0d19ff231ce9f73376a023341300aed96 (iommu/ioasid: Add ioasidreferences) in v5.11.
|
||||
# (2020-11-23).
|
||||
# Added by commit cb4789b0d19f ("iommu/ioasid: Add ioasid
|
||||
# references") in v5.11.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_LINUX_IOASID_H_PRESENT)
|
||||
#include <linux/ioasid.h>
|
||||
@ -5751,9 +5799,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if the 'drm_crtc_state' structure has 'no_vblank'.
|
||||
#
|
||||
# drm_crtc_state::no_vblank was added by commit b25c60af7a877
|
||||
# ("drm/crtc: Add a generic infrastructure to fake VBLANK events")
|
||||
# in 4.18.0-rc3 (2018-07-03).
|
||||
# Added by commit b25c60af7a87 ("drm/crtc: Add a generic
|
||||
# infrastructure to fake VBLANK events") in v4.19.
|
||||
#
|
||||
CODE="
|
||||
#include <drm/drm_crtc.h>
|
||||
@ -5773,7 +5820,7 @@ compile_test() {
|
||||
# an 'allow_fb_modifiers' field in the 'drm_mode_config' structure,
|
||||
# is added by commit e3eb3250d84e ("drm: add support for
|
||||
# tiled/compressed/etc modifier in addfb2") in v4.1, and removed by
|
||||
# commit 3d082157a242 ("drm: remove allow_fb_modifiers") in v5.18-rc1.
|
||||
# commit 3d082157a242 ("drm: remove allow_fb_modifiers") in v5.18.
|
||||
#
|
||||
# The 'struct drm_mode_config' definition, is moved to
|
||||
# drm_mode_config.h file by commit 28575f165d36 ("drm: Extract
|
||||
@ -5811,9 +5858,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if drm_mode.h has 'hdr_output_metadata' structure.
|
||||
#
|
||||
# struct hdr_output_metadata was added by commit fbb5d0353c62d
|
||||
# ("drm: Add HDR source metadata property") in 5.1.0-rc5
|
||||
# (2019-05-16)
|
||||
# Added by commit fbb5d0353c62 ("drm: Add HDR source metadata
|
||||
# property") in v5.3.
|
||||
#
|
||||
CODE="
|
||||
#include <drm/drm_mode.h>
|
||||
@ -5840,9 +5886,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if the platform_irq_count() function is present
|
||||
#
|
||||
# platform_irq_count was added by commit
|
||||
# 4b83555d5098e73cf2c5ca7f86c17ca0ba3b968e ("driver-core: platform: Add platform_irq_count()")
|
||||
# in 4.5-rc1 (2016-01-07)
|
||||
# Added by commit 4b83555d5098 ("driver-core: platform: Add
|
||||
# platform_irq_count()") in v4.5.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/platform_device.h>
|
||||
@ -5856,7 +5901,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if devm_clk_bulk_get_all() function is present
|
||||
#
|
||||
# Added by commit f08c2e286 ("clk: add managed version of clk_bulk_get_all")
|
||||
# Added by commit f08c2e2865f6 ("clk: add managed version of
|
||||
# clk_bulk_get_all") in v4.20.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_LINUX_CLK_H_PRESENT)
|
||||
@ -5923,7 +5969,7 @@ compile_test() {
|
||||
# dma_resv_add_excl_fence() and dma_resv_add_shared_fence() were
|
||||
# removed and replaced with dma_resv_add_fence() by commit
|
||||
# 73511edf8b19 ("dma-buf: specify usage while adding fences to
|
||||
# dma_resv obj v7") in linux-next, expected in v5.19-rc1.
|
||||
# dma_resv obj v7") in v5.19.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_LINUX_DMA_RESV_H_PRESENT)
|
||||
@ -5943,7 +5989,7 @@ compile_test() {
|
||||
# dma_resv_reserve_shared() was removed and replaced with
|
||||
# dma_resv_reserve_fences() by commit c8d4c18bfbc4
|
||||
# ("dma-buf/drivers: make reserving a shared slot mandatory v4") in
|
||||
# linux-next, expected in v5.19-rc1.
|
||||
# v5.19.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_LINUX_DMA_RESV_H_PRESENT)
|
||||
@ -5963,8 +6009,7 @@ compile_test() {
|
||||
#
|
||||
# reservation_object_reserve_shared() function prototype was updated
|
||||
# to take 'num_fences' argument by commit ca05359f1e64 ("dma-buf:
|
||||
# allow reserving more than one shared fence slot") in v4.21-rc1
|
||||
# (2018-12-14).
|
||||
# allow reserving more than one shared fence slot") in v5.0.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/reservation.h>
|
||||
@ -5981,9 +6026,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if the __get_task_ioprio() function is present.
|
||||
#
|
||||
# __get_task_ioprio was added by commit 893e5d32d583
|
||||
# ("block: Generalize get_current_ioprio() for any task") for
|
||||
# v5.20 linux-next (2022-06-23).
|
||||
# Added by commit 893e5d32d583 ("block: Generalize
|
||||
# get_current_ioprio() for any task") in v6.0.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/ioprio.h>
|
||||
@ -5998,9 +6042,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if 'num_registered_fb' variable is present.
|
||||
#
|
||||
# 'num_registered_fb' was removed by commit 5727dcfd8486
|
||||
# ("fbdev: Make registered_fb[] private to fbmem.c") for
|
||||
# v5.20 linux-next (2022-07-27).
|
||||
# Removed by commit 5727dcfd8486 ("fbdev: Make registered_fb[]
|
||||
# private to fbmem.c") in v6.1.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/fb.h>
|
||||
@ -6146,9 +6189,8 @@ compile_test() {
|
||||
#
|
||||
# Determine if 'struct drm_connector' has an 'override_edid' member.
|
||||
#
|
||||
# Removed by commit 90b575f52c6ab ("drm/edid: detach debugfs EDID
|
||||
# override from EDID property update") in linux-next, expected in
|
||||
# v6.2-rc1.
|
||||
# Removed by commit 90b575f52c6a ("drm/edid: detach debugfs EDID
|
||||
# override from EDID property update") in v6.2.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_DRM_DRM_CRTC_H_PRESENT)
|
||||
@ -6190,10 +6232,9 @@ compile_test() {
|
||||
# Determine if the 'vm_area_struct' structure has
|
||||
# const 'vm_flags'.
|
||||
#
|
||||
# A union of '__vm_flags' and 'const vm_flags' was added
|
||||
# by commit bc292ab00f6c ("mm: introduce vma->vm_flags
|
||||
# wrapper functions") in mm-stable branch (2023-02-09)
|
||||
# of the akpm/mm maintainer tree.
|
||||
# A union of '__vm_flags' and 'const vm_flags' was added by
|
||||
# commit bc292ab00f6c ("mm: introduce vma->vm_flags wrapper
|
||||
# functions") in v6.3.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/mm_types.h>
|
||||
@ -6209,8 +6250,8 @@ compile_test() {
|
||||
# Determine if the 'drm_driver' structure has a 'dumb_destroy'
|
||||
# function pointer.
|
||||
#
|
||||
# Removed by commit 96a7b60f6ddb2 ("drm: remove dumb_destroy
|
||||
# callback") in v6.3 linux-next (2023-02-10).
|
||||
# Removed by commit 96a7b60f6ddb ("drm: remove dumb_destroy
|
||||
# callback") in v6.4.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_DRM_DRMP_H_PRESENT)
|
||||
@ -6232,9 +6273,8 @@ compile_test() {
|
||||
#
|
||||
# Check if memory_failure() has trapno parameter.
|
||||
#
|
||||
# trapno argument was removed by commit
|
||||
# 83b57531c58f4173d1c0d0b2c0bc88c853c32ea5 ("mm/memory_failure:
|
||||
# Remove unused trapno from memory_failure") in v4.15.0 (2017-7-9)
|
||||
# Removed by commit 83b57531c58f ("mm/memory_failure: Remove
|
||||
# unused trapno from memory_failure") in v4.16.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/mm.h>
|
||||
@ -6251,9 +6291,8 @@ compile_test() {
|
||||
#
|
||||
# Check if memory_failure() flag MF_SW_SIMULATED is defined.
|
||||
#
|
||||
# MF_SW_SIMULATED was added by commit
|
||||
# 67f22ba7750f940bcd7e1b12720896c505c2d63f ("mm/hwpoison:
|
||||
# fix unpoison_memory()") in v5.19.0-rc2 (2022-6-16)
|
||||
# Added by commit 67f22ba7750f ("mm/memory-failure: disable
|
||||
# unpoison once hw error happens") in v5.19.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/mm.h>
|
||||
@ -6264,6 +6303,186 @@ compile_test() {
|
||||
compile_check_conftest "$CODE" "NV_MEMORY_FAILURE_MF_SW_SIMULATED_DEFINED" "" "types"
|
||||
;;
|
||||
|
||||
sync_file_get_fence)
|
||||
#
|
||||
# Determine if sync_file_get_fence() function is present
|
||||
#
|
||||
# Added by commit 972526a40932 ("dma-buf/sync_file: add
|
||||
# sync_file_get_fence()") in v4.9.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_LINUX_SYNC_FILE_H_PRESENT)
|
||||
#include <linux/sync_file.h>
|
||||
#endif
|
||||
void conftest_sync_file_get_fence(void)
|
||||
{
|
||||
sync_file_get_fence();
|
||||
}"
|
||||
|
||||
compile_check_conftest "$CODE" "NV_SYNC_FILE_GET_FENCE_PRESENT" "" "functions"
|
||||
;;
|
||||
|
||||
dma_fence_set_error)
|
||||
#
|
||||
# Determine if dma_fence_set_error() function is present
|
||||
#
|
||||
# Added by commit a009e975da5c ("dma-fence: Introduce
|
||||
# drm_fence_set_error() helper") in v4.11.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_LINUX_DMA_FENCE_H_PRESENT)
|
||||
#include <linux/dma-fence.h>
|
||||
#endif
|
||||
void conftest_dma_fence_set_error(void)
|
||||
{
|
||||
dma_fence_set_error();
|
||||
}"
|
||||
|
||||
compile_check_conftest "$CODE" "NV_DMA_FENCE_SET_ERROR_PRESENT" "" "functions"
|
||||
;;
|
||||
|
||||
fence_set_error)
|
||||
#
|
||||
# Determine if fence_set_error() function is present
|
||||
#
|
||||
# fence_set_error is a different name for dma_fence_set_error
|
||||
# present in kernels where commit a009e975da5c ("dma-fence:
|
||||
# Introduce drm_fence_set_error() helper") from v4.11 was
|
||||
# backported, but commit f54d1867005c ("dma-buf: Rename struct fence
|
||||
# to dma_fence") from v4.10 was not. In particular, Tegra v4.9
|
||||
# kernels, such as commit f5e0724e76c2 ("dma-fence: Introduce
|
||||
# drm_fence_set_error() helper") in NVIDIA Linux for Tegra (L4T) r31
|
||||
# and r32 kernels in the L4T kernel repo
|
||||
# git://nv-tegra.nvidia.com/linux-4.9.git, contain this function.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_LINUX_FENCE_H_PRESENT)
|
||||
#include <linux/fence.h>
|
||||
#endif
|
||||
void conftest_fence_set_error(void)
|
||||
{
|
||||
fence_set_error();
|
||||
}"
|
||||
|
||||
compile_check_conftest "$CODE" "NV_FENCE_SET_ERROR_PRESENT" "" "functions"
|
||||
;;
|
||||
|
||||
fence_ops_use_64bit_seqno)
|
||||
#
|
||||
# Determine if dma_fence_ops has the use_64bit_seqno member
|
||||
#
|
||||
# 64-bit fence seqno support was actually added by commit
|
||||
# b312d8ca3a7c ("dma-buf: make fence sequence numbers 64 bit v2")
|
||||
# in v5.1, but the field to explicitly declare support for it
|
||||
# didn't get added until commit 5e498abf1485 ("dma-buf:
|
||||
# explicitely note that dma-fence-chains use 64bit seqno") in
|
||||
# v5.2. Since it is currently trivial to work around the lack of
|
||||
# native 64-bit seqno in our driver, we'll use the work-around path
|
||||
# for kernels prior to v5.2 to avoid further ifdefing of the code.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_LINUX_DMA_FENCE_H_PRESENT)
|
||||
#include <linux/dma-fence.h>
|
||||
#endif
|
||||
int conftest_fence_ops(void)
|
||||
{
|
||||
return offsetof(struct dma_fence_ops, use_64bit_seqno);
|
||||
}"
|
||||
|
||||
compile_check_conftest "$CODE" "NV_DMA_FENCE_OPS_HAS_USE_64BIT_SEQNO" "" "types"
|
||||
;;
|
||||
|
||||
drm_fbdev_generic_setup)
|
||||
#
|
||||
# Determine whether drm_fbdev_generic_setup is present.
|
||||
#
|
||||
# Added by commit 9060d7f49376 ("drm/fb-helper: Finish the
|
||||
# generic fbdev emulation") in v4.19.
|
||||
#
|
||||
CODE="
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#if defined(NV_DRM_DRM_FBDEV_GENERIC_H_PRESENT)
|
||||
#include <drm/drm_fbdev_generic.h>
|
||||
#endif
|
||||
void conftest_drm_fbdev_generic_setup(void) {
|
||||
drm_fbdev_generic_setup();
|
||||
}"
|
||||
|
||||
compile_check_conftest "$CODE" "NV_DRM_FBDEV_GENERIC_SETUP_PRESENT" "" "functions"
|
||||
;;
|
||||
|
||||
drm_aperture_remove_conflicting_pci_framebuffers)
|
||||
#
|
||||
# Determine whether drm_aperture_remove_conflicting_pci_framebuffers is present.
|
||||
#
|
||||
# Added by commit 2916059147ea ("drm/aperture: Add infrastructure
|
||||
# for aperture ownership") in v5.14.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_DRM_DRM_APERTURE_H_PRESENT)
|
||||
#include <drm/drm_aperture.h>
|
||||
#endif
|
||||
void conftest_drm_aperture_remove_conflicting_pci_framebuffers(void) {
|
||||
drm_aperture_remove_conflicting_pci_framebuffers();
|
||||
}"
|
||||
|
||||
compile_check_conftest "$CODE" "NV_DRM_APERTURE_REMOVE_CONFLICTING_PCI_FRAMEBUFFERS_PRESENT" "" "functions"
|
||||
;;
|
||||
|
||||
drm_aperture_remove_conflicting_pci_framebuffers_has_driver_arg)
|
||||
#
|
||||
# Determine whether drm_aperture_remove_conflicting_pci_framebuffers
|
||||
# takes a struct drm_driver * as its second argument.
|
||||
#
|
||||
# Prior to commit 97c9bfe3f6605d41eb8f1206e6e0f62b31ba15d6, the
|
||||
# second argument was a char * pointer to the driver's name.
|
||||
#
|
||||
# To test if drm_aperture_remove_conflicting_pci_framebuffers() has
|
||||
# a req_driver argument, define a function with the expected
|
||||
# signature and then define the corresponding function
|
||||
# implementation with the expected signature. Successful compilation
|
||||
# indicates that this function has the expected signature.
|
||||
#
|
||||
# This change occurred in commit 97c9bfe3f660 ("drm/aperture: Pass
|
||||
# DRM driver structure instead of driver name") in v5.15
|
||||
# (2021-06-29).
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_DRM_DRM_DRV_H_PRESENT)
|
||||
#include <drm/drm_drv.h>
|
||||
#endif
|
||||
#if defined(NV_DRM_DRM_APERTURE_H_PRESENT)
|
||||
#include <drm/drm_aperture.h>
|
||||
#endif
|
||||
typeof(drm_aperture_remove_conflicting_pci_framebuffers) conftest_drm_aperture_remove_conflicting_pci_framebuffers;
|
||||
int conftest_drm_aperture_remove_conflicting_pci_framebuffers(struct pci_dev *pdev,
|
||||
const struct drm_driver *req_driver)
|
||||
{
|
||||
return 0;
|
||||
}"
|
||||
|
||||
compile_check_conftest "$CODE" "NV_DRM_APERTURE_REMOVE_CONFLICTING_PCI_FRAMEBUFFERS_HAS_DRIVER_ARG" "" "types"
|
||||
;;
|
||||
|
||||
find_next_bit_wrap)
|
||||
# Determine if 'find_next_bit_wrap' is defined.
|
||||
#
|
||||
# The function was added by commit 6cc18331a987 ("lib/find_bit:
|
||||
# add find_next{,_and}_bit_wrap") in v6.1-rc1 (2022-09-19).
|
||||
#
|
||||
# Ideally, we would want to be able to include linux/find.h.
|
||||
# However, linux/find.h does not allow direct inclusion. Rather
|
||||
# it has to be included through linux/bitmap.h.
|
||||
#
|
||||
CODE="
|
||||
#include <linux/bitmap.h>
|
||||
void conftest_find_next_bit_wrap(void) {
|
||||
(void)find_next_bit_wrap();
|
||||
}"
|
||||
|
||||
compile_check_conftest "$CODE" "NV_FIND_NEXT_BIT_WRAP_PRESENT" "" "functions"
|
||||
;;
|
||||
|
||||
crypto)
|
||||
#
|
||||
# Determine if we support various crypto functions.
|
||||
@ -6342,6 +6561,29 @@ compile_test() {
|
||||
compile_check_conftest "$CODE" "NV_MPOL_PREFERRED_MANY_PRESENT" "" "types"
|
||||
;;
|
||||
|
||||
drm_connector_attach_hdr_output_metadata_property)
|
||||
#
|
||||
# Determine if the function
|
||||
# drm_connector_attach_hdr_output_metadata_property() is present.
|
||||
#
|
||||
# Added by commit e057b52c1d90 ("drm/connector: Create a helper to
|
||||
# attach the hdr_output_metadata property") in v5.14.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_DRM_DRM_CRTC_H_PRESENT)
|
||||
#include <drm/drm_crtc.h>
|
||||
#endif
|
||||
#if defined(NV_DRM_DRM_CONNECTOR_H_PRESENT)
|
||||
#include <drm/drm_connector.h>
|
||||
#endif
|
||||
|
||||
void conftest_drm_connector_attach_hdr_output_metadata_property(void) {
|
||||
drm_connector_attach_hdr_output_metadata_property();
|
||||
}"
|
||||
|
||||
compile_check_conftest "$CODE" "NV_DRM_CONNECTOR_ATTACH_HDR_OUTPUT_METADATA_PROPERTY_PRESENT" "" "functions"
|
||||
;;
|
||||
|
||||
mmu_interval_notifier)
|
||||
#
|
||||
# Determine if mmu_interval_notifier struct is present or not
|
||||
@ -6357,11 +6599,48 @@ compile_test() {
|
||||
compile_check_conftest "$CODE" "NV_MMU_INTERVAL_NOTIFIER" "" "types"
|
||||
;;
|
||||
|
||||
drm_mode_create_dp_colorspace_property_has_supported_colorspaces_arg)
|
||||
# Determine if drm_mode_create_dp_colorspace_property() takes the
|
||||
# 'supported_colorspaces' argument.
|
||||
#
|
||||
# The 'u32 supported_colorspaces' argument was added to
|
||||
# drm_mode_create_dp_colorspace_property() by linux-next commit
|
||||
# c265f340eaa8 ("drm/connector: Allow drivers to pass list of
|
||||
# supported colorspaces").
|
||||
#
|
||||
# To test if drm_mode_create_dp_colorspace_property() has the
|
||||
# 'supported_colorspaces' argument, declare a function prototype
|
||||
# with typeof drm_mode_create_dp_colorspace_property and then
|
||||
# define the corresponding function implementation with the
|
||||
# expected signature. Successful compilation indicates that
|
||||
# drm_mode_create_dp_colorspace_property() has the
|
||||
# 'supported_colorspaces' argument.
|
||||
#
|
||||
CODE="
|
||||
#if defined(NV_DRM_DRM_CRTC_H_PRESENT)
|
||||
#include <drm/drm_crtc.h>
|
||||
#endif
|
||||
#if defined(NV_DRM_DRM_CONNECTOR_H_PRESENT)
|
||||
#include <drm/drm_connector.h>
|
||||
#endif
|
||||
|
||||
typeof(drm_mode_create_dp_colorspace_property) conftest_drm_mode_create_dp_colorspace_property_has_supported_colorspaces_arg;
|
||||
int conftest_drm_mode_create_dp_colorspace_property_has_supported_colorspaces_arg(struct drm_connector *connector,
|
||||
u32 supported_colorspaces)
|
||||
{
|
||||
return 0;
|
||||
}"
|
||||
|
||||
compile_check_conftest "$CODE" "NV_DRM_MODE_CREATE_DP_COLORSPACE_PROPERTY_HAS_SUPPORTED_COLORSPACES_ARG" "" "types"
|
||||
;;
|
||||
|
||||
# When adding a new conftest entry, please use the correct format for
|
||||
# specifying the relevant upstream Linux kernel commit.
|
||||
# specifying the relevant upstream Linux kernel commit. Please
|
||||
# avoid specifying -rc kernels, and only use SHAs that actually exist
|
||||
# in the upstream Linux kernel git repository.
|
||||
#
|
||||
# <function> was added|removed|etc by commit <sha> ("<commit message")
|
||||
# in <kernel-version> (<commit date>).
|
||||
# Added|Removed|etc by commit <short-sha> ("<commit message") in
|
||||
# <kernel-version>.
|
||||
|
||||
*)
|
||||
# Unknown test name given
|
||||
|
334
kernel-open/nvidia-drm/nv-kthread-q.c
Normal file
334
kernel-open/nvidia-drm/nv-kthread-q.c
Normal file
@ -0,0 +1,334 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2016 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "nv-kthread-q.h"
|
||||
#include "nv-list-helpers.h"
|
||||
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
#if defined(NV_LINUX_BUG_H_PRESENT)
|
||||
#include <linux/bug.h>
|
||||
#else
|
||||
#include <asm/bug.h>
|
||||
#endif
|
||||
|
||||
// Today's implementation is a little simpler and more limited than the
|
||||
// API description allows for in nv-kthread-q.h. Details include:
|
||||
//
|
||||
// 1. Each nv_kthread_q instance is a first-in, first-out queue.
|
||||
//
|
||||
// 2. Each nv_kthread_q instance is serviced by exactly one kthread.
|
||||
//
|
||||
// You can create any number of queues, each of which gets its own
|
||||
// named kernel thread (kthread). You can then insert arbitrary functions
|
||||
// into the queue, and those functions will be run in the context of the
|
||||
// queue's kthread.
|
||||
|
||||
#ifndef WARN
|
||||
// Only *really* old kernels (2.6.9) end up here. Just use a simple printk
|
||||
// to implement this, because such kernels won't be supported much longer.
|
||||
#define WARN(condition, format...) ({ \
|
||||
int __ret_warn_on = !!(condition); \
|
||||
if (unlikely(__ret_warn_on)) \
|
||||
printk(KERN_ERR format); \
|
||||
unlikely(__ret_warn_on); \
|
||||
})
|
||||
#endif
|
||||
|
||||
#define NVQ_WARN(fmt, ...) \
|
||||
do { \
|
||||
if (in_interrupt()) { \
|
||||
WARN(1, "nv_kthread_q: [in interrupt]: " fmt, \
|
||||
##__VA_ARGS__); \
|
||||
} \
|
||||
else { \
|
||||
WARN(1, "nv_kthread_q: task: %s: " fmt, \
|
||||
current->comm, \
|
||||
##__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static int _main_loop(void *args)
|
||||
{
|
||||
nv_kthread_q_t *q = (nv_kthread_q_t *)args;
|
||||
nv_kthread_q_item_t *q_item = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
while (1) {
|
||||
// Normally this thread is never interrupted. However,
|
||||
// down_interruptible (instead of down) is called here,
|
||||
// in order to avoid being classified as a potentially
|
||||
// hung task, by the kernel watchdog.
|
||||
while (down_interruptible(&q->q_sem))
|
||||
NVQ_WARN("Interrupted during semaphore wait\n");
|
||||
|
||||
if (atomic_read(&q->main_loop_should_exit))
|
||||
break;
|
||||
|
||||
spin_lock_irqsave(&q->q_lock, flags);
|
||||
|
||||
// The q_sem semaphore prevents us from getting here unless there is
|
||||
// at least one item in the list, so an empty list indicates a bug.
|
||||
if (unlikely(list_empty(&q->q_list_head))) {
|
||||
spin_unlock_irqrestore(&q->q_lock, flags);
|
||||
NVQ_WARN("_main_loop: Empty queue: q: 0x%p\n", q);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Consume one item from the queue
|
||||
q_item = list_first_entry(&q->q_list_head,
|
||||
nv_kthread_q_item_t,
|
||||
q_list_node);
|
||||
|
||||
list_del_init(&q_item->q_list_node);
|
||||
|
||||
spin_unlock_irqrestore(&q->q_lock, flags);
|
||||
|
||||
// Run the item
|
||||
q_item->function_to_run(q_item->function_args);
|
||||
|
||||
// Make debugging a little simpler by clearing this between runs:
|
||||
q_item = NULL;
|
||||
}
|
||||
|
||||
while (!kthread_should_stop())
|
||||
schedule();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nv_kthread_q_stop(nv_kthread_q_t *q)
|
||||
{
|
||||
// check if queue has been properly initialized
|
||||
if (unlikely(!q->q_kthread))
|
||||
return;
|
||||
|
||||
nv_kthread_q_flush(q);
|
||||
|
||||
// If this assertion fires, then a caller likely either broke the API rules,
|
||||
// by adding items after calling nv_kthread_q_stop, or possibly messed up
|
||||
// with inadequate flushing of self-rescheduling q_items.
|
||||
if (unlikely(!list_empty(&q->q_list_head)))
|
||||
NVQ_WARN("list not empty after flushing\n");
|
||||
|
||||
if (likely(!atomic_read(&q->main_loop_should_exit))) {
|
||||
|
||||
atomic_set(&q->main_loop_should_exit, 1);
|
||||
|
||||
// Wake up the kthread so that it can see that it needs to stop:
|
||||
up(&q->q_sem);
|
||||
|
||||
kthread_stop(q->q_kthread);
|
||||
q->q_kthread = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// When CONFIG_VMAP_STACK is defined, the kernel thread stack allocator used by
|
||||
// kthread_create_on_node relies on a 2 entry, per-core cache to minimize
|
||||
// vmalloc invocations. The cache is NUMA-unaware, so when there is a hit, the
|
||||
// stack location ends up being a function of the core assigned to the current
|
||||
// thread, instead of being a function of the specified NUMA node. The cache was
|
||||
// added to the kernel in commit ac496bf48d97f2503eaa353996a4dd5e4383eaf0
|
||||
// ("fork: Optimize task creation by caching two thread stacks per CPU if
|
||||
// CONFIG_VMAP_STACK=y")
|
||||
//
|
||||
// To work around the problematic cache, we create up to three kernel threads
|
||||
// -If the first thread's stack is resident on the preferred node, return this
|
||||
// thread.
|
||||
// -Otherwise, create a second thread. If its stack is resident on the
|
||||
// preferred node, stop the first thread and return this one.
|
||||
// -Otherwise, create a third thread. The stack allocator does not find a
|
||||
// cached stack, and so falls back to vmalloc, which takes the NUMA hint into
|
||||
// consideration. The first two threads are then stopped.
|
||||
//
|
||||
// When CONFIG_VMAP_STACK is not defined, the first kernel thread is returned.
|
||||
//
|
||||
// This function is never invoked when there is no NUMA preference (preferred
|
||||
// node is NUMA_NO_NODE).
|
||||
static struct task_struct *thread_create_on_node(int (*threadfn)(void *data),
|
||||
nv_kthread_q_t *q,
|
||||
int preferred_node,
|
||||
const char *q_name)
|
||||
{
|
||||
|
||||
unsigned i, j;
|
||||
const static unsigned attempts = 3;
|
||||
struct task_struct *thread[3];
|
||||
|
||||
for (i = 0;; i++) {
|
||||
struct page *stack;
|
||||
|
||||
thread[i] = kthread_create_on_node(threadfn, q, preferred_node, q_name);
|
||||
|
||||
if (unlikely(IS_ERR(thread[i]))) {
|
||||
|
||||
// Instead of failing, pick the previous thread, even if its
|
||||
// stack is not allocated on the preferred node.
|
||||
if (i > 0)
|
||||
i--;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// vmalloc is not used to allocate the stack, so simply return the
|
||||
// thread, even if its stack may not be allocated on the preferred node
|
||||
if (!is_vmalloc_addr(thread[i]->stack))
|
||||
break;
|
||||
|
||||
// Ran out of attempts - return thread even if its stack may not be
|
||||
// allocated on the preferred node
|
||||
if ((i == (attempts - 1)))
|
||||
break;
|
||||
|
||||
// Get the NUMA node where the first page of the stack is resident. If
|
||||
// it is the preferred node, select this thread.
|
||||
stack = vmalloc_to_page(thread[i]->stack);
|
||||
if (page_to_nid(stack) == preferred_node)
|
||||
break;
|
||||
}
|
||||
|
||||
for (j = i; j > 0; j--)
|
||||
kthread_stop(thread[j - 1]);
|
||||
|
||||
return thread[i];
|
||||
}
|
||||
|
||||
int nv_kthread_q_init_on_node(nv_kthread_q_t *q, const char *q_name, int preferred_node)
|
||||
{
|
||||
memset(q, 0, sizeof(*q));
|
||||
|
||||
INIT_LIST_HEAD(&q->q_list_head);
|
||||
spin_lock_init(&q->q_lock);
|
||||
sema_init(&q->q_sem, 0);
|
||||
|
||||
if (preferred_node == NV_KTHREAD_NO_NODE) {
|
||||
q->q_kthread = kthread_create(_main_loop, q, q_name);
|
||||
}
|
||||
else {
|
||||
q->q_kthread = thread_create_on_node(_main_loop, q, preferred_node, q_name);
|
||||
}
|
||||
|
||||
if (IS_ERR(q->q_kthread)) {
|
||||
int err = PTR_ERR(q->q_kthread);
|
||||
|
||||
// Clear q_kthread before returning so that nv_kthread_q_stop() can be
|
||||
// safely called on it making error handling easier.
|
||||
q->q_kthread = NULL;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
wake_up_process(q->q_kthread);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nv_kthread_q_init(nv_kthread_q_t *q, const char *qname)
|
||||
{
|
||||
return nv_kthread_q_init_on_node(q, qname, NV_KTHREAD_NO_NODE);
|
||||
}
|
||||
|
||||
// Returns true (non-zero) if the item was actually scheduled, and false if the
|
||||
// item was already pending in a queue.
|
||||
static int _raw_q_schedule(nv_kthread_q_t *q, nv_kthread_q_item_t *q_item)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret = 1;
|
||||
|
||||
spin_lock_irqsave(&q->q_lock, flags);
|
||||
|
||||
if (likely(list_empty(&q_item->q_list_node)))
|
||||
list_add_tail(&q_item->q_list_node, &q->q_list_head);
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
spin_unlock_irqrestore(&q->q_lock, flags);
|
||||
|
||||
if (likely(ret))
|
||||
up(&q->q_sem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void nv_kthread_q_item_init(nv_kthread_q_item_t *q_item,
|
||||
nv_q_func_t function_to_run,
|
||||
void *function_args)
|
||||
{
|
||||
INIT_LIST_HEAD(&q_item->q_list_node);
|
||||
q_item->function_to_run = function_to_run;
|
||||
q_item->function_args = function_args;
|
||||
}
|
||||
|
||||
// Returns true (non-zero) if the q_item got scheduled, false otherwise.
|
||||
int nv_kthread_q_schedule_q_item(nv_kthread_q_t *q,
|
||||
nv_kthread_q_item_t *q_item)
|
||||
{
|
||||
if (unlikely(atomic_read(&q->main_loop_should_exit))) {
|
||||
NVQ_WARN("Not allowed: nv_kthread_q_schedule_q_item was "
|
||||
"called with a non-alive q: 0x%p\n", q);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _raw_q_schedule(q, q_item);
|
||||
}
|
||||
|
||||
static void _q_flush_function(void *args)
|
||||
{
|
||||
struct completion *completion = (struct completion *)args;
|
||||
complete(completion);
|
||||
}
|
||||
|
||||
|
||||
static void _raw_q_flush(nv_kthread_q_t *q)
|
||||
{
|
||||
nv_kthread_q_item_t q_item;
|
||||
DECLARE_COMPLETION_ONSTACK(completion);
|
||||
|
||||
nv_kthread_q_item_init(&q_item, _q_flush_function, &completion);
|
||||
|
||||
_raw_q_schedule(q, &q_item);
|
||||
|
||||
// Wait for the flush item to run. Once it has run, then all of the
|
||||
// previously queued items in front of it will have run, so that means
|
||||
// the flush is complete.
|
||||
wait_for_completion(&completion);
|
||||
}
|
||||
|
||||
void nv_kthread_q_flush(nv_kthread_q_t *q)
|
||||
{
|
||||
if (unlikely(atomic_read(&q->main_loop_should_exit))) {
|
||||
NVQ_WARN("Not allowed: nv_kthread_q_flush was called after "
|
||||
"nv_kthread_q_stop. q: 0x%p\n", q);
|
||||
return;
|
||||
}
|
||||
|
||||
// This 2x flush is not a typing mistake. The queue really does have to be
|
||||
// flushed twice, in order to take care of the case of a q_item that
|
||||
// reschedules itself.
|
||||
_raw_q_flush(q);
|
||||
_raw_q_flush(q);
|
||||
}
|
@ -43,9 +43,13 @@
|
||||
#if defined(NV_LINUX_FENCE_H_PRESENT)
|
||||
typedef struct fence nv_dma_fence_t;
|
||||
typedef struct fence_ops nv_dma_fence_ops_t;
|
||||
typedef struct fence_cb nv_dma_fence_cb_t;
|
||||
typedef fence_func_t nv_dma_fence_func_t;
|
||||
#else
|
||||
typedef struct dma_fence nv_dma_fence_t;
|
||||
typedef struct dma_fence_ops nv_dma_fence_ops_t;
|
||||
typedef struct dma_fence_cb nv_dma_fence_cb_t;
|
||||
typedef dma_fence_func_t nv_dma_fence_func_t;
|
||||
#endif
|
||||
|
||||
#if defined(NV_LINUX_FENCE_H_PRESENT)
|
||||
@ -97,6 +101,14 @@ static inline int nv_dma_fence_signal(nv_dma_fence_t *fence) {
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int nv_dma_fence_signal_locked(nv_dma_fence_t *fence) {
|
||||
#if defined(NV_LINUX_FENCE_H_PRESENT)
|
||||
return fence_signal_locked(fence);
|
||||
#else
|
||||
return dma_fence_signal_locked(fence);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline u64 nv_dma_fence_context_alloc(unsigned num) {
|
||||
#if defined(NV_LINUX_FENCE_H_PRESENT)
|
||||
return fence_context_alloc(num);
|
||||
@ -108,7 +120,7 @@ static inline u64 nv_dma_fence_context_alloc(unsigned num) {
|
||||
static inline void
|
||||
nv_dma_fence_init(nv_dma_fence_t *fence,
|
||||
const nv_dma_fence_ops_t *ops,
|
||||
spinlock_t *lock, u64 context, unsigned seqno) {
|
||||
spinlock_t *lock, u64 context, uint64_t seqno) {
|
||||
#if defined(NV_LINUX_FENCE_H_PRESENT)
|
||||
fence_init(fence, ops, lock, context, seqno);
|
||||
#else
|
||||
@ -116,6 +128,29 @@ nv_dma_fence_init(nv_dma_fence_t *fence,
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void
|
||||
nv_dma_fence_set_error(nv_dma_fence_t *fence,
|
||||
int error) {
|
||||
#if defined(NV_DMA_FENCE_SET_ERROR_PRESENT)
|
||||
return dma_fence_set_error(fence, error);
|
||||
#elif defined(NV_FENCE_SET_ERROR_PRESENT)
|
||||
return fence_set_error(fence, error);
|
||||
#else
|
||||
fence->status = error;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int
|
||||
nv_dma_fence_add_callback(nv_dma_fence_t *fence,
|
||||
nv_dma_fence_cb_t *cb,
|
||||
nv_dma_fence_func_t func) {
|
||||
#if defined(NV_LINUX_FENCE_H_PRESENT)
|
||||
return fence_add_callback(fence, cb, func);
|
||||
#else
|
||||
return dma_fence_add_callback(fence, cb, func);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* defined(NV_DRM_FENCE_AVAILABLE) */
|
||||
|
||||
#endif /* __NVIDIA_DMA_FENCE_HELPER_H__ */
|
||||
|
@ -121,6 +121,20 @@ static inline void nv_dma_resv_add_excl_fence(nv_dma_resv_t *obj,
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void nv_dma_resv_add_shared_fence(nv_dma_resv_t *obj,
|
||||
nv_dma_fence_t *fence)
|
||||
{
|
||||
#if defined(NV_LINUX_DMA_RESV_H_PRESENT)
|
||||
#if defined(NV_DMA_RESV_ADD_FENCE_PRESENT)
|
||||
dma_resv_add_fence(obj, fence, DMA_RESV_USAGE_READ);
|
||||
#else
|
||||
dma_resv_add_shared_fence(obj, fence);
|
||||
#endif
|
||||
#else
|
||||
reservation_object_add_shared_fence(obj, fence);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* defined(NV_DRM_FENCE_AVAILABLE) */
|
||||
|
||||
#endif /* __NVIDIA_DMA_RESV_HELPER_H__ */
|
||||
|
@ -61,4 +61,15 @@
|
||||
#undef NV_DRM_FENCE_AVAILABLE
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We can support color management if either drm_helper_crtc_enable_color_mgmt()
|
||||
* or drm_crtc_enable_color_mgmt() exist.
|
||||
*/
|
||||
#if defined(NV_DRM_HELPER_CRTC_ENABLE_COLOR_MGMT_PRESENT) || \
|
||||
defined(NV_DRM_CRTC_ENABLE_COLOR_MGMT_PRESENT)
|
||||
#define NV_DRM_COLOR_MGMT_AVAILABLE
|
||||
#else
|
||||
#undef NV_DRM_COLOR_MGMT_AVAILABLE
|
||||
#endif
|
||||
|
||||
#endif /* defined(__NVIDIA_DRM_CONFTEST_H__) */
|
||||
|
@ -349,10 +349,125 @@ nv_drm_connector_best_encoder(struct drm_connector *connector)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if defined(NV_DRM_MODE_CREATE_DP_COLORSPACE_PROPERTY_HAS_SUPPORTED_COLORSPACES_ARG)
|
||||
static const NvU32 __nv_drm_connector_supported_colorspaces =
|
||||
BIT(DRM_MODE_COLORIMETRY_BT2020_RGB) |
|
||||
BIT(DRM_MODE_COLORIMETRY_BT2020_YCC);
|
||||
#endif
|
||||
|
||||
#if defined(NV_DRM_CONNECTOR_ATTACH_HDR_OUTPUT_METADATA_PROPERTY_PRESENT)
|
||||
static int
|
||||
__nv_drm_connector_atomic_check(struct drm_connector *connector,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_connector_state *new_connector_state =
|
||||
drm_atomic_get_new_connector_state(state, connector);
|
||||
struct drm_connector_state *old_connector_state =
|
||||
drm_atomic_get_old_connector_state(state, connector);
|
||||
struct nv_drm_device *nv_dev = to_nv_device(connector->dev);
|
||||
|
||||
struct drm_crtc *crtc = new_connector_state->crtc;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
struct nv_drm_crtc_state *nv_crtc_state;
|
||||
struct NvKmsKapiHeadRequestedConfig *req_config;
|
||||
|
||||
if (!crtc) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
||||
nv_crtc_state = to_nv_crtc_state(crtc_state);
|
||||
req_config = &nv_crtc_state->req_config;
|
||||
|
||||
/*
|
||||
* Override metadata for the entire head instead of allowing NVKMS to derive
|
||||
* it from the layers' metadata.
|
||||
*
|
||||
* This is the metadata that will sent to the display, and if applicable,
|
||||
* layers will be tone mapped to this metadata rather than that of the
|
||||
* display.
|
||||
*/
|
||||
req_config->flags.hdrInfoFrameChanged =
|
||||
!drm_connector_atomic_hdr_metadata_equal(old_connector_state,
|
||||
new_connector_state);
|
||||
if (new_connector_state->hdr_output_metadata &&
|
||||
new_connector_state->hdr_output_metadata->data) {
|
||||
|
||||
/*
|
||||
* Note that HDMI definitions are used here even though we might not
|
||||
* be using HDMI. While that seems odd, it is consistent with
|
||||
* upstream behavior.
|
||||
*/
|
||||
|
||||
struct hdr_output_metadata *hdr_metadata =
|
||||
new_connector_state->hdr_output_metadata->data;
|
||||
struct hdr_metadata_infoframe *info_frame =
|
||||
&hdr_metadata->hdmi_metadata_type1;
|
||||
unsigned int i;
|
||||
|
||||
if (hdr_metadata->metadata_type != HDMI_STATIC_METADATA_TYPE1) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(info_frame->display_primaries); i++) {
|
||||
req_config->modeSetConfig.hdrInfoFrame.staticMetadata.displayPrimaries[i].x =
|
||||
info_frame->display_primaries[i].x;
|
||||
req_config->modeSetConfig.hdrInfoFrame.staticMetadata.displayPrimaries[i].y =
|
||||
info_frame->display_primaries[i].y;
|
||||
}
|
||||
|
||||
req_config->modeSetConfig.hdrInfoFrame.staticMetadata.whitePoint.x =
|
||||
info_frame->white_point.x;
|
||||
req_config->modeSetConfig.hdrInfoFrame.staticMetadata.whitePoint.y =
|
||||
info_frame->white_point.y;
|
||||
req_config->modeSetConfig.hdrInfoFrame.staticMetadata.maxDisplayMasteringLuminance =
|
||||
info_frame->max_display_mastering_luminance;
|
||||
req_config->modeSetConfig.hdrInfoFrame.staticMetadata.minDisplayMasteringLuminance =
|
||||
info_frame->min_display_mastering_luminance;
|
||||
req_config->modeSetConfig.hdrInfoFrame.staticMetadata.maxCLL =
|
||||
info_frame->max_cll;
|
||||
req_config->modeSetConfig.hdrInfoFrame.staticMetadata.maxFALL =
|
||||
info_frame->max_fall;
|
||||
|
||||
req_config->modeSetConfig.hdrInfoFrame.eotf = info_frame->eotf;
|
||||
|
||||
req_config->modeSetConfig.hdrInfoFrame.enabled = NV_TRUE;
|
||||
} else {
|
||||
req_config->modeSetConfig.hdrInfoFrame.enabled = NV_FALSE;
|
||||
}
|
||||
|
||||
req_config->flags.colorimetryChanged =
|
||||
(old_connector_state->colorspace != new_connector_state->colorspace);
|
||||
// When adding a case here, also add to __nv_drm_connector_supported_colorspaces
|
||||
switch (new_connector_state->colorspace) {
|
||||
case DRM_MODE_COLORIMETRY_DEFAULT:
|
||||
req_config->modeSetConfig.colorimetry =
|
||||
NVKMS_OUTPUT_COLORIMETRY_DEFAULT;
|
||||
break;
|
||||
case DRM_MODE_COLORIMETRY_BT2020_RGB:
|
||||
case DRM_MODE_COLORIMETRY_BT2020_YCC:
|
||||
// Ignore RGB/YCC
|
||||
// See https://patchwork.freedesktop.org/patch/525496/?series=111865&rev=4
|
||||
req_config->modeSetConfig.colorimetry =
|
||||
NVKMS_OUTPUT_COLORIMETRY_BT2100;
|
||||
break;
|
||||
default:
|
||||
// XXX HDR TODO: Add support for more color spaces
|
||||
NV_DRM_DEV_LOG_ERR(nv_dev, "Unsupported color space");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* defined(NV_DRM_CONNECTOR_ATTACH_HDR_OUTPUT_METADATA_PROPERTY_PRESENT) */
|
||||
|
||||
static const struct drm_connector_helper_funcs nv_connector_helper_funcs = {
|
||||
.get_modes = nv_drm_connector_get_modes,
|
||||
.mode_valid = nv_drm_connector_mode_valid,
|
||||
.best_encoder = nv_drm_connector_best_encoder,
|
||||
#if defined(NV_DRM_CONNECTOR_ATTACH_HDR_OUTPUT_METADATA_PROPERTY_PRESENT)
|
||||
.atomic_check = __nv_drm_connector_atomic_check,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct drm_connector*
|
||||
@ -405,6 +520,32 @@ nv_drm_connector_new(struct drm_device *dev,
|
||||
DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
|
||||
}
|
||||
|
||||
#if defined(NV_DRM_CONNECTOR_ATTACH_HDR_OUTPUT_METADATA_PROPERTY_PRESENT)
|
||||
if (nv_connector->type == NVKMS_CONNECTOR_TYPE_HDMI) {
|
||||
#if defined(NV_DRM_MODE_CREATE_DP_COLORSPACE_PROPERTY_HAS_SUPPORTED_COLORSPACES_ARG)
|
||||
if (drm_mode_create_hdmi_colorspace_property(
|
||||
&nv_connector->base,
|
||||
__nv_drm_connector_supported_colorspaces) == 0) {
|
||||
#else
|
||||
if (drm_mode_create_hdmi_colorspace_property(&nv_connector->base) == 0) {
|
||||
#endif
|
||||
drm_connector_attach_colorspace_property(&nv_connector->base);
|
||||
}
|
||||
drm_connector_attach_hdr_output_metadata_property(&nv_connector->base);
|
||||
} else if (nv_connector->type == NVKMS_CONNECTOR_TYPE_DP) {
|
||||
#if defined(NV_DRM_MODE_CREATE_DP_COLORSPACE_PROPERTY_HAS_SUPPORTED_COLORSPACES_ARG)
|
||||
if (drm_mode_create_dp_colorspace_property(
|
||||
&nv_connector->base,
|
||||
__nv_drm_connector_supported_colorspaces) == 0) {
|
||||
#else
|
||||
if (drm_mode_create_dp_colorspace_property(&nv_connector->base) == 0) {
|
||||
#endif
|
||||
drm_connector_attach_colorspace_property(&nv_connector->base);
|
||||
}
|
||||
drm_connector_attach_hdr_output_metadata_property(&nv_connector->base);
|
||||
}
|
||||
#endif /* defined(NV_DRM_CONNECTOR_ATTACH_HDR_OUTPUT_METADATA_PROPERTY_PRESENT) */
|
||||
|
||||
/* Register connector with DRM subsystem */
|
||||
|
||||
ret = drm_connector_register(&nv_connector->base);
|
||||
|
@ -48,6 +48,11 @@
|
||||
#include <linux/host1x-next.h>
|
||||
#endif
|
||||
|
||||
#if defined(NV_DRM_DRM_COLOR_MGMT_H_PRESENT)
|
||||
#include <drm/drm_color_mgmt.h>
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(NV_DRM_HAS_HDR_OUTPUT_METADATA)
|
||||
static int
|
||||
nv_drm_atomic_replace_property_blob_from_id(struct drm_device *dev,
|
||||
@ -399,27 +404,25 @@ plane_req_config_update(struct drm_plane *plane,
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(info_frame->display_primaries); i ++) {
|
||||
req_config->config.hdrMetadata.displayPrimaries[i].x =
|
||||
req_config->config.hdrMetadata.val.displayPrimaries[i].x =
|
||||
info_frame->display_primaries[i].x;
|
||||
req_config->config.hdrMetadata.displayPrimaries[i].y =
|
||||
req_config->config.hdrMetadata.val.displayPrimaries[i].y =
|
||||
info_frame->display_primaries[i].y;
|
||||
}
|
||||
|
||||
req_config->config.hdrMetadata.whitePoint.x =
|
||||
req_config->config.hdrMetadata.val.whitePoint.x =
|
||||
info_frame->white_point.x;
|
||||
req_config->config.hdrMetadata.whitePoint.y =
|
||||
req_config->config.hdrMetadata.val.whitePoint.y =
|
||||
info_frame->white_point.y;
|
||||
req_config->config.hdrMetadata.maxDisplayMasteringLuminance =
|
||||
req_config->config.hdrMetadata.val.maxDisplayMasteringLuminance =
|
||||
info_frame->max_display_mastering_luminance;
|
||||
req_config->config.hdrMetadata.minDisplayMasteringLuminance =
|
||||
req_config->config.hdrMetadata.val.minDisplayMasteringLuminance =
|
||||
info_frame->min_display_mastering_luminance;
|
||||
req_config->config.hdrMetadata.maxCLL =
|
||||
req_config->config.hdrMetadata.val.maxCLL =
|
||||
info_frame->max_cll;
|
||||
req_config->config.hdrMetadata.maxFALL =
|
||||
req_config->config.hdrMetadata.val.maxFALL =
|
||||
info_frame->max_fall;
|
||||
|
||||
req_config->config.hdrMetadataSpecified = true;
|
||||
|
||||
switch (info_frame->eotf) {
|
||||
case HDMI_EOTF_SMPTE_ST2084:
|
||||
req_config->config.tf = NVKMS_OUTPUT_TF_PQ;
|
||||
@ -432,10 +435,21 @@ plane_req_config_update(struct drm_plane *plane,
|
||||
NV_DRM_DEV_LOG_ERR(nv_dev, "Unsupported EOTF");
|
||||
return -1;
|
||||
}
|
||||
|
||||
req_config->config.hdrMetadata.enabled = true;
|
||||
} else {
|
||||
req_config->config.hdrMetadataSpecified = false;
|
||||
req_config->config.hdrMetadata.enabled = false;
|
||||
req_config->config.tf = NVKMS_OUTPUT_TF_NONE;
|
||||
}
|
||||
|
||||
req_config->flags.hdrMetadataChanged =
|
||||
((old_config.hdrMetadata.enabled !=
|
||||
req_config->config.hdrMetadata.enabled) ||
|
||||
memcmp(&old_config.hdrMetadata.val,
|
||||
&req_config->config.hdrMetadata.val,
|
||||
sizeof(struct NvKmsHDRStaticMetadata)));
|
||||
|
||||
req_config->flags.tfChanged = (old_config.tf != req_config->config.tf);
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -692,9 +706,11 @@ static inline void __nv_drm_plane_atomic_destroy_state(
|
||||
#endif
|
||||
|
||||
#if defined(NV_DRM_HAS_HDR_OUTPUT_METADATA)
|
||||
struct nv_drm_plane_state *nv_drm_plane_state =
|
||||
to_nv_drm_plane_state(state);
|
||||
drm_property_blob_put(nv_drm_plane_state->hdr_output_metadata);
|
||||
{
|
||||
struct nv_drm_plane_state *nv_drm_plane_state =
|
||||
to_nv_drm_plane_state(state);
|
||||
drm_property_blob_put(nv_drm_plane_state->hdr_output_metadata);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -800,6 +816,9 @@ nv_drm_atomic_crtc_duplicate_state(struct drm_crtc *crtc)
|
||||
&(to_nv_crtc_state(crtc->state)->req_config),
|
||||
&nv_state->req_config);
|
||||
|
||||
nv_state->ilut_ramps = NULL;
|
||||
nv_state->olut_ramps = NULL;
|
||||
|
||||
return &nv_state->base;
|
||||
}
|
||||
|
||||
@ -823,6 +842,9 @@ static void nv_drm_atomic_crtc_destroy_state(struct drm_crtc *crtc,
|
||||
|
||||
__nv_drm_atomic_helper_crtc_destroy_state(crtc, &nv_state->base);
|
||||
|
||||
nv_drm_free(nv_state->ilut_ramps);
|
||||
nv_drm_free(nv_state->olut_ramps);
|
||||
|
||||
nv_drm_free(nv_state);
|
||||
}
|
||||
|
||||
@ -833,6 +855,9 @@ static struct drm_crtc_funcs nv_crtc_funcs = {
|
||||
.destroy = nv_drm_crtc_destroy,
|
||||
.atomic_duplicate_state = nv_drm_atomic_crtc_duplicate_state,
|
||||
.atomic_destroy_state = nv_drm_atomic_crtc_destroy_state,
|
||||
#if defined(NV_DRM_ATOMIC_HELPER_LEGACY_GAMMA_SET_PRESENT)
|
||||
.gamma_set = drm_atomic_helper_legacy_gamma_set,
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
@ -866,6 +891,198 @@ static int head_modeset_config_attach_connector(
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(NV_DRM_COLOR_MGMT_AVAILABLE)
|
||||
static int color_mgmt_config_copy_lut(struct NvKmsLutRamps *nvkms_lut,
|
||||
struct drm_color_lut *drm_lut,
|
||||
uint64_t lut_len)
|
||||
{
|
||||
uint64_t i = 0;
|
||||
if (lut_len != NVKMS_LUT_ARRAY_SIZE) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Both NvKms and drm LUT values are 16-bit linear values. NvKms LUT ramps
|
||||
* are in arrays in a single struct while drm LUT ramps are an array of
|
||||
* structs.
|
||||
*/
|
||||
for (i = 0; i < lut_len; i++) {
|
||||
nvkms_lut->red[i] = drm_lut[i].red;
|
||||
nvkms_lut->green[i] = drm_lut[i].green;
|
||||
nvkms_lut->blue[i] = drm_lut[i].blue;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void color_mgmt_config_ctm_to_csc(struct NvKmsCscMatrix *nvkms_csc,
|
||||
struct drm_color_ctm *drm_ctm)
|
||||
{
|
||||
int y;
|
||||
|
||||
/* CTM is a 3x3 matrix while ours is 3x4. Zero out the last column. */
|
||||
nvkms_csc->m[0][3] = nvkms_csc->m[1][3] = nvkms_csc->m[2][3] = 0;
|
||||
|
||||
for (y = 0; y < 3; y++) {
|
||||
int x;
|
||||
|
||||
for (x = 0; x < 3; x++) {
|
||||
/*
|
||||
* Values in the CTM are encoded in S31.32 sign-magnitude fixed-
|
||||
* point format, while NvKms CSC values are signed 2's-complement
|
||||
* S15.16 (Ssign-extend12-3.16?) fixed-point format.
|
||||
*/
|
||||
NvU64 ctmVal = drm_ctm->matrix[y*3 + x];
|
||||
NvU64 signBit = ctmVal & (1ULL << 63);
|
||||
NvU64 magnitude = ctmVal & ~signBit;
|
||||
|
||||
/*
|
||||
* Drop the low 16 bits of the fractional part and the high 17 bits
|
||||
* of the integral part. Drop 17 bits to avoid corner cases where
|
||||
* the highest resulting bit is a 1, causing the `cscVal = -cscVal`
|
||||
* line to result in a positive number.
|
||||
*/
|
||||
NvS32 cscVal = (magnitude >> 16) & ((1ULL << 31) - 1);
|
||||
if (signBit) {
|
||||
cscVal = -cscVal;
|
||||
}
|
||||
|
||||
nvkms_csc->m[y][x] = cscVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int color_mgmt_config_set(struct nv_drm_crtc_state *nv_crtc_state,
|
||||
struct NvKmsKapiHeadRequestedConfig *req_config)
|
||||
{
|
||||
struct NvKmsKapiHeadModeSetConfig *modeset_config =
|
||||
&req_config->modeSetConfig;
|
||||
struct drm_crtc_state *crtc_state = &nv_crtc_state->base;
|
||||
int ret = 0;
|
||||
|
||||
struct drm_color_lut *degamma_lut = NULL;
|
||||
struct drm_color_ctm *ctm = NULL;
|
||||
struct drm_color_lut *gamma_lut = NULL;
|
||||
uint64_t degamma_len = 0;
|
||||
uint64_t gamma_len = 0;
|
||||
|
||||
int i;
|
||||
struct drm_plane *plane;
|
||||
struct drm_plane_state *plane_state;
|
||||
|
||||
/*
|
||||
* According to the comment in the Linux kernel's
|
||||
* drivers/gpu/drm/drm_color_mgmt.c, if any of these properties are NULL,
|
||||
* that LUT or CTM needs to be changed to a linear LUT or identity matrix
|
||||
* respectively.
|
||||
*/
|
||||
|
||||
req_config->flags.lutChanged = NV_TRUE;
|
||||
if (crtc_state->degamma_lut) {
|
||||
nv_crtc_state->ilut_ramps = nv_drm_calloc(1, sizeof(*nv_crtc_state->ilut_ramps));
|
||||
if (!nv_crtc_state->ilut_ramps) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
degamma_lut = (struct drm_color_lut *)crtc_state->degamma_lut->data;
|
||||
degamma_len = crtc_state->degamma_lut->length /
|
||||
sizeof(struct drm_color_lut);
|
||||
|
||||
if ((ret = color_mgmt_config_copy_lut(nv_crtc_state->ilut_ramps,
|
||||
degamma_lut,
|
||||
degamma_len)) != 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
modeset_config->lut.input.specified = NV_TRUE;
|
||||
modeset_config->lut.input.depth = 30; /* specify the full LUT */
|
||||
modeset_config->lut.input.start = 0;
|
||||
modeset_config->lut.input.end = degamma_len - 1;
|
||||
modeset_config->lut.input.pRamps = nv_crtc_state->ilut_ramps;
|
||||
} else {
|
||||
/* setting input.end to 0 is equivalent to disabling the LUT, which
|
||||
* should be equivalent to a linear LUT */
|
||||
modeset_config->lut.input.specified = NV_TRUE;
|
||||
modeset_config->lut.input.depth = 30; /* specify the full LUT */
|
||||
modeset_config->lut.input.start = 0;
|
||||
modeset_config->lut.input.end = 0;
|
||||
modeset_config->lut.input.pRamps = NULL;
|
||||
}
|
||||
|
||||
nv_drm_for_each_new_plane_in_state(crtc_state->state, plane,
|
||||
plane_state, i) {
|
||||
struct nv_drm_plane *nv_plane = to_nv_plane(plane);
|
||||
uint32_t layer = nv_plane->layer_idx;
|
||||
struct NvKmsKapiLayerRequestedConfig *layer_config;
|
||||
|
||||
if (layer == NVKMS_KAPI_LAYER_INVALID_IDX || plane_state->crtc != crtc_state->crtc) {
|
||||
continue;
|
||||
}
|
||||
layer_config = &req_config->layerRequestedConfig[layer];
|
||||
|
||||
if (layer == NVKMS_KAPI_LAYER_PRIMARY_IDX && crtc_state->ctm) {
|
||||
ctm = (struct drm_color_ctm *)crtc_state->ctm->data;
|
||||
|
||||
color_mgmt_config_ctm_to_csc(&layer_config->config.csc, ctm);
|
||||
layer_config->config.cscUseMain = NV_FALSE;
|
||||
} else {
|
||||
/* When crtc_state->ctm is unset, this also sets the main layer to
|
||||
* the identity matrix.
|
||||
*/
|
||||
layer_config->config.csc = NVKMS_IDENTITY_CSC_MATRIX;
|
||||
}
|
||||
layer_config->flags.cscChanged = NV_TRUE;
|
||||
}
|
||||
|
||||
if (crtc_state->gamma_lut) {
|
||||
nv_crtc_state->olut_ramps = nv_drm_calloc(1, sizeof(*nv_crtc_state->olut_ramps));
|
||||
if (!nv_crtc_state->olut_ramps) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
gamma_lut = (struct drm_color_lut *)crtc_state->gamma_lut->data;
|
||||
gamma_len = crtc_state->gamma_lut->length /
|
||||
sizeof(struct drm_color_lut);
|
||||
|
||||
if ((ret = color_mgmt_config_copy_lut(nv_crtc_state->olut_ramps,
|
||||
gamma_lut,
|
||||
gamma_len)) != 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
modeset_config->lut.output.specified = NV_TRUE;
|
||||
modeset_config->lut.output.enabled = NV_TRUE;
|
||||
modeset_config->lut.output.pRamps = nv_crtc_state->olut_ramps;
|
||||
} else {
|
||||
/* disabling the output LUT should be equivalent to setting a linear
|
||||
* LUT */
|
||||
modeset_config->lut.output.specified = NV_TRUE;
|
||||
modeset_config->lut.output.enabled = NV_FALSE;
|
||||
modeset_config->lut.output.pRamps = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
/* free allocated state */
|
||||
nv_drm_free(nv_crtc_state->ilut_ramps);
|
||||
nv_drm_free(nv_crtc_state->olut_ramps);
|
||||
|
||||
/* remove dangling pointers */
|
||||
nv_crtc_state->ilut_ramps = NULL;
|
||||
nv_crtc_state->olut_ramps = NULL;
|
||||
modeset_config->lut.input.pRamps = NULL;
|
||||
modeset_config->lut.output.pRamps = NULL;
|
||||
|
||||
/* prevent attempts at reading NULLs */
|
||||
modeset_config->lut.input.specified = NV_FALSE;
|
||||
modeset_config->lut.output.specified = NV_FALSE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* NV_DRM_COLOR_MGMT_AVAILABLE */
|
||||
|
||||
/**
|
||||
* nv_drm_crtc_atomic_check() can fail after it has modified
|
||||
* the 'nv_drm_crtc_state::req_config', that is fine because 'nv_drm_crtc_state'
|
||||
@ -887,6 +1104,9 @@ static int nv_drm_crtc_atomic_check(struct drm_crtc *crtc,
|
||||
struct NvKmsKapiHeadRequestedConfig *req_config =
|
||||
&nv_crtc_state->req_config;
|
||||
int ret = 0;
|
||||
#if defined(NV_DRM_COLOR_MGMT_AVAILABLE)
|
||||
struct nv_drm_device *nv_dev = to_nv_device(crtc_state->crtc->dev);
|
||||
#endif
|
||||
|
||||
if (crtc_state->mode_changed) {
|
||||
drm_mode_to_nvkms_display_mode(&crtc_state->mode,
|
||||
@ -925,6 +1145,25 @@ static int nv_drm_crtc_atomic_check(struct drm_crtc *crtc,
|
||||
req_config->flags.activeChanged = NV_TRUE;
|
||||
}
|
||||
|
||||
#if defined(NV_DRM_CRTC_STATE_HAS_VRR_ENABLED)
|
||||
req_config->modeSetConfig.vrrEnabled = crtc_state->vrr_enabled;
|
||||
#endif
|
||||
|
||||
#if defined(NV_DRM_COLOR_MGMT_AVAILABLE)
|
||||
if (nv_dev->drmMasterChangedSinceLastAtomicCommit &&
|
||||
(crtc_state->degamma_lut ||
|
||||
crtc_state->ctm ||
|
||||
crtc_state->gamma_lut)) {
|
||||
|
||||
crtc_state->color_mgmt_changed = NV_TRUE;
|
||||
}
|
||||
if (crtc_state->color_mgmt_changed) {
|
||||
if ((ret = color_mgmt_config_set(nv_crtc_state, req_config)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1156,6 +1395,8 @@ nv_drm_plane_create(struct drm_device *dev,
|
||||
plane,
|
||||
validLayerRRTransforms);
|
||||
|
||||
nv_drm_free(formats);
|
||||
|
||||
return plane;
|
||||
|
||||
failed_plane_init:
|
||||
@ -1220,6 +1461,22 @@ static struct drm_crtc *__nv_drm_crtc_create(struct nv_drm_device *nv_dev,
|
||||
|
||||
drm_crtc_helper_add(&nv_crtc->base, &nv_crtc_helper_funcs);
|
||||
|
||||
#if defined(NV_DRM_COLOR_MGMT_AVAILABLE)
|
||||
#if defined(NV_DRM_CRTC_ENABLE_COLOR_MGMT_PRESENT)
|
||||
drm_crtc_enable_color_mgmt(&nv_crtc->base, NVKMS_LUT_ARRAY_SIZE, true,
|
||||
NVKMS_LUT_ARRAY_SIZE);
|
||||
#else
|
||||
drm_helper_crtc_enable_color_mgmt(&nv_crtc->base, NVKMS_LUT_ARRAY_SIZE,
|
||||
NVKMS_LUT_ARRAY_SIZE);
|
||||
#endif
|
||||
ret = drm_mode_crtc_set_gamma_size(&nv_crtc->base, NVKMS_LUT_ARRAY_SIZE);
|
||||
if (ret != 0) {
|
||||
NV_DRM_DEV_LOG_WARN(
|
||||
nv_dev,
|
||||
"Failed to initialize legacy gamma support for head %u", head);
|
||||
}
|
||||
#endif
|
||||
|
||||
return &nv_crtc->base;
|
||||
|
||||
failed_init_crtc:
|
||||
@ -1328,10 +1585,16 @@ static void NvKmsKapiCrcsToDrm(const struct NvKmsKapiCrcs *crcs,
|
||||
{
|
||||
drmCrcs->outputCrc32.value = crcs->outputCrc32.value;
|
||||
drmCrcs->outputCrc32.supported = crcs->outputCrc32.supported;
|
||||
drmCrcs->outputCrc32.__pad0 = 0;
|
||||
drmCrcs->outputCrc32.__pad1 = 0;
|
||||
drmCrcs->rasterGeneratorCrc32.value = crcs->rasterGeneratorCrc32.value;
|
||||
drmCrcs->rasterGeneratorCrc32.supported = crcs->rasterGeneratorCrc32.supported;
|
||||
drmCrcs->rasterGeneratorCrc32.__pad0 = 0;
|
||||
drmCrcs->rasterGeneratorCrc32.__pad1 = 0;
|
||||
drmCrcs->compositorCrc32.value = crcs->compositorCrc32.value;
|
||||
drmCrcs->compositorCrc32.supported = crcs->compositorCrc32.supported;
|
||||
drmCrcs->compositorCrc32.__pad0 = 0;
|
||||
drmCrcs->compositorCrc32.__pad1 = 0;
|
||||
}
|
||||
|
||||
int nv_drm_get_crtc_crc32_v2_ioctl(struct drm_device *dev,
|
||||
|
@ -129,6 +129,9 @@ struct nv_drm_crtc_state {
|
||||
*/
|
||||
struct NvKmsKapiHeadRequestedConfig req_config;
|
||||
|
||||
struct NvKmsLutRamps *ilut_ramps;
|
||||
struct NvKmsLutRamps *olut_ramps;
|
||||
|
||||
/**
|
||||
* @nv_flip:
|
||||
*
|
||||
|
@ -44,6 +44,10 @@
|
||||
#include <drm/drmP.h>
|
||||
#endif
|
||||
|
||||
#if defined(NV_DRM_DRM_ATOMIC_UAPI_H_PRESENT)
|
||||
#include <drm/drm_atomic_uapi.h>
|
||||
#endif
|
||||
|
||||
#if defined(NV_DRM_DRM_VBLANK_H_PRESENT)
|
||||
#include <drm/drm_vblank.h>
|
||||
#endif
|
||||
@ -60,6 +64,15 @@
|
||||
#include <drm/drm_ioctl.h>
|
||||
#endif
|
||||
|
||||
#if defined(NV_DRM_FBDEV_GENERIC_AVAILABLE)
|
||||
#include <drm/drm_aperture.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#endif
|
||||
|
||||
#if defined(NV_DRM_DRM_FBDEV_GENERIC_H_PRESENT)
|
||||
#include <drm/drm_fbdev_generic.h>
|
||||
#endif
|
||||
|
||||
#include <linux/pci.h>
|
||||
|
||||
/*
|
||||
@ -84,6 +97,11 @@
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#endif
|
||||
|
||||
static int nv_drm_revoke_modeset_permission(struct drm_device *dev,
|
||||
struct drm_file *filep,
|
||||
NvU32 dpyId);
|
||||
static int nv_drm_revoke_sub_ownership(struct drm_device *dev);
|
||||
|
||||
static struct nv_drm_device *dev_list = NULL;
|
||||
|
||||
static const char* nv_get_input_colorspace_name(
|
||||
@ -460,6 +478,11 @@ static int nv_drm_load(struct drm_device *dev, unsigned long flags)
|
||||
|
||||
nv_dev->supportsSyncpts = resInfo.caps.supportsSyncpts;
|
||||
|
||||
nv_dev->semsurf_stride = resInfo.caps.semsurf.stride;
|
||||
|
||||
nv_dev->semsurf_max_submitted_offset =
|
||||
resInfo.caps.semsurf.maxSubmittedOffset;
|
||||
|
||||
#if defined(NV_DRM_FORMAT_MODIFIERS_PRESENT)
|
||||
gen = nv_dev->pageKindGeneration;
|
||||
kind = nv_dev->genericPageKind;
|
||||
@ -546,6 +569,8 @@ static void __nv_drm_unload(struct drm_device *dev)
|
||||
|
||||
mutex_lock(&nv_dev->lock);
|
||||
|
||||
WARN_ON(nv_dev->subOwnershipGranted);
|
||||
|
||||
/* Disable event handling */
|
||||
|
||||
atomic_set(&nv_dev->enable_event_handling, false);
|
||||
@ -595,9 +620,15 @@ static int __nv_drm_master_set(struct drm_device *dev,
|
||||
{
|
||||
struct nv_drm_device *nv_dev = to_nv_device(dev);
|
||||
|
||||
if (!nvKms->grabOwnership(nv_dev->pDevice)) {
|
||||
/*
|
||||
* If this device is driving a framebuffer, then nvidia-drm already has
|
||||
* modeset ownership. Otherwise, grab ownership now.
|
||||
*/
|
||||
if (!nv_dev->hasFramebufferConsole &&
|
||||
!nvKms->grabOwnership(nv_dev->pDevice)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
nv_dev->drmMasterChangedSinceLastAtomicCommit = NV_TRUE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -631,6 +662,9 @@ void nv_drm_master_drop(struct drm_device *dev, struct drm_file *file_priv)
|
||||
struct nv_drm_device *nv_dev = to_nv_device(dev);
|
||||
int err;
|
||||
|
||||
nv_drm_revoke_modeset_permission(dev, file_priv, 0);
|
||||
nv_drm_revoke_sub_ownership(dev);
|
||||
|
||||
/*
|
||||
* After dropping nvkms modeset onwership, it is not guaranteed that
|
||||
* drm and nvkms modeset state will remain in sync. Therefore, disable
|
||||
@ -655,7 +689,9 @@ void nv_drm_master_drop(struct drm_device *dev, struct drm_file *file_priv)
|
||||
|
||||
drm_modeset_unlock_all(dev);
|
||||
|
||||
nvKms->releaseOwnership(nv_dev->pDevice);
|
||||
if (!nv_dev->hasFramebufferConsole) {
|
||||
nvKms->releaseOwnership(nv_dev->pDevice);
|
||||
}
|
||||
}
|
||||
#endif /* NV_DRM_ATOMIC_MODESET_AVAILABLE */
|
||||
|
||||
@ -693,15 +729,24 @@ static int nv_drm_get_dev_info_ioctl(struct drm_device *dev,
|
||||
|
||||
params->gpu_id = nv_dev->gpu_info.gpu_id;
|
||||
params->primary_index = dev->primary->index;
|
||||
params->generic_page_kind = 0;
|
||||
params->page_kind_generation = 0;
|
||||
params->sector_layout = 0;
|
||||
params->supports_sync_fd = false;
|
||||
params->supports_semsurf = false;
|
||||
|
||||
#if defined(NV_DRM_ATOMIC_MODESET_AVAILABLE)
|
||||
params->generic_page_kind = nv_dev->genericPageKind;
|
||||
params->page_kind_generation = nv_dev->pageKindGeneration;
|
||||
params->sector_layout = nv_dev->sectorLayout;
|
||||
#else
|
||||
params->generic_page_kind = 0;
|
||||
params->page_kind_generation = 0;
|
||||
params->sector_layout = 0;
|
||||
#endif
|
||||
/* Semaphore surfaces are only supported if the modeset = 1 parameter is set */
|
||||
if ((nv_dev->pDevice) != NULL && (nv_dev->semsurf_stride != 0)) {
|
||||
params->supports_semsurf = true;
|
||||
#if defined(NV_SYNC_FILE_GET_FENCE_PRESENT)
|
||||
params->supports_sync_fd = true;
|
||||
#endif /* defined(NV_SYNC_FILE_GET_FENCE_PRESENT) */
|
||||
}
|
||||
#endif /* defined(NV_DRM_ATOMIC_MODESET_AVAILABLE) */
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -833,10 +878,10 @@ static NvU32 nv_drm_get_head_bit_from_connector(struct drm_connector *connector)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nv_drm_grant_permission_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *filep)
|
||||
static int nv_drm_grant_modeset_permission(struct drm_device *dev,
|
||||
struct drm_nvidia_grant_permissions_params *params,
|
||||
struct drm_file *filep)
|
||||
{
|
||||
struct drm_nvidia_grant_permissions_params *params = data;
|
||||
struct nv_drm_device *nv_dev = to_nv_device(dev);
|
||||
struct nv_drm_connector *target_nv_connector = NULL;
|
||||
struct nv_drm_crtc *target_nv_crtc = NULL;
|
||||
@ -958,26 +1003,102 @@ done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool nv_drm_revoke_connector(struct nv_drm_device *nv_dev,
|
||||
struct nv_drm_connector *nv_connector)
|
||||
static int nv_drm_grant_sub_ownership(struct drm_device *dev,
|
||||
struct drm_nvidia_grant_permissions_params *params)
|
||||
{
|
||||
bool ret = true;
|
||||
if (nv_connector->modeset_permission_crtc) {
|
||||
if (nv_connector->nv_detected_encoder) {
|
||||
ret = nvKms->revokePermissions(
|
||||
nv_dev->pDevice, nv_connector->modeset_permission_crtc->head,
|
||||
nv_connector->nv_detected_encoder->hDisplay);
|
||||
}
|
||||
nv_connector->modeset_permission_crtc->modeset_permission_filep = NULL;
|
||||
nv_connector->modeset_permission_crtc = NULL;
|
||||
int ret = -EINVAL;
|
||||
struct nv_drm_device *nv_dev = to_nv_device(dev);
|
||||
struct drm_modeset_acquire_ctx *pctx;
|
||||
#if NV_DRM_MODESET_LOCK_ALL_END_ARGUMENT_COUNT == 3
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE,
|
||||
ret);
|
||||
pctx = &ctx;
|
||||
#else
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
pctx = dev->mode_config.acquire_ctx;
|
||||
#endif
|
||||
|
||||
if (nv_dev->subOwnershipGranted ||
|
||||
!nvKms->grantSubOwnership(params->fd, nv_dev->pDevice)) {
|
||||
goto done;
|
||||
}
|
||||
nv_connector->modeset_permission_filep = NULL;
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* When creating an ownership grant, shut down all heads and disable flip
|
||||
* notifications.
|
||||
*/
|
||||
ret = nv_drm_atomic_helper_disable_all(dev, pctx);
|
||||
if (ret != 0) {
|
||||
NV_DRM_DEV_LOG_ERR(
|
||||
nv_dev,
|
||||
"nv_drm_atomic_helper_disable_all failed with error code %d!",
|
||||
ret);
|
||||
}
|
||||
|
||||
atomic_set(&nv_dev->enable_event_handling, false);
|
||||
nv_dev->subOwnershipGranted = NV_TRUE;
|
||||
|
||||
ret = 0;
|
||||
|
||||
done:
|
||||
#if NV_DRM_MODESET_LOCK_ALL_END_ARGUMENT_COUNT == 3
|
||||
DRM_MODESET_LOCK_ALL_END(dev, ctx, ret);
|
||||
#else
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nv_drm_revoke_permission(struct drm_device *dev,
|
||||
struct drm_file *filep, NvU32 dpyId)
|
||||
static int nv_drm_grant_permission_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *filep)
|
||||
{
|
||||
struct drm_nvidia_grant_permissions_params *params = data;
|
||||
|
||||
if (params->type == NV_DRM_PERMISSIONS_TYPE_MODESET) {
|
||||
return nv_drm_grant_modeset_permission(dev, params, filep);
|
||||
} else if (params->type == NV_DRM_PERMISSIONS_TYPE_SUB_OWNER) {
|
||||
return nv_drm_grant_sub_ownership(dev, params);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
nv_drm_atomic_disable_connector(struct drm_atomic_state *state,
|
||||
struct nv_drm_connector *nv_connector)
|
||||
{
|
||||
struct drm_crtc_state *crtc_state;
|
||||
struct drm_connector_state *connector_state;
|
||||
int ret = 0;
|
||||
|
||||
if (nv_connector->modeset_permission_crtc) {
|
||||
crtc_state = drm_atomic_get_crtc_state(
|
||||
state, &nv_connector->modeset_permission_crtc->base);
|
||||
if (!crtc_state) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
crtc_state->active = false;
|
||||
ret = drm_atomic_set_mode_prop_for_crtc(crtc_state, NULL);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
connector_state = drm_atomic_get_connector_state(state, &nv_connector->base);
|
||||
if (!connector_state) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return drm_atomic_set_crtc_for_connector(connector_state, NULL);
|
||||
}
|
||||
|
||||
static int nv_drm_revoke_modeset_permission(struct drm_device *dev,
|
||||
struct drm_file *filep, NvU32 dpyId)
|
||||
{
|
||||
struct drm_modeset_acquire_ctx *pctx;
|
||||
struct drm_atomic_state *state;
|
||||
struct drm_connector *connector;
|
||||
struct drm_crtc *crtc;
|
||||
int ret = 0;
|
||||
@ -988,10 +1109,19 @@ static int nv_drm_revoke_permission(struct drm_device *dev,
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE,
|
||||
ret);
|
||||
pctx = &ctx;
|
||||
#else
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
pctx = dev->mode_config.acquire_ctx;
|
||||
#endif
|
||||
|
||||
state = drm_atomic_state_alloc(dev);
|
||||
if (!state) {
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
state->acquire_ctx = pctx;
|
||||
|
||||
/*
|
||||
* If dpyId is set, only revoke those specific resources. Otherwise,
|
||||
* it is from closing the file so revoke all resources for that filep.
|
||||
@ -1003,10 +1133,13 @@ static int nv_drm_revoke_permission(struct drm_device *dev,
|
||||
struct nv_drm_connector *nv_connector = to_nv_connector(connector);
|
||||
if (nv_connector->modeset_permission_filep == filep &&
|
||||
(!dpyId || nv_drm_connector_is_dpy_id(connector, dpyId))) {
|
||||
if (!nv_drm_connector_revoke_permissions(dev, nv_connector)) {
|
||||
ret = -EINVAL;
|
||||
// Continue trying to revoke as much as possible.
|
||||
ret = nv_drm_atomic_disable_connector(state, nv_connector);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Continue trying to revoke as much as possible.
|
||||
nv_drm_connector_revoke_permissions(dev, nv_connector);
|
||||
}
|
||||
}
|
||||
#if defined(NV_DRM_CONNECTOR_LIST_ITER_PRESENT)
|
||||
@ -1020,6 +1153,25 @@ static int nv_drm_revoke_permission(struct drm_device *dev,
|
||||
}
|
||||
}
|
||||
|
||||
ret = drm_atomic_commit(state);
|
||||
done:
|
||||
#if defined(NV_DRM_ATOMIC_STATE_REF_COUNTING_PRESENT)
|
||||
drm_atomic_state_put(state);
|
||||
#else
|
||||
if (ret != 0) {
|
||||
drm_atomic_state_free(state);
|
||||
} else {
|
||||
/*
|
||||
* In case of success, drm_atomic_commit() takes care to cleanup and
|
||||
* free @state.
|
||||
*
|
||||
* Comment placed above drm_atomic_commit() says: The caller must not
|
||||
* free or in any other way access @state. If the function fails then
|
||||
* the caller must clean up @state itself.
|
||||
*/
|
||||
}
|
||||
#endif
|
||||
|
||||
#if NV_DRM_MODESET_LOCK_ALL_END_ARGUMENT_COUNT == 3
|
||||
DRM_MODESET_LOCK_ALL_END(dev, ctx, ret);
|
||||
#else
|
||||
@ -1029,14 +1181,55 @@ static int nv_drm_revoke_permission(struct drm_device *dev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nv_drm_revoke_sub_ownership(struct drm_device *dev)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
struct nv_drm_device *nv_dev = to_nv_device(dev);
|
||||
#if NV_DRM_MODESET_LOCK_ALL_END_ARGUMENT_COUNT == 3
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE,
|
||||
ret);
|
||||
#else
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
#endif
|
||||
|
||||
if (!nv_dev->subOwnershipGranted) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!nvKms->revokeSubOwnership(nv_dev->pDevice)) {
|
||||
NV_DRM_DEV_LOG_ERR(nv_dev, "Failed to revoke sub-ownership from NVKMS");
|
||||
goto done;
|
||||
}
|
||||
|
||||
nv_dev->subOwnershipGranted = NV_FALSE;
|
||||
atomic_set(&nv_dev->enable_event_handling, true);
|
||||
ret = 0;
|
||||
|
||||
done:
|
||||
#if NV_DRM_MODESET_LOCK_ALL_END_ARGUMENT_COUNT == 3
|
||||
DRM_MODESET_LOCK_ALL_END(dev, ctx, ret);
|
||||
#else
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nv_drm_revoke_permission_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *filep)
|
||||
{
|
||||
struct drm_nvidia_revoke_permissions_params *params = data;
|
||||
if (!params->dpyId) {
|
||||
return -EINVAL;
|
||||
|
||||
if (params->type == NV_DRM_PERMISSIONS_TYPE_MODESET) {
|
||||
if (!params->dpyId) {
|
||||
return -EINVAL;
|
||||
}
|
||||
return nv_drm_revoke_modeset_permission(dev, filep, params->dpyId);
|
||||
} else if (params->type == NV_DRM_PERMISSIONS_TYPE_SUB_OWNER) {
|
||||
return nv_drm_revoke_sub_ownership(dev);
|
||||
}
|
||||
return nv_drm_revoke_permission(dev, filep, params->dpyId);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void nv_drm_postclose(struct drm_device *dev, struct drm_file *filep)
|
||||
@ -1051,7 +1244,7 @@ static void nv_drm_postclose(struct drm_device *dev, struct drm_file *filep)
|
||||
dev->mode_config.num_connector > 0 &&
|
||||
dev->mode_config.connector_list.next != NULL &&
|
||||
dev->mode_config.connector_list.prev != NULL) {
|
||||
nv_drm_revoke_permission(dev, filep, 0);
|
||||
nv_drm_revoke_modeset_permission(dev, filep, 0);
|
||||
}
|
||||
}
|
||||
#endif /* NV_DRM_ATOMIC_MODESET_AVAILABLE */
|
||||
@ -1310,6 +1503,18 @@ static const struct drm_ioctl_desc nv_drm_ioctls[] = {
|
||||
DRM_IOCTL_DEF_DRV(NVIDIA_GEM_PRIME_FENCE_ATTACH,
|
||||
nv_drm_gem_prime_fence_attach_ioctl,
|
||||
DRM_RENDER_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF_DRV(NVIDIA_SEMSURF_FENCE_CTX_CREATE,
|
||||
nv_drm_semsurf_fence_ctx_create_ioctl,
|
||||
DRM_RENDER_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF_DRV(NVIDIA_SEMSURF_FENCE_CREATE,
|
||||
nv_drm_semsurf_fence_create_ioctl,
|
||||
DRM_RENDER_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF_DRV(NVIDIA_SEMSURF_FENCE_WAIT,
|
||||
nv_drm_semsurf_fence_wait_ioctl,
|
||||
DRM_RENDER_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF_DRV(NVIDIA_SEMSURF_FENCE_ATTACH,
|
||||
nv_drm_semsurf_fence_attach_ioctl,
|
||||
DRM_RENDER_ALLOW|DRM_UNLOCKED),
|
||||
#endif
|
||||
|
||||
DRM_IOCTL_DEF_DRV(NVIDIA_GET_CLIENT_CAPABILITY,
|
||||
@ -1513,6 +1718,30 @@ static void nv_drm_register_drm_device(const nv_gpu_info_t *gpu_info)
|
||||
goto failed_drm_register;
|
||||
}
|
||||
|
||||
#if defined(NV_DRM_FBDEV_GENERIC_AVAILABLE)
|
||||
if (nv_drm_fbdev_module_param &&
|
||||
drm_core_check_feature(dev, DRIVER_MODESET)) {
|
||||
|
||||
if (!nvKms->grabOwnership(nv_dev->pDevice)) {
|
||||
NV_DRM_DEV_LOG_ERR(nv_dev, "Failed to grab NVKMS modeset ownership");
|
||||
goto failed_grab_ownership;
|
||||
}
|
||||
|
||||
if (device->bus == &pci_bus_type) {
|
||||
struct pci_dev *pdev = to_pci_dev(device);
|
||||
|
||||
#if defined(NV_DRM_APERTURE_REMOVE_CONFLICTING_PCI_FRAMEBUFFERS_HAS_DRIVER_ARG)
|
||||
drm_aperture_remove_conflicting_pci_framebuffers(pdev, &nv_drm_driver);
|
||||
#else
|
||||
drm_aperture_remove_conflicting_pci_framebuffers(pdev, nv_drm_driver.name);
|
||||
#endif
|
||||
}
|
||||
drm_fbdev_generic_setup(dev, 32);
|
||||
|
||||
nv_dev->hasFramebufferConsole = NV_TRUE;
|
||||
}
|
||||
#endif /* defined(NV_DRM_FBDEV_GENERIC_AVAILABLE) */
|
||||
|
||||
/* Add NVIDIA-DRM device into list */
|
||||
|
||||
nv_dev->next = dev_list;
|
||||
@ -1520,6 +1749,12 @@ static void nv_drm_register_drm_device(const nv_gpu_info_t *gpu_info)
|
||||
|
||||
return; /* Success */
|
||||
|
||||
#if defined(NV_DRM_FBDEV_GENERIC_AVAILABLE)
|
||||
failed_grab_ownership:
|
||||
|
||||
drm_dev_unregister(dev);
|
||||
#endif
|
||||
|
||||
failed_drm_register:
|
||||
|
||||
nv_drm_dev_free(dev);
|
||||
@ -1582,9 +1817,16 @@ void nv_drm_remove_devices(void)
|
||||
{
|
||||
while (dev_list != NULL) {
|
||||
struct nv_drm_device *next = dev_list->next;
|
||||
struct drm_device *dev = dev_list->dev;
|
||||
|
||||
drm_dev_unregister(dev_list->dev);
|
||||
nv_drm_dev_free(dev_list->dev);
|
||||
#if defined(NV_DRM_FBDEV_GENERIC_AVAILABLE)
|
||||
if (dev_list->hasFramebufferConsole) {
|
||||
drm_atomic_helper_shutdown(dev);
|
||||
nvKms->releaseOwnership(dev_list->pDevice);
|
||||
}
|
||||
#endif
|
||||
drm_dev_unregister(dev);
|
||||
nv_drm_dev_free(dev);
|
||||
|
||||
nv_drm_free(dev_list);
|
||||
|
||||
|
@ -38,6 +38,8 @@
|
||||
|
||||
#include "nvidia-dma-fence-helper.h"
|
||||
|
||||
#define NV_DRM_SEMAPHORE_SURFACE_FENCE_MAX_TIMEOUT_MS 5000
|
||||
|
||||
struct nv_drm_fence_context;
|
||||
|
||||
struct nv_drm_fence_context_ops {
|
||||
@ -45,17 +47,19 @@ struct nv_drm_fence_context_ops {
|
||||
};
|
||||
|
||||
struct nv_drm_fence_context {
|
||||
struct nv_drm_gem_object base;
|
||||
|
||||
const struct nv_drm_fence_context_ops *ops;
|
||||
|
||||
struct nv_drm_device *nv_dev;
|
||||
uint32_t context;
|
||||
uint64_t context;
|
||||
|
||||
NvU64 fenceSemIndex; /* Index into semaphore surface */
|
||||
};
|
||||
|
||||
struct nv_drm_prime_fence_context {
|
||||
struct nv_drm_fence_context base;
|
||||
|
||||
NvU64 fenceSemIndex; /* Index into semaphore surface */
|
||||
|
||||
/* Mapped semaphore surface */
|
||||
struct NvKmsKapiMemory *pSemSurface;
|
||||
NvU32 *pLinearAddress;
|
||||
@ -181,7 +185,7 @@ static void nv_drm_gem_prime_fence_event
|
||||
|
||||
/* Index into surface with 16 byte stride */
|
||||
unsigned int seqno = *((nv_fence_context->pLinearAddress) +
|
||||
(nv_fence_context->fenceSemIndex * 4));
|
||||
(nv_fence_context->base.fenceSemIndex * 4));
|
||||
|
||||
if (nv_fence->base.seqno > seqno) {
|
||||
/*
|
||||
@ -199,8 +203,8 @@ static void nv_drm_gem_prime_fence_event
|
||||
}
|
||||
|
||||
static inline struct nv_drm_prime_fence_context*
|
||||
to_prime_fence_context(struct nv_drm_fence_context *nv_fence_context) {
|
||||
return (struct nv_drm_prime_fence_context *)nv_fence_context;
|
||||
to_nv_prime_fence_context(struct nv_drm_fence_context *nv_fence_context) {
|
||||
return container_of(nv_fence_context, struct nv_drm_prime_fence_context, base);
|
||||
}
|
||||
|
||||
static void __nv_drm_prime_fence_context_destroy(
|
||||
@ -208,7 +212,7 @@ static void __nv_drm_prime_fence_context_destroy(
|
||||
{
|
||||
struct nv_drm_device *nv_dev = nv_fence_context->nv_dev;
|
||||
struct nv_drm_prime_fence_context *nv_prime_fence_context =
|
||||
to_prime_fence_context(nv_fence_context);
|
||||
to_nv_prime_fence_context(nv_fence_context);
|
||||
|
||||
/*
|
||||
* Free channel event before destroying the fence context, otherwise event
|
||||
@ -293,9 +297,9 @@ __nv_drm_prime_fence_context_new(
|
||||
.base.ops = &nv_drm_prime_fence_context_ops,
|
||||
.base.nv_dev = nv_dev,
|
||||
.base.context = nv_dma_fence_context_alloc(1),
|
||||
.base.fenceSemIndex = p->index,
|
||||
.pSemSurface = pSemSurface,
|
||||
.pLinearAddress = pLinearAddress,
|
||||
.fenceSemIndex = p->index,
|
||||
};
|
||||
|
||||
INIT_LIST_HEAD(&nv_prime_fence_context->pending);
|
||||
@ -391,47 +395,37 @@ int nv_drm_fence_supported_ioctl(struct drm_device *dev,
|
||||
return nv_dev->pDevice ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
struct nv_drm_gem_fence_context {
|
||||
struct nv_drm_gem_object base;
|
||||
struct nv_drm_fence_context *nv_fence_context;
|
||||
};
|
||||
|
||||
static inline struct nv_drm_gem_fence_context *to_gem_fence_context(
|
||||
static inline struct nv_drm_fence_context *to_nv_fence_context(
|
||||
struct nv_drm_gem_object *nv_gem)
|
||||
{
|
||||
if (nv_gem != NULL) {
|
||||
return container_of(nv_gem, struct nv_drm_gem_fence_context, base);
|
||||
return container_of(nv_gem, struct nv_drm_fence_context, base);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tear down of the 'struct nv_drm_gem_fence_context' object is not expected
|
||||
* Tear down of the 'struct nv_drm_fence_context' object is not expected
|
||||
* to be happen from any worker thread, if that happen it causes dead-lock
|
||||
* because tear down sequence calls to flush all existing
|
||||
* worker thread.
|
||||
*/
|
||||
static void
|
||||
__nv_drm_gem_fence_context_free(struct nv_drm_gem_object *nv_gem)
|
||||
__nv_drm_fence_context_gem_free(struct nv_drm_gem_object *nv_gem)
|
||||
{
|
||||
struct nv_drm_gem_fence_context *nv_gem_fence_context =
|
||||
to_gem_fence_context(nv_gem);
|
||||
struct nv_drm_fence_context *nv_fence_context =
|
||||
nv_gem_fence_context->nv_fence_context;
|
||||
struct nv_drm_fence_context *nv_fence_context = to_nv_fence_context(nv_gem);
|
||||
|
||||
nv_fence_context->ops->destroy(nv_fence_context);
|
||||
|
||||
nv_drm_free(nv_gem_fence_context);
|
||||
}
|
||||
|
||||
const struct nv_drm_gem_object_funcs nv_gem_fence_context_ops = {
|
||||
.free = __nv_drm_gem_fence_context_free,
|
||||
const struct nv_drm_gem_object_funcs nv_fence_context_gem_ops = {
|
||||
.free = __nv_drm_fence_context_gem_free,
|
||||
};
|
||||
|
||||
static inline
|
||||
struct nv_drm_gem_fence_context *
|
||||
__nv_drm_gem_object_fence_context_lookup(
|
||||
struct nv_drm_fence_context *
|
||||
__nv_drm_fence_context_lookup(
|
||||
struct drm_device *dev,
|
||||
struct drm_file *filp,
|
||||
u32 handle)
|
||||
@ -439,43 +433,31 @@ __nv_drm_gem_object_fence_context_lookup(
|
||||
struct nv_drm_gem_object *nv_gem =
|
||||
nv_drm_gem_object_lookup(dev, filp, handle);
|
||||
|
||||
if (nv_gem != NULL && nv_gem->ops != &nv_gem_fence_context_ops) {
|
||||
if (nv_gem != NULL && nv_gem->ops != &nv_fence_context_gem_ops) {
|
||||
nv_drm_gem_object_unreference_unlocked(nv_gem);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return to_gem_fence_context(nv_gem);
|
||||
return to_nv_fence_context(nv_gem);
|
||||
}
|
||||
|
||||
static int
|
||||
__nv_drm_gem_fence_context_create(struct drm_device *dev,
|
||||
struct nv_drm_fence_context *nv_fence_context,
|
||||
u32 *handle,
|
||||
struct drm_file *filep)
|
||||
__nv_drm_fence_context_gem_init(struct drm_device *dev,
|
||||
struct nv_drm_fence_context *nv_fence_context,
|
||||
u32 *handle,
|
||||
struct drm_file *filep)
|
||||
{
|
||||
struct nv_drm_device *nv_dev = to_nv_device(dev);
|
||||
struct nv_drm_gem_fence_context *nv_gem_fence_context = NULL;
|
||||
|
||||
if ((nv_gem_fence_context = nv_drm_calloc(
|
||||
1,
|
||||
sizeof(struct nv_drm_gem_fence_context))) == NULL) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
nv_gem_fence_context->nv_fence_context = nv_fence_context;
|
||||
|
||||
nv_drm_gem_object_init(nv_dev,
|
||||
&nv_gem_fence_context->base,
|
||||
&nv_gem_fence_context_ops,
|
||||
&nv_fence_context->base,
|
||||
&nv_fence_context_gem_ops,
|
||||
0 /* size */,
|
||||
NULL /* pMemory */);
|
||||
|
||||
return nv_drm_gem_handle_create_drop_reference(filep,
|
||||
&nv_gem_fence_context->base,
|
||||
&nv_fence_context->base,
|
||||
handle);
|
||||
|
||||
done:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int nv_drm_prime_fence_context_create_ioctl(struct drm_device *dev,
|
||||
@ -491,10 +473,10 @@ int nv_drm_prime_fence_context_create_ioctl(struct drm_device *dev,
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = __nv_drm_gem_fence_context_create(dev,
|
||||
&nv_prime_fence_context->base,
|
||||
&p->handle,
|
||||
filep);
|
||||
err = __nv_drm_fence_context_gem_init(dev,
|
||||
&nv_prime_fence_context->base,
|
||||
&p->handle,
|
||||
filep);
|
||||
if (err) {
|
||||
__nv_drm_prime_fence_context_destroy(&nv_prime_fence_context->base);
|
||||
}
|
||||
@ -505,6 +487,31 @@ done:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int __nv_drm_gem_attach_fence(struct nv_drm_gem_object *nv_gem,
|
||||
nv_dma_fence_t *fence,
|
||||
bool shared)
|
||||
{
|
||||
nv_dma_resv_t *resv = nv_drm_gem_res_obj(nv_gem);
|
||||
int ret;
|
||||
|
||||
nv_dma_resv_lock(resv, NULL);
|
||||
|
||||
ret = nv_dma_resv_reserve_fences(resv, 1, shared);
|
||||
if (ret == 0) {
|
||||
if (shared) {
|
||||
nv_dma_resv_add_shared_fence(resv, fence);
|
||||
} else {
|
||||
nv_dma_resv_add_excl_fence(resv, fence);
|
||||
}
|
||||
} else {
|
||||
NV_DRM_LOG_ERR("Failed to reserve fence. Error code: %d", ret);
|
||||
}
|
||||
|
||||
nv_dma_resv_unlock(resv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int nv_drm_gem_prime_fence_attach_ioctl(struct drm_device *dev,
|
||||
void *data, struct drm_file *filep)
|
||||
{
|
||||
@ -513,10 +520,13 @@ int nv_drm_gem_prime_fence_attach_ioctl(struct drm_device *dev,
|
||||
struct drm_nvidia_gem_prime_fence_attach_params *p = data;
|
||||
|
||||
struct nv_drm_gem_object *nv_gem;
|
||||
struct nv_drm_gem_fence_context *nv_gem_fence_context;
|
||||
|
||||
struct nv_drm_fence_context *nv_fence_context;
|
||||
nv_dma_fence_t *fence;
|
||||
nv_dma_resv_t *resv;
|
||||
|
||||
if (p->__pad != 0) {
|
||||
NV_DRM_DEV_LOG_ERR(nv_dev, "Padding fields must be zeroed");
|
||||
goto done;
|
||||
}
|
||||
|
||||
nv_gem = nv_drm_gem_object_lookup(nv_dev->dev, filep, p->handle);
|
||||
|
||||
@ -529,7 +539,7 @@ int nv_drm_gem_prime_fence_attach_ioctl(struct drm_device *dev,
|
||||
goto done;
|
||||
}
|
||||
|
||||
if((nv_gem_fence_context = __nv_drm_gem_object_fence_context_lookup(
|
||||
if((nv_fence_context = __nv_drm_fence_context_lookup(
|
||||
nv_dev->dev,
|
||||
filep,
|
||||
p->fence_context_handle)) == NULL) {
|
||||
@ -542,7 +552,7 @@ int nv_drm_gem_prime_fence_attach_ioctl(struct drm_device *dev,
|
||||
goto fence_context_lookup_failed;
|
||||
}
|
||||
|
||||
if (nv_gem_fence_context->nv_fence_context->ops !=
|
||||
if (nv_fence_context->ops !=
|
||||
&nv_drm_prime_fence_context_ops) {
|
||||
|
||||
NV_DRM_DEV_LOG_ERR(
|
||||
@ -554,7 +564,7 @@ int nv_drm_gem_prime_fence_attach_ioctl(struct drm_device *dev,
|
||||
}
|
||||
|
||||
fence = __nv_drm_prime_fence_context_create_fence(
|
||||
to_prime_fence_context(nv_gem_fence_context->nv_fence_context),
|
||||
to_nv_prime_fence_context(nv_fence_context),
|
||||
p->sem_thresh);
|
||||
|
||||
if (IS_ERR(fence)) {
|
||||
@ -567,26 +577,12 @@ int nv_drm_gem_prime_fence_attach_ioctl(struct drm_device *dev,
|
||||
goto fence_context_create_fence_failed;
|
||||
}
|
||||
|
||||
resv = nv_drm_gem_res_obj(nv_gem);
|
||||
ret = __nv_drm_gem_attach_fence(nv_gem, fence, true /* exclusive */);
|
||||
|
||||
nv_dma_resv_lock(resv, NULL);
|
||||
|
||||
ret = nv_dma_resv_reserve_fences(resv, 1, false);
|
||||
if (ret == 0) {
|
||||
nv_dma_resv_add_excl_fence(resv, fence);
|
||||
} else {
|
||||
NV_DRM_DEV_LOG_ERR(
|
||||
nv_dev,
|
||||
"Failed to reserve fence. Error code: %d", ret);
|
||||
}
|
||||
|
||||
nv_dma_resv_unlock(resv);
|
||||
|
||||
/* dma_resv_add_excl_fence takes its own reference to the fence. */
|
||||
nv_dma_fence_put(fence);
|
||||
|
||||
fence_context_create_fence_failed:
|
||||
nv_drm_gem_object_unreference_unlocked(&nv_gem_fence_context->base);
|
||||
nv_drm_gem_object_unreference_unlocked(&nv_fence_context->base);
|
||||
|
||||
fence_context_lookup_failed:
|
||||
nv_drm_gem_object_unreference_unlocked(nv_gem);
|
||||
@ -595,6 +591,1224 @@ done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct nv_drm_semsurf_fence {
|
||||
nv_dma_fence_t base;
|
||||
spinlock_t lock;
|
||||
|
||||
/*
|
||||
* When unsignaled, node in the associated fence context's pending fence
|
||||
* list. The list holds a reference to the fence
|
||||
*/
|
||||
struct list_head pending_node;
|
||||
|
||||
#if !defined(NV_DMA_FENCE_OPS_HAS_USE_64BIT_SEQNO)
|
||||
/* 64-bit version of base.seqno on kernels with 32-bit fence seqno */
|
||||
NvU64 wait_value;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Raw absolute kernel time (time domain and scale are treated as opaque)
|
||||
* when this fence times out.
|
||||
*/
|
||||
unsigned long timeout;
|
||||
};
|
||||
|
||||
struct nv_drm_semsurf_fence_callback {
|
||||
struct nv_drm_semsurf_fence_ctx *ctx;
|
||||
nv_drm_work work;
|
||||
NvU64 wait_value;
|
||||
};
|
||||
|
||||
struct nv_drm_sync_fd_wait_data {
|
||||
nv_dma_fence_cb_t dma_fence_cb;
|
||||
struct nv_drm_semsurf_fence_ctx *ctx;
|
||||
nv_drm_work work; /* Deferred second half of fence wait callback */
|
||||
|
||||
/* Could use a lockless list data structure here instead */
|
||||
struct list_head pending_node;
|
||||
|
||||
NvU64 pre_wait_value;
|
||||
NvU64 post_wait_value;
|
||||
};
|
||||
|
||||
struct nv_drm_semsurf_fence_ctx {
|
||||
struct nv_drm_fence_context base;
|
||||
|
||||
/* The NVKMS KAPI reference to the context's semaphore surface */
|
||||
struct NvKmsKapiSemaphoreSurface *pSemSurface;
|
||||
|
||||
/* CPU mapping of the semaphore slot values */
|
||||
union {
|
||||
volatile void *pVoid;
|
||||
volatile NvU32 *p32;
|
||||
volatile NvU64 *p64;
|
||||
} pSemMapping;
|
||||
volatile NvU64 *pMaxSubmittedMapping;
|
||||
|
||||
/* work thread for fence timeouts and waits */
|
||||
nv_drm_workthread worker;
|
||||
|
||||
/* Timeout timer and associated workthread work */
|
||||
nv_drm_timer timer;
|
||||
nv_drm_work timeout_work;
|
||||
|
||||
/* Protects access to everything below */
|
||||
spinlock_t lock;
|
||||
|
||||
/* List of pending fences which are not yet signaled */
|
||||
struct list_head pending_fences;
|
||||
|
||||
/* List of pending fence wait operations */
|
||||
struct list_head pending_waits;
|
||||
|
||||
/*
|
||||
* Tracking data for the single in-flight callback associated with this
|
||||
* context. Either both pointers will be valid, or both will be NULL.
|
||||
*
|
||||
* Note it is not safe to dereference these values outside of the context
|
||||
* lock unless it is certain the associated callback is not yet active,
|
||||
* or has been canceled. Their memory is owned by the callback itself as
|
||||
* soon as it is registered. Subtly, this means these variables can not
|
||||
* be used as output parameters to the function that registers the callback.
|
||||
*/
|
||||
struct {
|
||||
struct nv_drm_semsurf_fence_callback *local;
|
||||
struct NvKmsKapiSemaphoreSurfaceCallback *nvKms;
|
||||
} callback;
|
||||
|
||||
/*
|
||||
* Wait value associated with either the above or a being-registered
|
||||
* callback. May differ from callback->local->wait_value if it is the
|
||||
* latter. Zero if no callback is currently needed.
|
||||
*/
|
||||
NvU64 current_wait_value;
|
||||
};
|
||||
|
||||
static inline struct nv_drm_semsurf_fence_ctx*
|
||||
to_semsurf_fence_ctx(
|
||||
struct nv_drm_fence_context *nv_fence_context
|
||||
)
|
||||
{
|
||||
return container_of(nv_fence_context,
|
||||
struct nv_drm_semsurf_fence_ctx,
|
||||
base);
|
||||
}
|
||||
|
||||
static inline NvU64
|
||||
__nv_drm_get_semsurf_fence_seqno(const struct nv_drm_semsurf_fence *nv_fence)
|
||||
{
|
||||
#if defined(NV_DMA_FENCE_OPS_HAS_USE_64BIT_SEQNO)
|
||||
return nv_fence->base.seqno;
|
||||
#else
|
||||
return nv_fence->wait_value;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef READ_ONCE
|
||||
#define READ_ONCE(x) ACCESS_ONCE(x)
|
||||
#endif
|
||||
|
||||
static inline NvU64
|
||||
__nv_drm_get_semsurf_ctx_seqno(struct nv_drm_semsurf_fence_ctx *ctx)
|
||||
{
|
||||
NvU64 semVal;
|
||||
|
||||
if (ctx->pMaxSubmittedMapping) {
|
||||
/* 32-bit GPU semaphores */
|
||||
NvU64 maxSubmitted = READ_ONCE(*ctx->pMaxSubmittedMapping);
|
||||
|
||||
/*
|
||||
* Must happen after the max submitted read! See
|
||||
* NvTimeSemFermiGetPayload() for full details.
|
||||
*/
|
||||
semVal = READ_ONCE(*ctx->pSemMapping.p32);
|
||||
|
||||
if ((maxSubmitted & 0xFFFFFFFFull) < semVal) {
|
||||
maxSubmitted -= 0x100000000ull;
|
||||
}
|
||||
|
||||
semVal |= (maxSubmitted & 0xffffffff00000000ull);
|
||||
} else {
|
||||
/* 64-bit GPU semaphores */
|
||||
semVal = READ_ONCE(*ctx->pSemMapping.p64);
|
||||
}
|
||||
|
||||
return semVal;
|
||||
}
|
||||
|
||||
static void
|
||||
__nv_drm_semsurf_force_complete_pending(struct nv_drm_semsurf_fence_ctx *ctx)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* No locks are needed for the pending_fences list. This code runs after all
|
||||
* other possible references to the fence context have been removed. The
|
||||
* fences have their own individual locks to protect themselves.
|
||||
*/
|
||||
while (!list_empty(&ctx->pending_fences)) {
|
||||
struct nv_drm_semsurf_fence *nv_fence = list_first_entry(
|
||||
&ctx->pending_fences,
|
||||
typeof(*nv_fence),
|
||||
pending_node);
|
||||
nv_dma_fence_t *fence = &nv_fence->base;
|
||||
|
||||
list_del(&nv_fence->pending_node);
|
||||
|
||||
nv_dma_fence_set_error(fence, -ETIMEDOUT);
|
||||
nv_dma_fence_signal(fence);
|
||||
|
||||
/* Remove the pending list's reference */
|
||||
nv_dma_fence_put(fence);
|
||||
}
|
||||
|
||||
/*
|
||||
* The pending waits are also referenced by the fences they are waiting on,
|
||||
* but those fences are guaranteed to complete in finite time. Just keep the
|
||||
* the context alive until they do so.
|
||||
*/
|
||||
spin_lock_irqsave(&ctx->lock, flags);
|
||||
while (!list_empty(&ctx->pending_waits)) {
|
||||
spin_unlock_irqrestore(&ctx->lock, flags);
|
||||
nv_drm_yield();
|
||||
spin_lock_irqsave(&ctx->lock, flags);
|
||||
}
|
||||
spin_unlock_irqrestore(&ctx->lock, flags);
|
||||
}
|
||||
|
||||
/* Forward declaration */
|
||||
static void
|
||||
__nv_drm_semsurf_ctx_reg_callbacks(struct nv_drm_semsurf_fence_ctx *ctx);
|
||||
|
||||
static void
|
||||
__nv_drm_semsurf_ctx_fence_callback_work(void *data)
|
||||
{
|
||||
struct nv_drm_semsurf_fence_callback *callback = data;
|
||||
|
||||
__nv_drm_semsurf_ctx_reg_callbacks(callback->ctx);
|
||||
|
||||
nv_drm_free(callback);
|
||||
}
|
||||
|
||||
static struct nv_drm_semsurf_fence_callback*
|
||||
__nv_drm_semsurf_new_callback(struct nv_drm_semsurf_fence_ctx *ctx)
|
||||
{
|
||||
struct nv_drm_semsurf_fence_callback *newCallback =
|
||||
nv_drm_calloc(1, sizeof(*newCallback));
|
||||
|
||||
if (!newCallback) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
newCallback->ctx = ctx;
|
||||
nv_drm_workthread_work_init(&newCallback->work,
|
||||
__nv_drm_semsurf_ctx_fence_callback_work,
|
||||
newCallback);
|
||||
|
||||
return newCallback;
|
||||
}
|
||||
|
||||
static void
|
||||
__nv_drm_semsurf_ctx_process_completed(struct nv_drm_semsurf_fence_ctx *ctx,
|
||||
NvU64 *newWaitValueOut,
|
||||
unsigned long *newTimeoutOut)
|
||||
{
|
||||
struct list_head finished;
|
||||
struct list_head timed_out;
|
||||
struct nv_drm_semsurf_fence *nv_fence;
|
||||
nv_dma_fence_t *fence;
|
||||
NvU64 currentSeqno = __nv_drm_get_semsurf_ctx_seqno(ctx);
|
||||
NvU64 fenceSeqno = 0;
|
||||
unsigned long flags;
|
||||
unsigned long fenceTimeout = 0;
|
||||
unsigned long now = nv_drm_timer_now();
|
||||
|
||||
INIT_LIST_HEAD(&finished);
|
||||
INIT_LIST_HEAD(&timed_out);
|
||||
|
||||
spin_lock_irqsave(&ctx->lock, flags);
|
||||
|
||||
while (!list_empty(&ctx->pending_fences)) {
|
||||
nv_fence = list_first_entry(&ctx->pending_fences,
|
||||
typeof(*nv_fence),
|
||||
pending_node);
|
||||
|
||||
fenceSeqno = __nv_drm_get_semsurf_fence_seqno(nv_fence);
|
||||
fenceTimeout = nv_fence->timeout;
|
||||
|
||||
if (fenceSeqno <= currentSeqno) {
|
||||
list_move_tail(&nv_fence->pending_node, &finished);
|
||||
} else if (fenceTimeout <= now) {
|
||||
list_move_tail(&nv_fence->pending_node, &timed_out);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the caller passes non-NULL newWaitValueOut and newTimeoutOut
|
||||
* parameters, it establishes a contract. If the returned values are
|
||||
* non-zero, the caller must attempt to register a callback associated with
|
||||
* the new wait value and reset the context's timer to the specified
|
||||
* timeout.
|
||||
*/
|
||||
if (newWaitValueOut && newTimeoutOut) {
|
||||
if (list_empty(&ctx->pending_fences)) {
|
||||
/* No pending fences, so no waiter is needed. */
|
||||
ctx->current_wait_value = fenceSeqno = 0;
|
||||
fenceTimeout = 0;
|
||||
} else if (fenceSeqno == ctx->current_wait_value) {
|
||||
/*
|
||||
* The context already has a waiter registered, or in the process of
|
||||
* being registered, for this fence. Indicate to the caller no new
|
||||
* waiter registration is needed, and leave the ctx state alone.
|
||||
*/
|
||||
fenceSeqno = 0;
|
||||
fenceTimeout = 0;
|
||||
} else {
|
||||
/* A new waiter must be registered. Prep the context */
|
||||
ctx->current_wait_value = fenceSeqno;
|
||||
}
|
||||
|
||||
*newWaitValueOut = fenceSeqno;
|
||||
*newTimeoutOut = fenceTimeout;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&ctx->lock, flags);
|
||||
|
||||
while (!list_empty(&finished)) {
|
||||
nv_fence = list_first_entry(&finished, typeof(*nv_fence), pending_node);
|
||||
list_del_init(&nv_fence->pending_node);
|
||||
fence = &nv_fence->base;
|
||||
nv_dma_fence_signal(fence);
|
||||
nv_dma_fence_put(fence); /* Drops the pending list's reference */
|
||||
}
|
||||
|
||||
while (!list_empty(&timed_out)) {
|
||||
nv_fence = list_first_entry(&timed_out, typeof(*nv_fence),
|
||||
pending_node);
|
||||
list_del_init(&nv_fence->pending_node);
|
||||
fence = &nv_fence->base;
|
||||
nv_dma_fence_set_error(fence, -ETIMEDOUT);
|
||||
nv_dma_fence_signal(fence);
|
||||
nv_dma_fence_put(fence); /* Drops the pending list's reference */
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
__nv_drm_semsurf_ctx_callback(void *data)
|
||||
{
|
||||
struct nv_drm_semsurf_fence_callback *callback = data;
|
||||
struct nv_drm_semsurf_fence_ctx *ctx = callback->ctx;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ctx->lock, flags);
|
||||
/* If this was the context's currently registered callback, clear it. */
|
||||
if (ctx->callback.local == callback) {
|
||||
ctx->callback.local = NULL;
|
||||
ctx->callback.nvKms = NULL;
|
||||
}
|
||||
/* If storing of this callback may have been pending, prevent it. */
|
||||
if (ctx->current_wait_value == callback->wait_value) {
|
||||
ctx->current_wait_value = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&ctx->lock, flags);
|
||||
|
||||
/*
|
||||
* This is redundant with the __nv_drm_semsurf_ctx_reg_callbacks() call from
|
||||
* __nv_drm_semsurf_ctx_fence_callback_work(), which will be called by the
|
||||
* work enqueued below, but calling it here as well allows unblocking
|
||||
* waiters with less latency.
|
||||
*/
|
||||
__nv_drm_semsurf_ctx_process_completed(ctx, NULL, NULL);
|
||||
|
||||
if (!nv_drm_workthread_add_work(&ctx->worker, &callback->work)) {
|
||||
/*
|
||||
* The context is shutting down. It will force-signal all fences when
|
||||
* doing so, so there's no need for any more callback handling.
|
||||
*/
|
||||
nv_drm_free(callback);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Take spin lock, attempt to stash newNvKmsCallback/newCallback in ctx.
|
||||
* If current_wait_value in fence context != new_wait_value, we raced with
|
||||
* someone registering a newer waiter. Release spin lock, and unregister our
|
||||
* waiter. It isn't needed anymore.
|
||||
*/
|
||||
static bool
|
||||
__nv_drm_semsurf_ctx_store_callback(
|
||||
struct nv_drm_semsurf_fence_ctx *ctx,
|
||||
NvU64 new_wait_value,
|
||||
struct NvKmsKapiSemaphoreSurfaceCallback *newNvKmsCallback,
|
||||
struct nv_drm_semsurf_fence_callback *newCallback)
|
||||
{
|
||||
struct nv_drm_device *nv_dev = ctx->base.nv_dev;
|
||||
struct NvKmsKapiSemaphoreSurfaceCallback *oldNvKmsCallback;
|
||||
struct nv_drm_semsurf_fence_callback *oldCallback = NULL;
|
||||
NvU64 oldWaitValue;
|
||||
unsigned long flags;
|
||||
bool installed = false;
|
||||
|
||||
spin_lock_irqsave(&ctx->lock, flags);
|
||||
if (ctx->current_wait_value == new_wait_value) {
|
||||
oldCallback = ctx->callback.local;
|
||||
oldNvKmsCallback = ctx->callback.nvKms;
|
||||
oldWaitValue = oldCallback ? oldCallback->wait_value : 0;
|
||||
ctx->callback.local = newCallback;
|
||||
ctx->callback.nvKms = newNvKmsCallback;
|
||||
installed = true;
|
||||
}
|
||||
spin_unlock_irqrestore(&ctx->lock, flags);
|
||||
|
||||
if (oldCallback) {
|
||||
if (nvKms->unregisterSemaphoreSurfaceCallback(nv_dev->pDevice,
|
||||
ctx->pSemSurface,
|
||||
ctx->base.fenceSemIndex,
|
||||
oldWaitValue,
|
||||
oldNvKmsCallback)) {
|
||||
/*
|
||||
* The old callback was successfully canceled, and its NVKMS and RM
|
||||
* resources have been freed. Free its local tracking data.
|
||||
*/
|
||||
nv_drm_free(oldCallback);
|
||||
} else {
|
||||
/*
|
||||
* The new callback is already running. It will do no harm, and free
|
||||
* itself.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
return installed;
|
||||
}
|
||||
|
||||
/*
|
||||
* Processes completed fences and registers an RM callback and a timeout timer
|
||||
* for the next incomplete fence, if any. To avoid calling in to RM while
|
||||
* holding a spinlock, this is done in a loop until the state settles.
|
||||
*
|
||||
* Can NOT be called from in an atomic context/interrupt handler.
|
||||
*/
|
||||
static void
|
||||
__nv_drm_semsurf_ctx_reg_callbacks(struct nv_drm_semsurf_fence_ctx *ctx)
|
||||
|
||||
{
|
||||
struct nv_drm_device *nv_dev = ctx->base.nv_dev;
|
||||
struct nv_drm_semsurf_fence_callback *newCallback =
|
||||
__nv_drm_semsurf_new_callback(ctx);
|
||||
struct NvKmsKapiSemaphoreSurfaceCallback *newNvKmsCallback;
|
||||
NvU64 newWaitValue;
|
||||
unsigned long newTimeout;
|
||||
NvKmsKapiRegisterWaiterResult kapiRet;
|
||||
|
||||
if (!newCallback) {
|
||||
NV_DRM_DEV_LOG_ERR(
|
||||
nv_dev,
|
||||
"Failed to allocate new fence signal callback data");
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
/*
|
||||
* Process any completed or timed out fences. This returns the wait
|
||||
* value and timeout of the first remaining pending fence, or 0/0
|
||||
* if no pending fences remain. It will also tag the context as
|
||||
* waiting for the value returned.
|
||||
*/
|
||||
__nv_drm_semsurf_ctx_process_completed(ctx,
|
||||
&newWaitValue,
|
||||
&newTimeout);
|
||||
|
||||
if (newWaitValue == 0) {
|
||||
/* No fences remain, so no callback is needed. */
|
||||
nv_drm_free(newCallback);
|
||||
newCallback = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
newCallback->wait_value = newWaitValue;
|
||||
|
||||
/*
|
||||
* Attempt to register a callback for the remaining fences. Note this
|
||||
* code may be running concurrently in multiple places, attempting to
|
||||
* register a callback for the same value, a value greater than
|
||||
* newWaitValue if more fences have since completed, or a value less
|
||||
* than newWaitValue if new fences have been created tracking lower
|
||||
* values than the previously lowest pending one. Hence, even if this
|
||||
* registration succeeds, the callback may be discarded
|
||||
*/
|
||||
kapiRet =
|
||||
nvKms->registerSemaphoreSurfaceCallback(nv_dev->pDevice,
|
||||
ctx->pSemSurface,
|
||||
__nv_drm_semsurf_ctx_callback,
|
||||
newCallback,
|
||||
ctx->base.fenceSemIndex,
|
||||
newWaitValue,
|
||||
0,
|
||||
&newNvKmsCallback);
|
||||
} while (kapiRet == NVKMS_KAPI_REG_WAITER_ALREADY_SIGNALLED);
|
||||
|
||||
/* Can't deref newCallback at this point unless kapiRet indicates failure */
|
||||
|
||||
if (kapiRet != NVKMS_KAPI_REG_WAITER_SUCCESS) {
|
||||
/*
|
||||
* This is expected if another thread concurrently registered a callback
|
||||
* for the same value, which is fine. That thread's callback will do the
|
||||
* same work this thread's would have. Clean this one up and return.
|
||||
*
|
||||
* Another possibility is that an allocation or some other low-level
|
||||
* operation that can spuriously fail has caused this failure, or of
|
||||
* course a bug resulting in invalid usage of the
|
||||
* registerSemaphoreSurfaceCallback() API. There is no good way to
|
||||
* handle such failures, so the fence timeout will be relied upon to
|
||||
* guarantee forward progress in those cases.
|
||||
*/
|
||||
nv_drm_free(newCallback);
|
||||
return;
|
||||
}
|
||||
|
||||
nv_drm_mod_timer(&ctx->timer, newTimeout);
|
||||
|
||||
if (!__nv_drm_semsurf_ctx_store_callback(ctx,
|
||||
newWaitValue,
|
||||
newNvKmsCallback,
|
||||
newCallback)) {
|
||||
/*
|
||||
* Another thread registered a callback for a different value before
|
||||
* this thread's callback could be stored in the context, or the
|
||||
* callback is already running. That's OK. One of the following is true:
|
||||
*
|
||||
* -A new fence with a lower value has been registered, and the callback
|
||||
* associated with that fence is now active and associated with the
|
||||
* context.
|
||||
*
|
||||
* -This fence has already completed, and a new callback associated with
|
||||
* a higher value has been registered and associated with the context.
|
||||
* This lower-value callback is no longer needed, as any fences
|
||||
* associated with it must have been marked completed before
|
||||
* registering the higher-value callback.
|
||||
*
|
||||
* -The callback started running and cleared ctx->current_wait_value
|
||||
* before the callback could be stored in the context. Work to signal
|
||||
* the fence is now pending.
|
||||
*
|
||||
* Hence, it is safe to request cancellation of the callback and free
|
||||
* the associated data if cancellation succeeds.
|
||||
*/
|
||||
if (nvKms->unregisterSemaphoreSurfaceCallback(nv_dev->pDevice,
|
||||
ctx->pSemSurface,
|
||||
ctx->base.fenceSemIndex,
|
||||
newWaitValue,
|
||||
newNvKmsCallback)) {
|
||||
/* RM callback successfully canceled. Free local tracking data */
|
||||
nv_drm_free(newCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void __nv_drm_semsurf_fence_ctx_destroy(
|
||||
struct nv_drm_fence_context *nv_fence_context)
|
||||
{
|
||||
struct nv_drm_device *nv_dev = nv_fence_context->nv_dev;
|
||||
struct nv_drm_semsurf_fence_ctx *ctx =
|
||||
to_semsurf_fence_ctx(nv_fence_context);
|
||||
struct NvKmsKapiSemaphoreSurfaceCallback *pendingNvKmsCallback;
|
||||
NvU64 pendingWaitValue;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* The workthread must be shut down before the timer is stopped to ensure
|
||||
* the timer does not queue work that restarts itself.
|
||||
*/
|
||||
nv_drm_workthread_shutdown(&ctx->worker);
|
||||
|
||||
nv_drm_del_timer_sync(&ctx->timer);
|
||||
|
||||
/*
|
||||
* The semaphore surface could still be sending callbacks, so it is still
|
||||
* not safe to dereference the ctx->callback pointers. However,
|
||||
* unregistering a callback via its handle is safe, as that code in NVKMS
|
||||
* takes care to avoid dereferencing the handle until it knows the callback
|
||||
* has been canceled in RM. This unregistration must be done to ensure the
|
||||
* callback data is not leaked in NVKMS if it is still pending, as freeing
|
||||
* the semaphore surface only cleans up RM's callback data.
|
||||
*/
|
||||
spin_lock_irqsave(&ctx->lock, flags);
|
||||
pendingNvKmsCallback = ctx->callback.nvKms;
|
||||
pendingWaitValue = ctx->callback.local ?
|
||||
ctx->callback.local->wait_value : 0;
|
||||
spin_unlock_irqrestore(&ctx->lock, flags);
|
||||
|
||||
if (pendingNvKmsCallback) {
|
||||
WARN_ON(pendingWaitValue == 0);
|
||||
nvKms->unregisterSemaphoreSurfaceCallback(nv_dev->pDevice,
|
||||
ctx->pSemSurface,
|
||||
ctx->base.fenceSemIndex,
|
||||
pendingWaitValue,
|
||||
pendingNvKmsCallback);
|
||||
}
|
||||
|
||||
nvKms->freeSemaphoreSurface(nv_dev->pDevice, ctx->pSemSurface);
|
||||
|
||||
/*
|
||||
* Now that the semaphore surface, the timer, and the workthread are gone:
|
||||
*
|
||||
* -No more RM/NVKMS callbacks will arrive, nor are any in progress. Freeing
|
||||
* the semaphore surface cancels all its callbacks associated with this
|
||||
* instance of it, and idles any pending callbacks.
|
||||
*
|
||||
* -No more timer callbacks will arrive, nor are any in flight.
|
||||
*
|
||||
* -The workthread has been idled and is no longer running.
|
||||
*
|
||||
* Further, given the destructor is running, no other references to the
|
||||
* fence context exist, so this code can assume no concurrent access to the
|
||||
* fence context's data will happen from here on out.
|
||||
*/
|
||||
|
||||
if (ctx->callback.local) {
|
||||
nv_drm_free(ctx->callback.local);
|
||||
ctx->callback.local = NULL;
|
||||
ctx->callback.nvKms = NULL;
|
||||
}
|
||||
|
||||
__nv_drm_semsurf_force_complete_pending(ctx);
|
||||
|
||||
nv_drm_free(nv_fence_context);
|
||||
}
|
||||
|
||||
static void
|
||||
__nv_drm_semsurf_ctx_timeout_work(void *data)
|
||||
{
|
||||
struct nv_drm_semsurf_fence_ctx *ctx = data;
|
||||
|
||||
__nv_drm_semsurf_ctx_reg_callbacks(ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
__nv_drm_semsurf_ctx_timeout_callback(nv_drm_timer *timer)
|
||||
{
|
||||
struct nv_drm_semsurf_fence_ctx *ctx =
|
||||
container_of(timer, typeof(*ctx), timer);
|
||||
|
||||
/*
|
||||
* Schedule work to register new waiter & timer on a worker thread.
|
||||
*
|
||||
* It does not matter if this fails. There are two possible failure cases:
|
||||
*
|
||||
* - ctx->timeout_work is already scheduled. That existing scheduled work
|
||||
* will do at least as much as work scheduled right now and executed
|
||||
* immediately, which is sufficient.
|
||||
*
|
||||
* - The context is shutting down. In this case, all fences will be force-
|
||||
* signalled, so no further callbacks or timeouts are needed.
|
||||
*
|
||||
* Note this work may schedule a new timeout timer. To ensure that doesn't
|
||||
* happen while context shutdown is shutting down and idling the timer, the
|
||||
* the worker thread must be shut down before the timer is stopped.
|
||||
*/
|
||||
nv_drm_workthread_add_work(&ctx->worker, &ctx->timeout_work);
|
||||
}
|
||||
|
||||
static struct nv_drm_fence_context_ops
|
||||
nv_drm_semsurf_fence_ctx_ops = {
|
||||
.destroy = __nv_drm_semsurf_fence_ctx_destroy,
|
||||
};
|
||||
|
||||
static struct nv_drm_semsurf_fence_ctx*
|
||||
__nv_drm_semsurf_fence_ctx_new(
|
||||
struct nv_drm_device *nv_dev,
|
||||
struct drm_nvidia_semsurf_fence_ctx_create_params *p
|
||||
)
|
||||
{
|
||||
struct nv_drm_semsurf_fence_ctx *ctx;
|
||||
struct NvKmsKapiSemaphoreSurface *pSemSurface;
|
||||
uint8_t *semMapping;
|
||||
uint8_t *maxSubmittedMapping;
|
||||
char worker_name[20+16+1]; /* strlen(nvidia-drm/timeline-) + 16 for %llx + NUL */
|
||||
|
||||
pSemSurface = nvKms->importSemaphoreSurface(nv_dev->pDevice,
|
||||
p->nvkms_params_ptr,
|
||||
p->nvkms_params_size,
|
||||
(void **)&semMapping,
|
||||
(void **)&maxSubmittedMapping);
|
||||
if (!pSemSurface) {
|
||||
NV_DRM_DEV_LOG_ERR(
|
||||
nv_dev,
|
||||
"Failed to import semaphore surface");
|
||||
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a fence context object and initialize it.
|
||||
*/
|
||||
|
||||
if ((ctx = nv_drm_calloc(1, sizeof(*ctx))) == NULL) {
|
||||
goto failed_alloc_fence_context;
|
||||
}
|
||||
|
||||
semMapping += (p->index * nv_dev->semsurf_stride);
|
||||
if (maxSubmittedMapping) {
|
||||
maxSubmittedMapping += (p->index * nv_dev->semsurf_stride) +
|
||||
nv_dev->semsurf_max_submitted_offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* nv_dma_fence_context_alloc() cannot fail, so we do not need
|
||||
* to check a return value.
|
||||
*/
|
||||
|
||||
*ctx = (struct nv_drm_semsurf_fence_ctx) {
|
||||
.base.ops = &nv_drm_semsurf_fence_ctx_ops,
|
||||
.base.nv_dev = nv_dev,
|
||||
.base.context = nv_dma_fence_context_alloc(1),
|
||||
.base.fenceSemIndex = p->index,
|
||||
.pSemSurface = pSemSurface,
|
||||
.pSemMapping.pVoid = semMapping,
|
||||
.pMaxSubmittedMapping = (volatile NvU64 *)maxSubmittedMapping,
|
||||
.callback.local = NULL,
|
||||
.callback.nvKms = NULL,
|
||||
.current_wait_value = 0,
|
||||
};
|
||||
|
||||
spin_lock_init(&ctx->lock);
|
||||
INIT_LIST_HEAD(&ctx->pending_fences);
|
||||
INIT_LIST_HEAD(&ctx->pending_waits);
|
||||
|
||||
sprintf(worker_name, "nvidia-drm/timeline-%llx",
|
||||
(long long unsigned)ctx->base.context);
|
||||
if (!nv_drm_workthread_init(&ctx->worker, worker_name)) {
|
||||
goto failed_alloc_worker;
|
||||
}
|
||||
|
||||
nv_drm_workthread_work_init(&ctx->timeout_work,
|
||||
__nv_drm_semsurf_ctx_timeout_work,
|
||||
ctx);
|
||||
|
||||
nv_drm_timer_setup(&ctx->timer, __nv_drm_semsurf_ctx_timeout_callback);
|
||||
|
||||
return ctx;
|
||||
|
||||
failed_alloc_worker:
|
||||
nv_drm_free(ctx);
|
||||
|
||||
failed_alloc_fence_context:
|
||||
nvKms->freeSemaphoreSurface(nv_dev->pDevice, pSemSurface);
|
||||
|
||||
failed:
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
int nv_drm_semsurf_fence_ctx_create_ioctl(struct drm_device *dev,
|
||||
void *data,
|
||||
struct drm_file *filep)
|
||||
{
|
||||
struct nv_drm_device *nv_dev = to_nv_device(dev);
|
||||
struct drm_nvidia_semsurf_fence_ctx_create_params *p = data;
|
||||
struct nv_drm_semsurf_fence_ctx *ctx;
|
||||
int err;
|
||||
|
||||
if (p->__pad != 0) {
|
||||
NV_DRM_DEV_LOG_ERR(nv_dev, "Padding fields must be zeroed");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ctx = __nv_drm_semsurf_fence_ctx_new(nv_dev, p);
|
||||
|
||||
if (!ctx) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
err = __nv_drm_fence_context_gem_init(dev, &ctx->base, &p->handle, filep);
|
||||
|
||||
if (err) {
|
||||
__nv_drm_semsurf_fence_ctx_destroy(&ctx->base);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline struct nv_drm_semsurf_fence*
|
||||
to_nv_drm_semsurf_fence(nv_dma_fence_t *fence)
|
||||
{
|
||||
return container_of(fence, struct nv_drm_semsurf_fence, base);
|
||||
}
|
||||
|
||||
static const char*
|
||||
__nv_drm_semsurf_fence_op_get_timeline_name(nv_dma_fence_t *fence)
|
||||
{
|
||||
return "nvidia.semaphore_surface";
|
||||
}
|
||||
|
||||
static bool
|
||||
__nv_drm_semsurf_fence_op_enable_signaling(nv_dma_fence_t *fence)
|
||||
{
|
||||
// DO NOTHING - Could defer RM callback registration until this point
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
__nv_drm_semsurf_fence_op_release(nv_dma_fence_t *fence)
|
||||
{
|
||||
struct nv_drm_semsurf_fence *nv_fence =
|
||||
to_nv_drm_semsurf_fence(fence);
|
||||
|
||||
nv_drm_free(nv_fence);
|
||||
}
|
||||
|
||||
static const nv_dma_fence_ops_t nv_drm_semsurf_fence_ops = {
|
||||
.get_driver_name = nv_drm_gem_fence_op_get_driver_name,
|
||||
.get_timeline_name = __nv_drm_semsurf_fence_op_get_timeline_name,
|
||||
.enable_signaling = __nv_drm_semsurf_fence_op_enable_signaling,
|
||||
.release = __nv_drm_semsurf_fence_op_release,
|
||||
.wait = nv_dma_fence_default_wait,
|
||||
#if defined(NV_DMA_FENCE_OPS_HAS_USE_64BIT_SEQNO)
|
||||
.use_64bit_seqno = true,
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* Completes fence initialization, places a new reference to the fence in the
|
||||
* context's pending fence list, and updates/registers any RM callbacks and
|
||||
* timeout timers if necessary.
|
||||
*
|
||||
* Can NOT be called from in an atomic context/interrupt handler.
|
||||
*/
|
||||
static void
|
||||
__nv_drm_semsurf_ctx_add_pending(struct nv_drm_semsurf_fence_ctx *ctx,
|
||||
struct nv_drm_semsurf_fence *nv_fence,
|
||||
NvU64 timeoutMS)
|
||||
{
|
||||
struct list_head *pending;
|
||||
unsigned long flags;
|
||||
|
||||
if (timeoutMS > NV_DRM_SEMAPHORE_SURFACE_FENCE_MAX_TIMEOUT_MS) {
|
||||
timeoutMS = NV_DRM_SEMAPHORE_SURFACE_FENCE_MAX_TIMEOUT_MS;
|
||||
}
|
||||
|
||||
/* Add a reference to the fence for the list */
|
||||
nv_dma_fence_get(&nv_fence->base);
|
||||
INIT_LIST_HEAD(&nv_fence->pending_node);
|
||||
|
||||
nv_fence->timeout = nv_drm_timeout_from_ms(timeoutMS);
|
||||
|
||||
spin_lock_irqsave(&ctx->lock, flags);
|
||||
|
||||
list_for_each(pending, &ctx->pending_fences) {
|
||||
struct nv_drm_semsurf_fence *pending_fence =
|
||||
list_entry(pending, typeof(*pending_fence), pending_node);
|
||||
if (__nv_drm_get_semsurf_fence_seqno(pending_fence) >
|
||||
__nv_drm_get_semsurf_fence_seqno(nv_fence)) {
|
||||
/* Inserts 'nv_fence->pending_node' before 'pending' */
|
||||
list_add_tail(&nv_fence->pending_node, pending);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (list_empty(&nv_fence->pending_node)) {
|
||||
/*
|
||||
* Inserts 'fence->pending_node' at the end of 'ctx->pending_fences',
|
||||
* or as the head if the list is empty
|
||||
*/
|
||||
list_add_tail(&nv_fence->pending_node, &ctx->pending_fences);
|
||||
}
|
||||
|
||||
/* Fence is live starting... now! */
|
||||
spin_unlock_irqrestore(&ctx->lock, flags);
|
||||
|
||||
/* Register new wait and timeout callbacks, if necessary */
|
||||
__nv_drm_semsurf_ctx_reg_callbacks(ctx);
|
||||
}
|
||||
|
||||
static nv_dma_fence_t *__nv_drm_semsurf_fence_ctx_create_fence(
|
||||
struct nv_drm_device *nv_dev,
|
||||
struct nv_drm_semsurf_fence_ctx *ctx,
|
||||
NvU64 wait_value,
|
||||
NvU64 timeout_value_ms)
|
||||
{
|
||||
struct nv_drm_semsurf_fence *nv_fence;
|
||||
nv_dma_fence_t *fence;
|
||||
int ret = 0;
|
||||
|
||||
if (timeout_value_ms == 0 ||
|
||||
timeout_value_ms > NV_DRM_SEMAPHORE_SURFACE_FENCE_MAX_TIMEOUT_MS) {
|
||||
timeout_value_ms = NV_DRM_SEMAPHORE_SURFACE_FENCE_MAX_TIMEOUT_MS;
|
||||
}
|
||||
|
||||
if ((nv_fence = nv_drm_calloc(1, sizeof(*nv_fence))) == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fence = &nv_fence->base;
|
||||
spin_lock_init(&nv_fence->lock);
|
||||
#if !defined(NV_DMA_FENCE_OPS_HAS_USE_64BIT_SEQNO)
|
||||
nv_fence->wait_value = wait_value;
|
||||
#endif
|
||||
|
||||
/* Initializes the fence with one reference (for the caller) */
|
||||
nv_dma_fence_init(fence, &nv_drm_semsurf_fence_ops,
|
||||
&nv_fence->lock,
|
||||
ctx->base.context, wait_value);
|
||||
|
||||
__nv_drm_semsurf_ctx_add_pending(ctx, nv_fence, timeout_value_ms);
|
||||
|
||||
out:
|
||||
/* Returned fence has one reference reserved for the caller. */
|
||||
return ret != 0 ? ERR_PTR(ret) : &nv_fence->base;
|
||||
}
|
||||
|
||||
int nv_drm_semsurf_fence_create_ioctl(struct drm_device *dev,
|
||||
void *data,
|
||||
struct drm_file *filep)
|
||||
{
|
||||
struct nv_drm_device *nv_dev = to_nv_device(dev);
|
||||
struct drm_nvidia_semsurf_fence_create_params *p = data;
|
||||
struct nv_drm_fence_context *nv_fence_context;
|
||||
nv_dma_fence_t *fence;
|
||||
int ret = -EINVAL;
|
||||
int fd;
|
||||
|
||||
if (p->__pad != 0) {
|
||||
NV_DRM_DEV_LOG_ERR(nv_dev, "Padding fields must be zeroed");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ((nv_fence_context = __nv_drm_fence_context_lookup(
|
||||
nv_dev->dev,
|
||||
filep,
|
||||
p->fence_context_handle)) == NULL) {
|
||||
NV_DRM_DEV_LOG_ERR(
|
||||
nv_dev,
|
||||
"Failed to lookup gem object for fence context: 0x%08x",
|
||||
p->fence_context_handle);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (nv_fence_context->ops != &nv_drm_semsurf_fence_ctx_ops) {
|
||||
NV_DRM_DEV_LOG_ERR(
|
||||
nv_dev,
|
||||
"Wrong fence context type: 0x%08x",
|
||||
p->fence_context_handle);
|
||||
|
||||
goto fence_context_create_fence_failed;
|
||||
}
|
||||
|
||||
fence = __nv_drm_semsurf_fence_ctx_create_fence(
|
||||
nv_dev,
|
||||
to_semsurf_fence_ctx(nv_fence_context),
|
||||
p->wait_value,
|
||||
p->timeout_value_ms);
|
||||
|
||||
if (IS_ERR(fence)) {
|
||||
ret = PTR_ERR(fence);
|
||||
|
||||
NV_DRM_DEV_LOG_ERR(
|
||||
nv_dev,
|
||||
"Failed to allocate fence: 0x%08x", p->fence_context_handle);
|
||||
|
||||
goto fence_context_create_fence_failed;
|
||||
}
|
||||
|
||||
if ((fd = nv_drm_create_sync_file(fence)) < 0) {
|
||||
ret = fd;
|
||||
|
||||
NV_DRM_DEV_LOG_ERR(
|
||||
nv_dev,
|
||||
"Failed to create sync file from fence on ctx 0x%08x",
|
||||
p->fence_context_handle);
|
||||
|
||||
goto fence_context_create_sync_failed;
|
||||
}
|
||||
|
||||
p->fd = fd;
|
||||
ret = 0;
|
||||
|
||||
fence_context_create_sync_failed:
|
||||
/*
|
||||
* Release this function's reference to the fence. If successful, the sync
|
||||
* FD will still hold a reference, and the pending list (if the fence hasn't
|
||||
* already been signaled) will also retain a reference.
|
||||
*/
|
||||
nv_dma_fence_put(fence);
|
||||
|
||||
fence_context_create_fence_failed:
|
||||
nv_drm_gem_object_unreference_unlocked(&nv_fence_context->base);
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
__nv_drm_semsurf_free_wait_data(struct nv_drm_sync_fd_wait_data *wait_data)
|
||||
{
|
||||
struct nv_drm_semsurf_fence_ctx *ctx = wait_data->ctx;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ctx->lock, flags);
|
||||
list_del(&wait_data->pending_node);
|
||||
spin_unlock_irqrestore(&ctx->lock, flags);
|
||||
|
||||
nv_drm_free(wait_data);
|
||||
}
|
||||
|
||||
static void
|
||||
__nv_drm_semsurf_wait_fence_work_cb
|
||||
(
|
||||
void *arg
|
||||
)
|
||||
{
|
||||
struct nv_drm_sync_fd_wait_data *wait_data = arg;
|
||||
struct nv_drm_semsurf_fence_ctx *ctx = wait_data->ctx;
|
||||
struct nv_drm_device *nv_dev = ctx->base.nv_dev;
|
||||
NvKmsKapiRegisterWaiterResult ret;
|
||||
|
||||
/*
|
||||
* Note this command applies "newValue" immediately if the semaphore has
|
||||
* already reached "waitValue." It only returns NVKMS_KAPI_ALREADY_SIGNALLED
|
||||
* if a separate notification was requested as well.
|
||||
*/
|
||||
ret = nvKms->registerSemaphoreSurfaceCallback(nv_dev->pDevice,
|
||||
ctx->pSemSurface,
|
||||
NULL,
|
||||
NULL,
|
||||
ctx->base.fenceSemIndex,
|
||||
wait_data->pre_wait_value,
|
||||
wait_data->post_wait_value,
|
||||
NULL);
|
||||
|
||||
if (ret != NVKMS_KAPI_REG_WAITER_SUCCESS) {
|
||||
NV_DRM_DEV_LOG_ERR(nv_dev,
|
||||
"Failed to register auto-value-update on pre-wait value for sync FD semaphore surface");
|
||||
}
|
||||
|
||||
__nv_drm_semsurf_free_wait_data(wait_data);
|
||||
}
|
||||
|
||||
static void
|
||||
__nv_drm_semsurf_wait_fence_cb
|
||||
(
|
||||
nv_dma_fence_t *fence,
|
||||
nv_dma_fence_cb_t *cb
|
||||
)
|
||||
{
|
||||
struct nv_drm_sync_fd_wait_data *wait_data =
|
||||
container_of(cb, typeof(*wait_data), dma_fence_cb);
|
||||
struct nv_drm_semsurf_fence_ctx *ctx = wait_data->ctx;
|
||||
|
||||
/*
|
||||
* Defer registering the wait with RM to a worker thread, since
|
||||
* this function may be called in interrupt context, which
|
||||
* could mean arriving here directly from RM's top/bottom half
|
||||
* handler when the fence being waited on came from an RM-managed GPU.
|
||||
*/
|
||||
if (!nv_drm_workthread_add_work(&ctx->worker, &wait_data->work)) {
|
||||
/*
|
||||
* The context is shutting down. RM would likely just drop
|
||||
* the wait anyway as part of that, so do nothing. Either the
|
||||
* client is exiting uncleanly, or it is a bug in the client
|
||||
* in that it didn't consume its wait before destroying the
|
||||
* fence context used to instantiate it.
|
||||
*/
|
||||
__nv_drm_semsurf_free_wait_data(wait_data);
|
||||
}
|
||||
|
||||
/* Don't need to reference the fence anymore, just the fence context. */
|
||||
nv_dma_fence_put(fence);
|
||||
}
|
||||
|
||||
int nv_drm_semsurf_fence_wait_ioctl(struct drm_device *dev,
|
||||
void *data,
|
||||
struct drm_file *filep)
|
||||
{
|
||||
struct nv_drm_device *nv_dev = to_nv_device(dev);
|
||||
struct drm_nvidia_semsurf_fence_wait_params *p = data;
|
||||
struct nv_drm_fence_context *nv_fence_context;
|
||||
struct nv_drm_semsurf_fence_ctx *ctx;
|
||||
struct nv_drm_sync_fd_wait_data *wait_data = NULL;
|
||||
nv_dma_fence_t *fence;
|
||||
unsigned long flags;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (p->pre_wait_value >= p->post_wait_value) {
|
||||
NV_DRM_DEV_LOG_ERR(
|
||||
nv_dev,
|
||||
"Non-monotonic wait values specified to fence wait: 0x%llu, 0x%llu",
|
||||
p->pre_wait_value, p->post_wait_value);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ((nv_fence_context = __nv_drm_fence_context_lookup(
|
||||
nv_dev->dev,
|
||||
filep,
|
||||
p->fence_context_handle)) == NULL) {
|
||||
NV_DRM_DEV_LOG_ERR(
|
||||
nv_dev,
|
||||
"Failed to lookup gem object for fence context: 0x%08x",
|
||||
p->fence_context_handle);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (nv_fence_context->ops != &nv_drm_semsurf_fence_ctx_ops) {
|
||||
NV_DRM_DEV_LOG_ERR(
|
||||
nv_dev,
|
||||
"Wrong fence context type: 0x%08x",
|
||||
p->fence_context_handle);
|
||||
|
||||
goto fence_context_sync_lookup_failed;
|
||||
}
|
||||
|
||||
ctx = to_semsurf_fence_ctx(nv_fence_context);
|
||||
|
||||
wait_data = nv_drm_calloc(1, sizeof(*wait_data));
|
||||
|
||||
if (!wait_data) {
|
||||
NV_DRM_DEV_LOG_ERR(
|
||||
nv_dev,
|
||||
"Failed to allocate callback data for sync FD wait: %d", p->fd);
|
||||
|
||||
goto fence_context_sync_lookup_failed;
|
||||
}
|
||||
|
||||
fence = nv_drm_sync_file_get_fence(p->fd);
|
||||
|
||||
if (!fence) {
|
||||
NV_DRM_DEV_LOG_ERR(
|
||||
nv_dev,
|
||||
"Attempt to wait on invalid sync FD: %d", p->fd);
|
||||
|
||||
goto fence_context_sync_lookup_failed;
|
||||
}
|
||||
|
||||
wait_data->ctx = ctx;
|
||||
wait_data->pre_wait_value = p->pre_wait_value;
|
||||
wait_data->post_wait_value = p->post_wait_value;
|
||||
nv_drm_workthread_work_init(&wait_data->work,
|
||||
__nv_drm_semsurf_wait_fence_work_cb,
|
||||
wait_data);
|
||||
|
||||
spin_lock_irqsave(&ctx->lock, flags);
|
||||
list_add(&wait_data->pending_node, &ctx->pending_waits);
|
||||
spin_unlock_irqrestore(&ctx->lock, flags);
|
||||
|
||||
ret = nv_dma_fence_add_callback(fence,
|
||||
&wait_data->dma_fence_cb,
|
||||
__nv_drm_semsurf_wait_fence_cb);
|
||||
|
||||
if (ret) {
|
||||
if (ret == -ENOENT) {
|
||||
/* The fence is already signaled */
|
||||
} else {
|
||||
NV_DRM_LOG_ERR(
|
||||
"Failed to add dma_fence callback. Signaling early!");
|
||||
/* Proceed as if the fence wait succeeded */
|
||||
}
|
||||
|
||||
/* Execute second half of wait immediately, avoiding the worker thread */
|
||||
nv_dma_fence_put(fence);
|
||||
__nv_drm_semsurf_wait_fence_work_cb(wait_data);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
fence_context_sync_lookup_failed:
|
||||
if (ret && wait_data) {
|
||||
/*
|
||||
* Do not use __nv_drm_semsurf_free_wait_data() here, as the wait_data
|
||||
* has not been added to the pending list yet.
|
||||
*/
|
||||
nv_drm_free(wait_data);
|
||||
}
|
||||
|
||||
nv_drm_gem_object_unreference_unlocked(&nv_fence_context->base);
|
||||
|
||||
done:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nv_drm_semsurf_fence_attach_ioctl(struct drm_device *dev,
|
||||
void *data,
|
||||
struct drm_file *filep)
|
||||
{
|
||||
struct nv_drm_device *nv_dev = to_nv_device(dev);
|
||||
struct drm_nvidia_semsurf_fence_attach_params *p = data;
|
||||
struct nv_drm_gem_object *nv_gem = NULL;
|
||||
struct nv_drm_fence_context *nv_fence_context = NULL;
|
||||
nv_dma_fence_t *fence;
|
||||
int ret = -EINVAL;
|
||||
|
||||
nv_gem = nv_drm_gem_object_lookup(nv_dev->dev, filep, p->handle);
|
||||
|
||||
if (!nv_gem) {
|
||||
NV_DRM_DEV_LOG_ERR(
|
||||
nv_dev,
|
||||
"Failed to lookup gem object for fence attach: 0x%08x",
|
||||
p->handle);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
nv_fence_context = __nv_drm_fence_context_lookup(
|
||||
nv_dev->dev,
|
||||
filep,
|
||||
p->fence_context_handle);
|
||||
|
||||
if (!nv_fence_context) {
|
||||
NV_DRM_DEV_LOG_ERR(
|
||||
nv_dev,
|
||||
"Failed to lookup gem object for fence context: 0x%08x",
|
||||
p->fence_context_handle);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (nv_fence_context->ops != &nv_drm_semsurf_fence_ctx_ops) {
|
||||
NV_DRM_DEV_LOG_ERR(
|
||||
nv_dev,
|
||||
"Wrong fence context type: 0x%08x",
|
||||
p->fence_context_handle);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
fence = __nv_drm_semsurf_fence_ctx_create_fence(
|
||||
nv_dev,
|
||||
to_semsurf_fence_ctx(nv_fence_context),
|
||||
p->wait_value,
|
||||
p->timeout_value_ms);
|
||||
|
||||
if (IS_ERR(fence)) {
|
||||
ret = PTR_ERR(fence);
|
||||
|
||||
NV_DRM_DEV_LOG_ERR(
|
||||
nv_dev,
|
||||
"Failed to allocate fence: 0x%08x", p->handle);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = __nv_drm_gem_attach_fence(nv_gem, fence, p->shared);
|
||||
|
||||
nv_dma_fence_put(fence);
|
||||
|
||||
done:
|
||||
if (nv_fence_context) {
|
||||
nv_drm_gem_object_unreference_unlocked(&nv_fence_context->base);
|
||||
}
|
||||
|
||||
if (nv_gem) {
|
||||
nv_drm_gem_object_unreference_unlocked(nv_gem);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* NV_DRM_FENCE_AVAILABLE */
|
||||
|
||||
#endif /* NV_DRM_AVAILABLE */
|
||||
|
@ -41,6 +41,22 @@ int nv_drm_prime_fence_context_create_ioctl(struct drm_device *dev,
|
||||
int nv_drm_gem_prime_fence_attach_ioctl(struct drm_device *dev,
|
||||
void *data, struct drm_file *filep);
|
||||
|
||||
int nv_drm_semsurf_fence_ctx_create_ioctl(struct drm_device *dev,
|
||||
void *data,
|
||||
struct drm_file *filep);
|
||||
|
||||
int nv_drm_semsurf_fence_create_ioctl(struct drm_device *dev,
|
||||
void *data,
|
||||
struct drm_file *filep);
|
||||
|
||||
int nv_drm_semsurf_fence_wait_ioctl(struct drm_device *dev,
|
||||
void *data,
|
||||
struct drm_file *filep);
|
||||
|
||||
int nv_drm_semsurf_fence_attach_ioctl(struct drm_device *dev,
|
||||
void *data,
|
||||
struct drm_file *filep);
|
||||
|
||||
#endif /* NV_DRM_FENCE_AVAILABLE */
|
||||
|
||||
#endif /* NV_DRM_AVAILABLE */
|
||||
|
@ -465,7 +465,7 @@ int nv_drm_gem_alloc_nvkms_memory_ioctl(struct drm_device *dev,
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (p->__pad != 0) {
|
||||
if ((p->__pad0 != 0) || (p->__pad1 != 0)) {
|
||||
ret = -EINVAL;
|
||||
NV_DRM_DEV_LOG_ERR(nv_dev, "non-zero value in padding field");
|
||||
goto failed;
|
||||
|
@ -95,6 +95,16 @@ static inline struct nv_drm_gem_object *to_nv_gem_object(
|
||||
* 3e70fd160cf0b1945225eaa08dd2cb8544f21cb8 (2018-11-15).
|
||||
*/
|
||||
|
||||
static inline void
|
||||
nv_drm_gem_object_reference(struct nv_drm_gem_object *nv_gem)
|
||||
{
|
||||
#if defined(NV_DRM_GEM_OBJECT_GET_PRESENT)
|
||||
drm_gem_object_get(&nv_gem->base);
|
||||
#else
|
||||
drm_gem_object_reference(&nv_gem->base);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void
|
||||
nv_drm_gem_object_unreference_unlocked(struct nv_drm_gem_object *nv_gem)
|
||||
{
|
||||
|
@ -306,6 +306,36 @@ int nv_drm_atomic_helper_disable_all(struct drm_device *dev,
|
||||
for_each_plane_in_state(__state, plane, plane_state, __i)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* for_each_new_plane_in_state() was added by kernel commit
|
||||
* 581e49fe6b411f407102a7f2377648849e0fa37f which was Signed-off-by:
|
||||
* Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
|
||||
* Daniel Vetter <daniel.vetter@ffwll.ch>
|
||||
*
|
||||
* This commit also added the old_state and new_state pointers to
|
||||
* __drm_planes_state. Because of this, the best that can be done on kernel
|
||||
* versions without this macro is for_each_plane_in_state.
|
||||
*/
|
||||
|
||||
/**
|
||||
* nv_drm_for_each_new_plane_in_state - iterate over all planes in an atomic update
|
||||
* @__state: &struct drm_atomic_state pointer
|
||||
* @plane: &struct drm_plane iteration cursor
|
||||
* @new_plane_state: &struct drm_plane_state iteration cursor for the new state
|
||||
* @__i: int iteration cursor, for macro-internal use
|
||||
*
|
||||
* This iterates over all planes in an atomic update, tracking only the new
|
||||
* state. This is useful in enable functions, where we need the new state the
|
||||
* hardware should be in when the atomic commit operation has completed.
|
||||
*/
|
||||
#if !defined(for_each_new_plane_in_state)
|
||||
#define nv_drm_for_each_new_plane_in_state(__state, plane, new_plane_state, __i) \
|
||||
nv_drm_for_each_plane_in_state(__state, plane, new_plane_state, __i)
|
||||
#else
|
||||
#define nv_drm_for_each_new_plane_in_state(__state, plane, new_plane_state, __i) \
|
||||
for_each_new_plane_in_state(__state, plane, new_plane_state, __i)
|
||||
#endif
|
||||
|
||||
static inline struct drm_connector *
|
||||
nv_drm_connector_lookup(struct drm_device *dev, struct drm_file *filep,
|
||||
uint32_t id)
|
||||
|
@ -48,6 +48,10 @@
|
||||
#define DRM_NVIDIA_GET_CONNECTOR_ID_FOR_DPY_ID 0x11
|
||||
#define DRM_NVIDIA_GRANT_PERMISSIONS 0x12
|
||||
#define DRM_NVIDIA_REVOKE_PERMISSIONS 0x13
|
||||
#define DRM_NVIDIA_SEMSURF_FENCE_CTX_CREATE 0x14
|
||||
#define DRM_NVIDIA_SEMSURF_FENCE_CREATE 0x15
|
||||
#define DRM_NVIDIA_SEMSURF_FENCE_WAIT 0x16
|
||||
#define DRM_NVIDIA_SEMSURF_FENCE_ATTACH 0x17
|
||||
|
||||
#define DRM_IOCTL_NVIDIA_GEM_IMPORT_NVKMS_MEMORY \
|
||||
DRM_IOWR((DRM_COMMAND_BASE + DRM_NVIDIA_GEM_IMPORT_NVKMS_MEMORY), \
|
||||
@ -133,6 +137,26 @@
|
||||
DRM_IOWR((DRM_COMMAND_BASE + DRM_NVIDIA_REVOKE_PERMISSIONS), \
|
||||
struct drm_nvidia_revoke_permissions_params)
|
||||
|
||||
#define DRM_IOCTL_NVIDIA_SEMSURF_FENCE_CTX_CREATE \
|
||||
DRM_IOWR((DRM_COMMAND_BASE + \
|
||||
DRM_NVIDIA_SEMSURF_FENCE_CTX_CREATE), \
|
||||
struct drm_nvidia_semsurf_fence_ctx_create_params)
|
||||
|
||||
#define DRM_IOCTL_NVIDIA_SEMSURF_FENCE_CREATE \
|
||||
DRM_IOWR((DRM_COMMAND_BASE + \
|
||||
DRM_NVIDIA_SEMSURF_FENCE_CREATE), \
|
||||
struct drm_nvidia_semsurf_fence_create_params)
|
||||
|
||||
#define DRM_IOCTL_NVIDIA_SEMSURF_FENCE_WAIT \
|
||||
DRM_IOW((DRM_COMMAND_BASE + \
|
||||
DRM_NVIDIA_SEMSURF_FENCE_WAIT), \
|
||||
struct drm_nvidia_semsurf_fence_wait_params)
|
||||
|
||||
#define DRM_IOCTL_NVIDIA_SEMSURF_FENCE_ATTACH \
|
||||
DRM_IOW((DRM_COMMAND_BASE + \
|
||||
DRM_NVIDIA_SEMSURF_FENCE_ATTACH), \
|
||||
struct drm_nvidia_semsurf_fence_attach_params)
|
||||
|
||||
struct drm_nvidia_gem_import_nvkms_memory_params {
|
||||
uint64_t mem_size; /* IN */
|
||||
|
||||
@ -158,6 +182,8 @@ struct drm_nvidia_get_dev_info_params {
|
||||
uint32_t generic_page_kind; /* OUT */
|
||||
uint32_t page_kind_generation; /* OUT */
|
||||
uint32_t sector_layout; /* OUT */
|
||||
uint32_t supports_sync_fd; /* OUT */
|
||||
uint32_t supports_semsurf; /* OUT */
|
||||
};
|
||||
|
||||
struct drm_nvidia_prime_fence_context_create_params {
|
||||
@ -179,6 +205,7 @@ struct drm_nvidia_gem_prime_fence_attach_params {
|
||||
uint32_t handle; /* IN GEM handle to attach fence to */
|
||||
uint32_t fence_context_handle; /* IN GEM handle to fence context on which fence is run on */
|
||||
uint32_t sem_thresh; /* IN Semaphore value to reach before signal */
|
||||
uint32_t __pad;
|
||||
};
|
||||
|
||||
struct drm_nvidia_get_client_capability_params {
|
||||
@ -190,6 +217,8 @@ struct drm_nvidia_get_client_capability_params {
|
||||
struct drm_nvidia_crtc_crc32 {
|
||||
uint32_t value; /* Read value, undefined if supported is false */
|
||||
uint8_t supported; /* Supported boolean, true if readable by hardware */
|
||||
uint8_t __pad0;
|
||||
uint16_t __pad1;
|
||||
};
|
||||
|
||||
struct drm_nvidia_crtc_crc32_v2_out {
|
||||
@ -229,10 +258,11 @@ struct drm_nvidia_gem_alloc_nvkms_memory_params {
|
||||
uint32_t handle; /* OUT */
|
||||
uint8_t block_linear; /* IN */
|
||||
uint8_t compressible; /* IN/OUT */
|
||||
uint16_t __pad;
|
||||
uint16_t __pad0;
|
||||
|
||||
uint64_t memory_size; /* IN */
|
||||
uint32_t flags; /* IN */
|
||||
uint32_t __pad1;
|
||||
};
|
||||
|
||||
struct drm_nvidia_gem_export_dmabuf_memory_params {
|
||||
@ -266,13 +296,90 @@ struct drm_nvidia_get_connector_id_for_dpy_id_params {
|
||||
uint32_t connectorId; /* OUT */
|
||||
};
|
||||
|
||||
enum drm_nvidia_permissions_type {
|
||||
NV_DRM_PERMISSIONS_TYPE_MODESET = 2,
|
||||
NV_DRM_PERMISSIONS_TYPE_SUB_OWNER = 3
|
||||
};
|
||||
|
||||
struct drm_nvidia_grant_permissions_params {
|
||||
int32_t fd; /* IN */
|
||||
uint32_t dpyId; /* IN */
|
||||
uint32_t type; /* IN */
|
||||
};
|
||||
|
||||
struct drm_nvidia_revoke_permissions_params {
|
||||
uint32_t dpyId; /* IN */
|
||||
uint32_t type; /* IN */
|
||||
};
|
||||
|
||||
struct drm_nvidia_semsurf_fence_ctx_create_params {
|
||||
uint64_t index; /* IN Index of the desired semaphore in the
|
||||
* fence context's semaphore surface */
|
||||
|
||||
/* Params for importing userspace semaphore surface */
|
||||
uint64_t nvkms_params_ptr; /* IN */
|
||||
uint64_t nvkms_params_size; /* IN */
|
||||
|
||||
uint32_t handle; /* OUT GEM handle to fence context */
|
||||
uint32_t __pad;
|
||||
};
|
||||
|
||||
struct drm_nvidia_semsurf_fence_create_params {
|
||||
uint32_t fence_context_handle; /* IN GEM handle to fence context on which
|
||||
* fence is run on */
|
||||
|
||||
uint32_t timeout_value_ms; /* IN Timeout value in ms for the fence
|
||||
* after which the fence will be signaled
|
||||
* with its error status set to -ETIMEDOUT.
|
||||
* Default timeout value is 5000ms */
|
||||
|
||||
uint64_t wait_value; /* IN Semaphore value to reach before signal */
|
||||
|
||||
int32_t fd; /* OUT sync FD object representing the
|
||||
* semaphore at the specified index reaching
|
||||
* a value >= wait_value */
|
||||
uint32_t __pad;
|
||||
};
|
||||
|
||||
/*
|
||||
* Note there is no provision for timeouts in this ioctl. The kernel
|
||||
* documentation asserts timeouts should be handled by fence producers, and
|
||||
* that waiters should not second-guess their logic, as it is producers rather
|
||||
* than consumers that have better information when it comes to determining a
|
||||
* reasonable timeout for a given workload.
|
||||
*/
|
||||
struct drm_nvidia_semsurf_fence_wait_params {
|
||||
uint32_t fence_context_handle; /* IN GEM handle to fence context which will
|
||||
* be used to wait on the sync FD. Need not
|
||||
* be the fence context used to create the
|
||||
* sync FD. */
|
||||
|
||||
int32_t fd; /* IN sync FD object to wait on */
|
||||
|
||||
uint64_t pre_wait_value; /* IN Wait for the semaphore represented by
|
||||
* fence_context to reach this value before
|
||||
* waiting for the sync file. */
|
||||
|
||||
uint64_t post_wait_value; /* IN Signal the semaphore represented by
|
||||
* fence_context to this value after waiting
|
||||
* for the sync file */
|
||||
};
|
||||
|
||||
struct drm_nvidia_semsurf_fence_attach_params {
|
||||
uint32_t handle; /* IN GEM handle of buffer */
|
||||
|
||||
uint32_t fence_context_handle; /* IN GEM handle of fence context */
|
||||
|
||||
uint32_t timeout_value_ms; /* IN Timeout value in ms for the fence
|
||||
* after which the fence will be signaled
|
||||
* with its error status set to -ETIMEDOUT.
|
||||
* Default timeout value is 5000ms */
|
||||
|
||||
uint32_t shared; /* IN If true, fence will reserve shared
|
||||
* access to the buffer, otherwise it will
|
||||
* reserve exclusive access */
|
||||
|
||||
uint64_t wait_value; /* IN Semaphore value to reach before signal */
|
||||
};
|
||||
|
||||
#endif /* _UAPI_NVIDIA_DRM_IOCTL_H_ */
|
||||
|
@ -35,7 +35,13 @@
|
||||
#include <drm/drmP.h>
|
||||
#endif
|
||||
|
||||
#if defined(NV_LINUX_SYNC_FILE_H_PRESENT)
|
||||
#include <linux/file.h>
|
||||
#include <linux/sync_file.h>
|
||||
#endif
|
||||
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include "nv-mm.h"
|
||||
|
||||
@ -45,6 +51,14 @@ MODULE_PARM_DESC(
|
||||
bool nv_drm_modeset_module_param = false;
|
||||
module_param_named(modeset, nv_drm_modeset_module_param, bool, 0400);
|
||||
|
||||
#if defined(NV_DRM_FBDEV_GENERIC_AVAILABLE)
|
||||
MODULE_PARM_DESC(
|
||||
fbdev,
|
||||
"Create a framebuffer device (1 = enable, 0 = disable (default)) (EXPERIMENTAL)");
|
||||
bool nv_drm_fbdev_module_param = false;
|
||||
module_param_named(fbdev, nv_drm_fbdev_module_param, bool, 0400);
|
||||
#endif
|
||||
|
||||
void *nv_drm_calloc(size_t nmemb, size_t size)
|
||||
{
|
||||
size_t total_size = nmemb * size;
|
||||
@ -81,14 +95,10 @@ char *nv_drm_asprintf(const char *fmt, ...)
|
||||
|
||||
#if defined(NVCPU_X86) || defined(NVCPU_X86_64)
|
||||
#define WRITE_COMBINE_FLUSH() asm volatile("sfence":::"memory")
|
||||
#elif defined(NVCPU_FAMILY_ARM)
|
||||
#if defined(NVCPU_ARM)
|
||||
#define WRITE_COMBINE_FLUSH() { dsb(); outer_sync(); }
|
||||
#elif defined(NVCPU_AARCH64)
|
||||
#define WRITE_COMBINE_FLUSH() mb()
|
||||
#endif
|
||||
#elif defined(NVCPU_PPC64LE)
|
||||
#define WRITE_COMBINE_FLUSH() asm volatile("sync":::"memory")
|
||||
#else
|
||||
#define WRITE_COMBINE_FLUSH() mb()
|
||||
#endif
|
||||
|
||||
void nv_drm_write_combine_flush(void)
|
||||
@ -160,6 +170,122 @@ void nv_drm_vunmap(void *address)
|
||||
vunmap(address);
|
||||
}
|
||||
|
||||
bool nv_drm_workthread_init(nv_drm_workthread *worker, const char *name)
|
||||
{
|
||||
worker->shutting_down = false;
|
||||
if (nv_kthread_q_init(&worker->q, name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
spin_lock_init(&worker->lock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void nv_drm_workthread_shutdown(nv_drm_workthread *worker)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&worker->lock, flags);
|
||||
worker->shutting_down = true;
|
||||
spin_unlock_irqrestore(&worker->lock, flags);
|
||||
|
||||
nv_kthread_q_stop(&worker->q);
|
||||
}
|
||||
|
||||
void nv_drm_workthread_work_init(nv_drm_work *work,
|
||||
void (*callback)(void *),
|
||||
void *arg)
|
||||
{
|
||||
nv_kthread_q_item_init(work, callback, arg);
|
||||
}
|
||||
|
||||
int nv_drm_workthread_add_work(nv_drm_workthread *worker, nv_drm_work *work)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irqsave(&worker->lock, flags);
|
||||
if (!worker->shutting_down) {
|
||||
ret = nv_kthread_q_schedule_q_item(&worker->q, work);
|
||||
}
|
||||
spin_unlock_irqrestore(&worker->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void nv_drm_timer_setup(nv_drm_timer *timer, void (*callback)(nv_drm_timer *nv_drm_timer))
|
||||
{
|
||||
nv_timer_setup(timer, callback);
|
||||
}
|
||||
|
||||
void nv_drm_mod_timer(nv_drm_timer *timer, unsigned long timeout_native)
|
||||
{
|
||||
mod_timer(&timer->kernel_timer, timeout_native);
|
||||
}
|
||||
|
||||
unsigned long nv_drm_timer_now(void)
|
||||
{
|
||||
return jiffies;
|
||||
}
|
||||
|
||||
unsigned long nv_drm_timeout_from_ms(NvU64 relative_timeout_ms)
|
||||
{
|
||||
return jiffies + msecs_to_jiffies(relative_timeout_ms);
|
||||
}
|
||||
|
||||
bool nv_drm_del_timer_sync(nv_drm_timer *timer)
|
||||
{
|
||||
if (del_timer_sync(&timer->kernel_timer)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(NV_DRM_FENCE_AVAILABLE)
|
||||
int nv_drm_create_sync_file(nv_dma_fence_t *fence)
|
||||
{
|
||||
#if defined(NV_LINUX_SYNC_FILE_H_PRESENT)
|
||||
struct sync_file *sync;
|
||||
int fd = get_unused_fd_flags(O_CLOEXEC);
|
||||
|
||||
if (fd < 0) {
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* sync_file_create() generates its own reference to the fence */
|
||||
sync = sync_file_create(fence);
|
||||
|
||||
if (IS_ERR(sync)) {
|
||||
put_unused_fd(fd);
|
||||
return PTR_ERR(sync);
|
||||
}
|
||||
|
||||
fd_install(fd, sync->file);
|
||||
|
||||
return fd;
|
||||
#else /* defined(NV_LINUX_SYNC_FILE_H_PRESENT) */
|
||||
return -EINVAL;
|
||||
#endif /* defined(NV_LINUX_SYNC_FILE_H_PRESENT) */
|
||||
}
|
||||
|
||||
nv_dma_fence_t *nv_drm_sync_file_get_fence(int fd)
|
||||
{
|
||||
#if defined(NV_SYNC_FILE_GET_FENCE_PRESENT)
|
||||
return sync_file_get_fence(fd);
|
||||
#else /* defined(NV_SYNC_FILE_GET_FENCE_PRESENT) */
|
||||
return NULL;
|
||||
#endif /* defined(NV_SYNC_FILE_GET_FENCE_PRESENT) */
|
||||
}
|
||||
#endif /* defined(NV_DRM_FENCE_AVAILABLE) */
|
||||
|
||||
void nv_drm_yield(void)
|
||||
{
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule_timeout(1);
|
||||
}
|
||||
|
||||
#endif /* NV_DRM_AVAILABLE */
|
||||
|
||||
/*************************************************************************
|
||||
|
@ -237,6 +237,14 @@ nv_drm_atomic_apply_modeset_config(struct drm_device *dev,
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* If sub-owner permission was granted to another NVKMS client, disallow
|
||||
* modesets through the DRM interface.
|
||||
*/
|
||||
if (nv_dev->subOwnershipGranted) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(requested_config, 0, sizeof(*requested_config));
|
||||
|
||||
/* Loop over affected crtcs and construct NvKmsKapiRequestedModeSetConfig */
|
||||
@ -274,9 +282,6 @@ nv_drm_atomic_apply_modeset_config(struct drm_device *dev,
|
||||
|
||||
nv_new_crtc_state->nv_flip = NULL;
|
||||
}
|
||||
#if defined(NV_DRM_CRTC_STATE_HAS_VRR_ENABLED)
|
||||
requested_config->headRequestedConfig[nv_crtc->head].modeSetConfig.vrrEnabled = new_crtc_state->vrr_enabled;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -292,7 +297,9 @@ nv_drm_atomic_apply_modeset_config(struct drm_device *dev,
|
||||
requested_config,
|
||||
&reply_config,
|
||||
commit)) {
|
||||
return -EINVAL;
|
||||
if (commit || reply_config.flipResult != NV_KMS_FLIP_RESULT_IN_PROGRESS) {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (commit && nv_dev->supportsSyncpts) {
|
||||
@ -388,42 +395,56 @@ int nv_drm_atomic_commit(struct drm_device *dev,
|
||||
struct nv_drm_device *nv_dev = to_nv_device(dev);
|
||||
|
||||
/*
|
||||
* drm_mode_config_funcs::atomic_commit() mandates to return -EBUSY
|
||||
* for nonblocking commit if previous updates (commit tasks/flip event) are
|
||||
* pending. In case of blocking commits it mandates to wait for previous
|
||||
* updates to complete.
|
||||
* XXX: drm_mode_config_funcs::atomic_commit() mandates to return -EBUSY
|
||||
* for nonblocking commit if the commit would need to wait for previous
|
||||
* updates (commit tasks/flip event) to complete. In case of blocking
|
||||
* commits it mandates to wait for previous updates to complete. However,
|
||||
* the kernel DRM-KMS documentation does explicitly allow maintaining a
|
||||
* queue of outstanding commits.
|
||||
*
|
||||
* Our system already implements such a queue, but due to
|
||||
* bug 4054608, it is currently not used.
|
||||
*/
|
||||
if (nonblock) {
|
||||
nv_drm_for_each_crtc_in_state(state, crtc, crtc_state, i) {
|
||||
struct nv_drm_crtc *nv_crtc = to_nv_crtc(crtc);
|
||||
nv_drm_for_each_crtc_in_state(state, crtc, crtc_state, i) {
|
||||
struct nv_drm_crtc *nv_crtc = to_nv_crtc(crtc);
|
||||
|
||||
/*
|
||||
* Here you aren't required to hold nv_drm_crtc::flip_list_lock
|
||||
* because:
|
||||
*
|
||||
* The core DRM driver acquires lock for all affected crtcs before
|
||||
* calling into ->commit() hook, therefore it is not possible for
|
||||
* other threads to call into ->commit() hook affecting same crtcs
|
||||
* and enqueue flip objects into flip_list -
|
||||
*
|
||||
* nv_drm_atomic_commit_internal()
|
||||
* |-> nv_drm_atomic_apply_modeset_config(commit=true)
|
||||
* |-> nv_drm_crtc_enqueue_flip()
|
||||
*
|
||||
* Only possibility is list_empty check races with code path
|
||||
* dequeuing flip object -
|
||||
*
|
||||
* __nv_drm_handle_flip_event()
|
||||
* |-> nv_drm_crtc_dequeue_flip()
|
||||
*
|
||||
* But this race condition can't lead list_empty() to return
|
||||
* incorrect result. nv_drm_crtc_dequeue_flip() in the middle of
|
||||
* updating the list could not trick us into thinking the list is
|
||||
* empty when it isn't.
|
||||
*/
|
||||
/*
|
||||
* Here you aren't required to hold nv_drm_crtc::flip_list_lock
|
||||
* because:
|
||||
*
|
||||
* The core DRM driver acquires lock for all affected crtcs before
|
||||
* calling into ->commit() hook, therefore it is not possible for
|
||||
* other threads to call into ->commit() hook affecting same crtcs
|
||||
* and enqueue flip objects into flip_list -
|
||||
*
|
||||
* nv_drm_atomic_commit_internal()
|
||||
* |-> nv_drm_atomic_apply_modeset_config(commit=true)
|
||||
* |-> nv_drm_crtc_enqueue_flip()
|
||||
*
|
||||
* Only possibility is list_empty check races with code path
|
||||
* dequeuing flip object -
|
||||
*
|
||||
* __nv_drm_handle_flip_event()
|
||||
* |-> nv_drm_crtc_dequeue_flip()
|
||||
*
|
||||
* But this race condition can't lead list_empty() to return
|
||||
* incorrect result. nv_drm_crtc_dequeue_flip() in the middle of
|
||||
* updating the list could not trick us into thinking the list is
|
||||
* empty when it isn't.
|
||||
*/
|
||||
if (nonblock) {
|
||||
if (!list_empty(&nv_crtc->flip_list)) {
|
||||
return -EBUSY;
|
||||
}
|
||||
} else {
|
||||
if (wait_event_timeout(
|
||||
nv_dev->flip_event_wq,
|
||||
list_empty(&nv_crtc->flip_list),
|
||||
3 * HZ /* 3 second */) == 0) {
|
||||
NV_DRM_DEV_LOG_ERR(
|
||||
nv_dev,
|
||||
"Flip event timeout on head %u", nv_crtc->head);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -467,6 +488,7 @@ int nv_drm_atomic_commit(struct drm_device *dev,
|
||||
|
||||
goto done;
|
||||
}
|
||||
nv_dev->drmMasterChangedSinceLastAtomicCommit = NV_FALSE;
|
||||
|
||||
nv_drm_for_each_crtc_in_state(state, crtc, crtc_state, i) {
|
||||
struct nv_drm_crtc *nv_crtc = to_nv_crtc(crtc);
|
||||
|
@ -29,10 +29,47 @@
|
||||
|
||||
#if defined(NV_DRM_AVAILABLE)
|
||||
|
||||
#if defined(NV_DRM_FENCE_AVAILABLE)
|
||||
#include "nvidia-dma-fence-helper.h"
|
||||
#endif
|
||||
|
||||
#if defined(NV_LINUX)
|
||||
#include "nv-kthread-q.h"
|
||||
#include "linux/spinlock.h"
|
||||
|
||||
typedef struct nv_drm_workthread {
|
||||
spinlock_t lock;
|
||||
struct nv_kthread_q q;
|
||||
bool shutting_down;
|
||||
} nv_drm_workthread;
|
||||
|
||||
typedef nv_kthread_q_item_t nv_drm_work;
|
||||
|
||||
#else /* defined(NV_LINUX) */
|
||||
#error "Need to define deferred work primitives for this OS"
|
||||
#endif /* else defined(NV_LINUX) */
|
||||
|
||||
#if defined(NV_LINUX)
|
||||
#include "nv-timer.h"
|
||||
|
||||
typedef struct nv_timer nv_drm_timer;
|
||||
|
||||
#else /* defined(NV_LINUX) */
|
||||
#error "Need to define kernel timer callback primitives for this OS"
|
||||
#endif /* else defined(NV_LINUX) */
|
||||
|
||||
#if defined(NV_DRM_FBDEV_GENERIC_SETUP_PRESENT) && defined(NV_DRM_APERTURE_REMOVE_CONFLICTING_PCI_FRAMEBUFFERS_PRESENT)
|
||||
#define NV_DRM_FBDEV_GENERIC_AVAILABLE
|
||||
#endif
|
||||
|
||||
struct page;
|
||||
|
||||
/* Set to true when the atomic modeset feature is enabled. */
|
||||
extern bool nv_drm_modeset_module_param;
|
||||
#if defined(NV_DRM_FBDEV_GENERIC_AVAILABLE)
|
||||
/* Set to true when the nvidia-drm driver should install a framebuffer device */
|
||||
extern bool nv_drm_fbdev_module_param;
|
||||
#endif
|
||||
|
||||
void *nv_drm_calloc(size_t nmemb, size_t size);
|
||||
|
||||
@ -51,6 +88,37 @@ void *nv_drm_vmap(struct page **pages, unsigned long pages_count);
|
||||
|
||||
void nv_drm_vunmap(void *address);
|
||||
|
||||
#endif
|
||||
bool nv_drm_workthread_init(nv_drm_workthread *worker, const char *name);
|
||||
|
||||
/* Can be called concurrently with nv_drm_workthread_add_work() */
|
||||
void nv_drm_workthread_shutdown(nv_drm_workthread *worker);
|
||||
|
||||
void nv_drm_workthread_work_init(nv_drm_work *work,
|
||||
void (*callback)(void *),
|
||||
void *arg);
|
||||
|
||||
/* Can be called concurrently with nv_drm_workthread_shutdown() */
|
||||
int nv_drm_workthread_add_work(nv_drm_workthread *worker, nv_drm_work *work);
|
||||
|
||||
void nv_drm_timer_setup(nv_drm_timer *timer,
|
||||
void (*callback)(nv_drm_timer *nv_drm_timer));
|
||||
|
||||
void nv_drm_mod_timer(nv_drm_timer *timer, unsigned long relative_timeout_ms);
|
||||
|
||||
bool nv_drm_del_timer_sync(nv_drm_timer *timer);
|
||||
|
||||
unsigned long nv_drm_timer_now(void);
|
||||
|
||||
unsigned long nv_drm_timeout_from_ms(NvU64 relative_timeout_ms);
|
||||
|
||||
#if defined(NV_DRM_FENCE_AVAILABLE)
|
||||
int nv_drm_create_sync_file(nv_dma_fence_t *fence);
|
||||
|
||||
nv_dma_fence_t *nv_drm_sync_file_get_fence(int fd);
|
||||
#endif /* defined(NV_DRM_FENCE_AVAILABLE) */
|
||||
|
||||
void nv_drm_yield(void);
|
||||
|
||||
#endif /* defined(NV_DRM_AVAILABLE) */
|
||||
|
||||
#endif /* __NVIDIA_DRM_OS_INTERFACE_H__ */
|
||||
|
@ -46,12 +46,33 @@
|
||||
#define NV_DRM_LOG_ERR(__fmt, ...) \
|
||||
DRM_ERROR("[nvidia-drm] " __fmt "\n", ##__VA_ARGS__)
|
||||
|
||||
/*
|
||||
* DRM_WARN() was added in v4.9 by kernel commit
|
||||
* 30b0da8d556e65ff935a56cd82c05ba0516d3e4a
|
||||
*
|
||||
* Before this commit, only DRM_INFO and DRM_ERROR were defined and
|
||||
* DRM_INFO(fmt, ...) was defined as
|
||||
* printk(KERN_INFO "[" DRM_NAME "] " fmt, ##__VA_ARGS__). So, if
|
||||
* DRM_WARN is undefined this defines NV_DRM_LOG_WARN following the
|
||||
* same pattern as DRM_INFO.
|
||||
*/
|
||||
#ifdef DRM_WARN
|
||||
#define NV_DRM_LOG_WARN(__fmt, ...) \
|
||||
DRM_WARN("[nvidia-drm] " __fmt "\n", ##__VA_ARGS__)
|
||||
#else
|
||||
#define NV_DRM_LOG_WARN(__fmt, ...) \
|
||||
printk(KERN_WARNING "[" DRM_NAME "] [nvidia-drm] " __fmt "\n", ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#define NV_DRM_LOG_INFO(__fmt, ...) \
|
||||
DRM_INFO("[nvidia-drm] " __fmt "\n", ##__VA_ARGS__)
|
||||
|
||||
#define NV_DRM_DEV_LOG_INFO(__dev, __fmt, ...) \
|
||||
NV_DRM_LOG_INFO("[GPU ID 0x%08x] " __fmt, __dev->gpu_info.gpu_id, ##__VA_ARGS__)
|
||||
|
||||
#define NV_DRM_DEV_LOG_WARN(__dev, __fmt, ...) \
|
||||
NV_DRM_LOG_WARN("[GPU ID 0x%08x] " __fmt, __dev->gpu_info.gpu_id, ##__VA_ARGS__)
|
||||
|
||||
#define NV_DRM_DEV_LOG_ERR(__dev, __fmt, ...) \
|
||||
NV_DRM_LOG_ERR("[GPU ID 0x%08x] " __fmt, __dev->gpu_info.gpu_id, ##__VA_ARGS__)
|
||||
|
||||
@ -117,9 +138,26 @@ struct nv_drm_device {
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(NV_DRM_FENCE_AVAILABLE)
|
||||
NvU64 semsurf_stride;
|
||||
NvU64 semsurf_max_submitted_offset;
|
||||
#endif
|
||||
|
||||
NvBool hasVideoMemory;
|
||||
|
||||
NvBool supportsSyncpts;
|
||||
NvBool subOwnershipGranted;
|
||||
NvBool hasFramebufferConsole;
|
||||
|
||||
/**
|
||||
* @drmMasterChangedSinceLastAtomicCommit:
|
||||
*
|
||||
* This flag is set in nv_drm_master_set and reset after a completed atomic
|
||||
* commit. It is used to restore or recommit state that is lost by the
|
||||
* NvKms modeset owner change, such as the CRTC color management
|
||||
* properties.
|
||||
*/
|
||||
NvBool drmMasterChangedSinceLastAtomicCommit;
|
||||
|
||||
struct drm_property *nv_out_fence_property;
|
||||
struct drm_property *nv_input_colorspace_property;
|
||||
|
@ -19,6 +19,7 @@ NVIDIA_DRM_SOURCES += nvidia-drm/nvidia-drm-modeset.c
|
||||
NVIDIA_DRM_SOURCES += nvidia-drm/nvidia-drm-fence.c
|
||||
NVIDIA_DRM_SOURCES += nvidia-drm/nvidia-drm-linux.c
|
||||
NVIDIA_DRM_SOURCES += nvidia-drm/nvidia-drm-helper.c
|
||||
NVIDIA_DRM_SOURCES += nvidia-drm/nv-kthread-q.c
|
||||
NVIDIA_DRM_SOURCES += nvidia-drm/nv-pci-table.c
|
||||
NVIDIA_DRM_SOURCES += nvidia-drm/nvidia-drm-gem-nvkms-memory.c
|
||||
NVIDIA_DRM_SOURCES += nvidia-drm/nvidia-drm-gem-user-memory.c
|
||||
@ -79,6 +80,17 @@ NV_CONFTEST_FUNCTION_COMPILE_TESTS += drm_rotation_available
|
||||
NV_CONFTEST_FUNCTION_COMPILE_TESTS += drm_vma_offset_exact_lookup_locked
|
||||
NV_CONFTEST_FUNCTION_COMPILE_TESTS += drm_gem_object_put_unlocked
|
||||
NV_CONFTEST_FUNCTION_COMPILE_TESTS += nvhost_dma_fence_unpack
|
||||
NV_CONFTEST_FUNCTION_COMPILE_TESTS += list_is_first
|
||||
NV_CONFTEST_FUNCTION_COMPILE_TESTS += timer_setup
|
||||
NV_CONFTEST_FUNCTION_COMPILE_TESTS += dma_fence_set_error
|
||||
NV_CONFTEST_FUNCTION_COMPILE_TESTS += fence_set_error
|
||||
NV_CONFTEST_FUNCTION_COMPILE_TESTS += sync_file_get_fence
|
||||
NV_CONFTEST_FUNCTION_COMPILE_TESTS += drm_aperture_remove_conflicting_pci_framebuffers
|
||||
NV_CONFTEST_FUNCTION_COMPILE_TESTS += drm_fbdev_generic_setup
|
||||
NV_CONFTEST_FUNCTION_COMPILE_TESTS += drm_connector_attach_hdr_output_metadata_property
|
||||
NV_CONFTEST_FUNCTION_COMPILE_TESTS += drm_helper_crtc_enable_color_mgmt
|
||||
NV_CONFTEST_FUNCTION_COMPILE_TESTS += drm_crtc_enable_color_mgmt
|
||||
NV_CONFTEST_FUNCTION_COMPILE_TESTS += drm_atomic_helper_legacy_gamma_set
|
||||
|
||||
NV_CONFTEST_TYPE_COMPILE_TESTS += drm_bus_present
|
||||
NV_CONFTEST_TYPE_COMPILE_TESTS += drm_bus_has_bus_type
|
||||
@ -133,3 +145,6 @@ NV_CONFTEST_TYPE_COMPILE_TESTS += drm_connector_lookup
|
||||
NV_CONFTEST_TYPE_COMPILE_TESTS += drm_connector_put
|
||||
NV_CONFTEST_TYPE_COMPILE_TESTS += vm_area_struct_has_const_vm_flags
|
||||
NV_CONFTEST_TYPE_COMPILE_TESTS += drm_driver_has_dumb_destroy
|
||||
NV_CONFTEST_TYPE_COMPILE_TESTS += fence_ops_use_64bit_seqno
|
||||
NV_CONFTEST_TYPE_COMPILE_TESTS += drm_aperture_remove_conflicting_pci_framebuffers_has_driver_arg
|
||||
NV_CONFTEST_TYPE_COMPILE_TESTS += drm_mode_create_dp_colorspace_property_has_supported_colorspaces_arg
|
||||
|
@ -247,6 +247,11 @@ int nv_kthread_q_init_on_node(nv_kthread_q_t *q, const char *q_name, int preferr
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nv_kthread_q_init(nv_kthread_q_t *q, const char *qname)
|
||||
{
|
||||
return nv_kthread_q_init_on_node(q, qname, NV_KTHREAD_NO_NODE);
|
||||
}
|
||||
|
||||
// Returns true (non-zero) if the item was actually scheduled, and false if the
|
||||
// item was already pending in a queue.
|
||||
static int _raw_q_schedule(nv_kthread_q_t *q, nv_kthread_q_item_t *q_item)
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2015-21 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2015-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
@ -65,9 +65,15 @@
|
||||
static bool output_rounding_fix = true;
|
||||
module_param_named(output_rounding_fix, output_rounding_fix, bool, 0400);
|
||||
|
||||
static bool disable_hdmi_frl = false;
|
||||
module_param_named(disable_hdmi_frl, disable_hdmi_frl, bool, 0400);
|
||||
|
||||
static bool disable_vrr_memclk_switch = false;
|
||||
module_param_named(disable_vrr_memclk_switch, disable_vrr_memclk_switch, bool, 0400);
|
||||
|
||||
static bool hdmi_deepcolor = false;
|
||||
module_param_named(hdmi_deepcolor, hdmi_deepcolor, bool, 0400);
|
||||
|
||||
/* These parameters are used for fault injection tests. Normally the defaults
|
||||
* should be used. */
|
||||
MODULE_PARM_DESC(fail_malloc, "Fail the Nth call to nvkms_alloc");
|
||||
@ -78,6 +84,7 @@ MODULE_PARM_DESC(malloc_verbose, "Report information about malloc calls on modul
|
||||
static bool malloc_verbose = false;
|
||||
module_param_named(malloc_verbose, malloc_verbose, bool, 0400);
|
||||
|
||||
#if NVKMS_CONFIG_FILE_SUPPORTED
|
||||
/* This parameter is used to find the dpy override conf file */
|
||||
#define NVKMS_CONF_FILE_SPECIFIED (nvkms_conf != NULL)
|
||||
|
||||
@ -86,6 +93,7 @@ MODULE_PARM_DESC(config_file,
|
||||
"(default: disabled)");
|
||||
static char *nvkms_conf = NULL;
|
||||
module_param_named(config_file, nvkms_conf, charp, 0400);
|
||||
#endif
|
||||
|
||||
static atomic_t nvkms_alloc_called_count;
|
||||
|
||||
@ -94,11 +102,21 @@ NvBool nvkms_output_rounding_fix(void)
|
||||
return output_rounding_fix;
|
||||
}
|
||||
|
||||
NvBool nvkms_disable_hdmi_frl(void)
|
||||
{
|
||||
return disable_hdmi_frl;
|
||||
}
|
||||
|
||||
NvBool nvkms_disable_vrr_memclk_switch(void)
|
||||
{
|
||||
return disable_vrr_memclk_switch;
|
||||
}
|
||||
|
||||
NvBool nvkms_hdmi_deepcolor(void)
|
||||
{
|
||||
return hdmi_deepcolor;
|
||||
}
|
||||
|
||||
#define NVKMS_SYNCPT_STUBS_NEEDED
|
||||
|
||||
/*************************************************************************
|
||||
@ -335,7 +353,7 @@ NvU64 nvkms_get_usec(void)
|
||||
struct timespec64 ts;
|
||||
NvU64 ns;
|
||||
|
||||
ktime_get_real_ts64(&ts);
|
||||
ktime_get_raw_ts64(&ts);
|
||||
|
||||
ns = timespec64_to_ns(&ts);
|
||||
return ns / 1000;
|
||||
@ -1382,6 +1400,7 @@ static void nvkms_proc_exit(void)
|
||||
/*************************************************************************
|
||||
* NVKMS Config File Read
|
||||
************************************************************************/
|
||||
#if NVKMS_CONFIG_FILE_SUPPORTED
|
||||
static NvBool nvkms_fs_mounted(void)
|
||||
{
|
||||
return current->fs != NULL;
|
||||
@ -1489,6 +1508,11 @@ static void nvkms_read_config_file_locked(void)
|
||||
|
||||
nvkms_free(buffer, buf_size);
|
||||
}
|
||||
#else
|
||||
static void nvkms_read_config_file_locked(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/*************************************************************************
|
||||
* NVKMS KAPI functions
|
||||
|
@ -97,8 +97,9 @@ typedef struct {
|
||||
} NvKmsSyncPtOpParams;
|
||||
|
||||
NvBool nvkms_output_rounding_fix(void);
|
||||
|
||||
NvBool nvkms_disable_hdmi_frl(void);
|
||||
NvBool nvkms_disable_vrr_memclk_switch(void);
|
||||
NvBool nvkms_hdmi_deepcolor(void);
|
||||
|
||||
void nvkms_call_rm (void *ops);
|
||||
void* nvkms_alloc (size_t size,
|
||||
|
@ -58,6 +58,18 @@ nvidia-modeset-y += $(NVIDIA_MODESET_BINARY_OBJECT_O)
|
||||
NVIDIA_MODESET_CFLAGS += -I$(src)/nvidia-modeset
|
||||
NVIDIA_MODESET_CFLAGS += -UDEBUG -U_DEBUG -DNDEBUG -DNV_BUILD_MODULE_INSTANCES=0
|
||||
|
||||
# Some Android kernels prohibit driver use of filesystem functions like
|
||||
# filp_open() and kernel_read(). Disable the NVKMS_CONFIG_FILE_SUPPORTED
|
||||
# functionality that uses those functions when building for Android.
|
||||
|
||||
PLATFORM_IS_ANDROID ?= 0
|
||||
|
||||
ifeq ($(PLATFORM_IS_ANDROID),1)
|
||||
NVIDIA_MODESET_CFLAGS += -DNVKMS_CONFIG_FILE_SUPPORTED=0
|
||||
else
|
||||
NVIDIA_MODESET_CFLAGS += -DNVKMS_CONFIG_FILE_SUPPORTED=1
|
||||
endif
|
||||
|
||||
$(call ASSIGN_PER_OBJ_CFLAGS, $(NVIDIA_MODESET_OBJECTS), $(NVIDIA_MODESET_CFLAGS))
|
||||
|
||||
|
||||
|
@ -66,6 +66,8 @@ enum NvKmsClientType {
|
||||
NVKMS_CLIENT_KERNEL_SPACE,
|
||||
};
|
||||
|
||||
struct NvKmsPerOpenDev;
|
||||
|
||||
NvBool nvKmsIoctl(
|
||||
void *pOpenVoid,
|
||||
NvU32 cmd,
|
||||
@ -104,4 +106,6 @@ NvBool nvKmsKapiGetFunctionsTableInternal
|
||||
NvBool nvKmsGetBacklight(NvU32 display_id, void *drv_priv, NvU32 *brightness);
|
||||
NvBool nvKmsSetBacklight(NvU32 display_id, void *drv_priv, NvU32 brightness);
|
||||
|
||||
NvBool nvKmsOpenDevHasSubOwnerPermissionOrBetter(const struct NvKmsPerOpenDev *pOpenDev);
|
||||
|
||||
#endif /* __NV_KMS_H__ */
|
||||
|
@ -249,8 +249,8 @@ static int nv_dma_map(struct sg_table *sg_head, void *context,
|
||||
nv_mem_context->sg_allocated = 1;
|
||||
for_each_sg(sg_head->sgl, sg, nv_mem_context->npages, i) {
|
||||
sg_set_page(sg, NULL, nv_mem_context->page_size, 0);
|
||||
sg->dma_address = dma_mapping->dma_addresses[i];
|
||||
sg->dma_length = nv_mem_context->page_size;
|
||||
sg_dma_address(sg) = dma_mapping->dma_addresses[i];
|
||||
sg_dma_len(sg) = nv_mem_context->page_size;
|
||||
}
|
||||
nv_mem_context->sg_head = *sg_head;
|
||||
*nmap = nv_mem_context->npages;
|
||||
@ -304,8 +304,13 @@ static void nv_mem_put_pages_common(int nc,
|
||||
return;
|
||||
|
||||
if (nc) {
|
||||
#ifdef NVIDIA_P2P_CAP_GET_PAGES_PERSISTENT_API
|
||||
ret = nvidia_p2p_put_pages_persistent(nv_mem_context->page_virt_start,
|
||||
nv_mem_context->page_table, 0);
|
||||
#else
|
||||
ret = nvidia_p2p_put_pages(0, 0, nv_mem_context->page_virt_start,
|
||||
nv_mem_context->page_table);
|
||||
#endif
|
||||
} else {
|
||||
ret = nvidia_p2p_put_pages(0, 0, nv_mem_context->page_virt_start,
|
||||
nv_mem_context->page_table);
|
||||
@ -412,9 +417,15 @@ static int nv_mem_get_pages_nc(unsigned long addr,
|
||||
nv_mem_context->core_context = core_context;
|
||||
nv_mem_context->page_size = GPU_PAGE_SIZE;
|
||||
|
||||
#ifdef NVIDIA_P2P_CAP_GET_PAGES_PERSISTENT_API
|
||||
ret = nvidia_p2p_get_pages_persistent(nv_mem_context->page_virt_start,
|
||||
nv_mem_context->mapped_size,
|
||||
&nv_mem_context->page_table, 0);
|
||||
#else
|
||||
ret = nvidia_p2p_get_pages(0, 0, nv_mem_context->page_virt_start, nv_mem_context->mapped_size,
|
||||
&nv_mem_context->page_table, NULL, NULL);
|
||||
#endif
|
||||
|
||||
if (ret < 0) {
|
||||
peer_err("error %d while calling nvidia_p2p_get_pages() with NULL callback\n", ret);
|
||||
return ret;
|
||||
@ -459,8 +470,6 @@ static int __init nv_mem_client_init(void)
|
||||
}
|
||||
|
||||
#if defined (NV_MLNX_IB_PEER_MEM_SYMBOLS_PRESENT)
|
||||
int status = 0;
|
||||
|
||||
// off by one, to leave space for the trailing '1' which is flagging
|
||||
// the new client type
|
||||
BUG_ON(strlen(DRV_NAME) > IB_PEER_MEMORY_NAME_MAX-1);
|
||||
@ -489,7 +498,7 @@ static int __init nv_mem_client_init(void)
|
||||
&mem_invalidate_callback);
|
||||
if (!reg_handle) {
|
||||
peer_err("nv_mem_client_init -- error while registering traditional client\n");
|
||||
status = -EINVAL;
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -499,12 +508,12 @@ static int __init nv_mem_client_init(void)
|
||||
reg_handle_nc = ib_register_peer_memory_client(&nv_mem_client_nc, NULL);
|
||||
if (!reg_handle_nc) {
|
||||
peer_err("nv_mem_client_init -- error while registering nc client\n");
|
||||
status = -EINVAL;
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
if (status) {
|
||||
if (rc) {
|
||||
if (reg_handle) {
|
||||
ib_unregister_peer_memory_client(reg_handle);
|
||||
reg_handle = NULL;
|
||||
@ -516,7 +525,7 @@ out:
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
return rc;
|
||||
#else
|
||||
return -EINVAL;
|
||||
#endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2022 NVIDIA Corporation
|
||||
Copyright (c) 2023 NVIDIA Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2022 NVIDIA Corporation
|
||||
Copyright (c) 2023 NVIDIA Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
|
@ -247,6 +247,11 @@ int nv_kthread_q_init_on_node(nv_kthread_q_t *q, const char *q_name, int preferr
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nv_kthread_q_init(nv_kthread_q_t *q, const char *qname)
|
||||
{
|
||||
return nv_kthread_q_init_on_node(q, qname, NV_KTHREAD_NO_NODE);
|
||||
}
|
||||
|
||||
// Returns true (non-zero) if the item was actually scheduled, and false if the
|
||||
// item was already pending in a queue.
|
||||
static int _raw_q_schedule(nv_kthread_q_t *q, nv_kthread_q_item_t *q_item)
|
||||
|
@ -27,6 +27,7 @@ NVIDIA_UVM_SOURCES += nvidia-uvm/uvm_rm_mem.c
|
||||
NVIDIA_UVM_SOURCES += nvidia-uvm/uvm_channel.c
|
||||
NVIDIA_UVM_SOURCES += nvidia-uvm/uvm_lock.c
|
||||
NVIDIA_UVM_SOURCES += nvidia-uvm/uvm_hal.c
|
||||
NVIDIA_UVM_SOURCES += nvidia-uvm/uvm_processors.c
|
||||
NVIDIA_UVM_SOURCES += nvidia-uvm/uvm_range_tree.c
|
||||
NVIDIA_UVM_SOURCES += nvidia-uvm/uvm_rb_tree.c
|
||||
NVIDIA_UVM_SOURCES += nvidia-uvm/uvm_range_allocator.c
|
||||
|
@ -82,10 +82,12 @@ NV_CONFTEST_FUNCTION_COMPILE_TESTS += set_pages_uc
|
||||
NV_CONFTEST_FUNCTION_COMPILE_TESTS += ktime_get_raw_ts64
|
||||
NV_CONFTEST_FUNCTION_COMPILE_TESTS += ioasid_get
|
||||
NV_CONFTEST_FUNCTION_COMPILE_TESTS += mm_pasid_drop
|
||||
NV_CONFTEST_FUNCTION_COMPILE_TESTS += migrate_vma_setup
|
||||
NV_CONFTEST_FUNCTION_COMPILE_TESTS += mmget_not_zero
|
||||
NV_CONFTEST_FUNCTION_COMPILE_TESTS += mmgrab
|
||||
NV_CONFTEST_FUNCTION_COMPILE_TESTS += iommu_sva_bind_device_has_drvdata_arg
|
||||
NV_CONFTEST_FUNCTION_COMPILE_TESTS += vm_fault_to_errno
|
||||
NV_CONFTEST_FUNCTION_COMPILE_TESTS += find_next_bit_wrap
|
||||
|
||||
NV_CONFTEST_TYPE_COMPILE_TESTS += backing_dev_info
|
||||
NV_CONFTEST_TYPE_COMPILE_TESTS += mm_context_t
|
||||
@ -99,6 +101,7 @@ NV_CONFTEST_TYPE_COMPILE_TESTS += kmem_cache_has_kobj_remove_work
|
||||
NV_CONFTEST_TYPE_COMPILE_TESTS += sysfs_slab_unlink
|
||||
NV_CONFTEST_TYPE_COMPILE_TESTS += vm_fault_t
|
||||
NV_CONFTEST_TYPE_COMPILE_TESTS += mmu_notifier_ops_invalidate_range
|
||||
NV_CONFTEST_TYPE_COMPILE_TESTS += mmu_notifier_ops_arch_invalidate_secondary_tlbs
|
||||
NV_CONFTEST_TYPE_COMPILE_TESTS += proc_ops
|
||||
NV_CONFTEST_TYPE_COMPILE_TESTS += timespec64
|
||||
NV_CONFTEST_TYPE_COMPILE_TESTS += mm_has_mmap_lock
|
||||
@ -113,4 +116,3 @@ NV_CONFTEST_TYPE_COMPILE_TESTS += mpol_preferred_many_present
|
||||
NV_CONFTEST_TYPE_COMPILE_TESTS += mmu_interval_notifier
|
||||
|
||||
NV_CONFTEST_SYMBOL_COMPILE_TESTS += is_export_symbol_present_int_active_memcg
|
||||
NV_CONFTEST_SYMBOL_COMPILE_TESTS += is_export_symbol_present_migrate_vma_setup
|
||||
|
@ -24,11 +24,11 @@
|
||||
#include "nvstatus.h"
|
||||
|
||||
#if !defined(NV_PRINTF_STRING_SECTION)
|
||||
#if defined(NVRM) && NVCPU_IS_RISCV64
|
||||
#if defined(NVRM) && NVOS_IS_LIBOS
|
||||
#define NV_PRINTF_STRING_SECTION __attribute__ ((section (".logging")))
|
||||
#else // defined(NVRM) && NVCPU_IS_RISCV64
|
||||
#else // defined(NVRM) && NVOS_IS_LIBOS
|
||||
#define NV_PRINTF_STRING_SECTION
|
||||
#endif // defined(NVRM) && NVCPU_IS_RISCV64
|
||||
#endif // defined(NVRM) && NVOS_IS_LIBOS
|
||||
#endif // !defined(NV_PRINTF_STRING_SECTION)
|
||||
|
||||
/*
|
||||
|
@ -571,7 +571,6 @@ static void uvm_vm_open_managed_entry(struct vm_area_struct *vma)
|
||||
static void uvm_vm_close_managed(struct vm_area_struct *vma)
|
||||
{
|
||||
uvm_va_space_t *va_space = uvm_va_space_get(vma->vm_file);
|
||||
uvm_processor_id_t gpu_id;
|
||||
bool make_zombie = false;
|
||||
|
||||
if (current->mm != NULL)
|
||||
@ -606,12 +605,6 @@ static void uvm_vm_close_managed(struct vm_area_struct *vma)
|
||||
|
||||
uvm_destroy_vma_managed(vma, make_zombie);
|
||||
|
||||
// Notify GPU address spaces that the fault buffer needs to be flushed to
|
||||
// avoid finding stale entries that can be attributed to new VA ranges
|
||||
// reallocated at the same address.
|
||||
for_each_gpu_id_in_mask(gpu_id, &va_space->registered_gpu_va_spaces) {
|
||||
uvm_processor_mask_set_atomic(&va_space->needs_fault_buffer_flush, gpu_id);
|
||||
}
|
||||
uvm_va_space_up_write(va_space);
|
||||
|
||||
if (current->mm != NULL)
|
||||
|
@ -216,6 +216,10 @@ NV_STATUS UvmDeinitialize(void);
|
||||
// Note that it is not required to release VA ranges that were reserved with
|
||||
// UvmReserveVa().
|
||||
//
|
||||
// This is useful for per-process checkpoint and restore, where kernel-mode
|
||||
// state needs to be reconfigured to match the expectations of a pre-existing
|
||||
// user-mode process.
|
||||
//
|
||||
// UvmReopen() closes the open file returned by UvmGetFileDescriptor() and
|
||||
// replaces it with a new open file with the same name.
|
||||
//
|
||||
|
@ -114,6 +114,8 @@ static void flush_tlb_write_faults(uvm_gpu_va_space_t *gpu_va_space,
|
||||
{
|
||||
uvm_ats_fault_invalidate_t *ats_invalidate;
|
||||
|
||||
uvm_ats_smmu_invalidate_tlbs(gpu_va_space, addr, size);
|
||||
|
||||
if (client_type == UVM_FAULT_CLIENT_TYPE_GPC)
|
||||
ats_invalidate = &gpu_va_space->gpu->parent->fault_buffer_info.replayable.ats_invalidate;
|
||||
else
|
||||
@ -588,4 +590,3 @@ NV_STATUS uvm_ats_invalidate_tlbs(uvm_gpu_va_space_t *gpu_va_space,
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -29,8 +29,12 @@
|
||||
#include "uvm_va_space.h"
|
||||
#include "uvm_va_space_mm.h"
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/mm_types.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mmu_context.h>
|
||||
|
||||
// linux/sched/mm.h is needed for mmget_not_zero and mmput to get the mm
|
||||
// reference required for the iommu_sva_bind_device() call. This header is not
|
||||
@ -46,17 +50,271 @@
|
||||
#define UVM_IOMMU_SVA_BIND_DEVICE(dev, mm) iommu_sva_bind_device(dev, mm)
|
||||
#endif
|
||||
|
||||
// Base address of SMMU CMDQ-V for GSMMU0.
|
||||
#define SMMU_CMDQV_BASE_ADDR(smmu_base) (smmu_base + 0x200000)
|
||||
#define SMMU_CMDQV_BASE_LEN 0x00830000
|
||||
|
||||
// CMDQV configuration is done by firmware but we check status here.
|
||||
#define SMMU_CMDQV_CONFIG 0x0
|
||||
#define SMMU_CMDQV_CONFIG_CMDQV_EN BIT(0)
|
||||
|
||||
// Used to map a particular VCMDQ to a VINTF.
|
||||
#define SMMU_CMDQV_CMDQ_ALLOC_MAP(vcmdq_id) (0x200 + 0x4 * (vcmdq_id))
|
||||
#define SMMU_CMDQV_CMDQ_ALLOC_MAP_ALLOC BIT(0)
|
||||
|
||||
// Shift for the field containing the index of the virtual interface
|
||||
// owning the VCMDQ.
|
||||
#define SMMU_CMDQV_CMDQ_ALLOC_MAP_VIRT_INTF_INDX_SHIFT 15
|
||||
|
||||
// Base address for the VINTF registers.
|
||||
#define SMMU_VINTF_BASE_ADDR(cmdqv_base_addr, vintf_id) (cmdqv_base_addr + 0x1000 + 0x100 * (vintf_id))
|
||||
|
||||
// Virtual interface (VINTF) configuration registers. The WAR only
|
||||
// works on baremetal so we need to configure ourselves as the
|
||||
// hypervisor owner.
|
||||
#define SMMU_VINTF_CONFIG 0x0
|
||||
#define SMMU_VINTF_CONFIG_ENABLE BIT(0)
|
||||
#define SMMU_VINTF_CONFIG_HYP_OWN BIT(17)
|
||||
|
||||
#define SMMU_VINTF_STATUS 0x0
|
||||
#define SMMU_VINTF_STATUS_ENABLED BIT(0)
|
||||
|
||||
// Caclulates the base address for a particular VCMDQ instance.
|
||||
#define SMMU_VCMDQ_BASE_ADDR(cmdqv_base_addr, vcmdq_id) (cmdqv_base_addr + 0x10000 + 0x80 * (vcmdq_id))
|
||||
|
||||
// SMMU command queue consumer index register. Updated by SMMU
|
||||
// when commands are consumed.
|
||||
#define SMMU_VCMDQ_CONS 0x0
|
||||
|
||||
// SMMU command queue producer index register. Updated by UVM when
|
||||
// commands are added to the queue.
|
||||
#define SMMU_VCMDQ_PROD 0x4
|
||||
|
||||
// Configuration register used to enable a VCMDQ.
|
||||
#define SMMU_VCMDQ_CONFIG 0x8
|
||||
#define SMMU_VCMDQ_CONFIG_ENABLE BIT(0)
|
||||
|
||||
// Status register used to check the VCMDQ is enabled.
|
||||
#define SMMU_VCMDQ_STATUS 0xc
|
||||
#define SMMU_VCMDQ_STATUS_ENABLED BIT(0)
|
||||
|
||||
// Base address offset for the VCMDQ registers.
|
||||
#define SMMU_VCMDQ_CMDQ_BASE 0x10000
|
||||
|
||||
// Size of the command queue. Each command is 8 bytes and we can't
|
||||
// have a command queue greater than one page.
|
||||
#define SMMU_VCMDQ_CMDQ_BASE_LOG2SIZE 9
|
||||
#define SMMU_VCMDQ_CMDQ_ENTRIES (1UL << SMMU_VCMDQ_CMDQ_BASE_LOG2SIZE)
|
||||
|
||||
// We always use VINTF63 for the WAR
|
||||
#define VINTF 63
|
||||
static void smmu_vintf_write32(void __iomem *smmu_cmdqv_base, int reg, NvU32 val)
|
||||
{
|
||||
iowrite32(val, SMMU_VINTF_BASE_ADDR(smmu_cmdqv_base, VINTF) + reg);
|
||||
}
|
||||
|
||||
static NvU32 smmu_vintf_read32(void __iomem *smmu_cmdqv_base, int reg)
|
||||
{
|
||||
return ioread32(SMMU_VINTF_BASE_ADDR(smmu_cmdqv_base, VINTF) + reg);
|
||||
}
|
||||
|
||||
// We always use VCMDQ127 for the WAR
|
||||
#define VCMDQ 127
|
||||
void smmu_vcmdq_write32(void __iomem *smmu_cmdqv_base, int reg, NvU32 val)
|
||||
{
|
||||
iowrite32(val, SMMU_VCMDQ_BASE_ADDR(smmu_cmdqv_base, VCMDQ) + reg);
|
||||
}
|
||||
|
||||
NvU32 smmu_vcmdq_read32(void __iomem *smmu_cmdqv_base, int reg)
|
||||
{
|
||||
return ioread32(SMMU_VCMDQ_BASE_ADDR(smmu_cmdqv_base, VCMDQ) + reg);
|
||||
}
|
||||
|
||||
static void smmu_vcmdq_write64(void __iomem *smmu_cmdqv_base, int reg, NvU64 val)
|
||||
{
|
||||
iowrite64(val, SMMU_VCMDQ_BASE_ADDR(smmu_cmdqv_base, VCMDQ) + reg);
|
||||
}
|
||||
|
||||
// Fix for Bug 4130089: [GH180][r535] WAR for kernel not issuing SMMU
|
||||
// TLB invalidates on read-only to read-write upgrades
|
||||
static NV_STATUS uvm_ats_smmu_war_init(uvm_parent_gpu_t *parent_gpu)
|
||||
{
|
||||
uvm_spin_loop_t spin;
|
||||
NV_STATUS status;
|
||||
unsigned long cmdqv_config;
|
||||
void __iomem *smmu_cmdqv_base;
|
||||
struct acpi_iort_node *node;
|
||||
struct acpi_iort_smmu_v3 *iort_smmu;
|
||||
|
||||
node = *(struct acpi_iort_node **) dev_get_platdata(parent_gpu->pci_dev->dev.iommu->iommu_dev->dev->parent);
|
||||
iort_smmu = (struct acpi_iort_smmu_v3 *) node->node_data;
|
||||
|
||||
smmu_cmdqv_base = ioremap(SMMU_CMDQV_BASE_ADDR(iort_smmu->base_address), SMMU_CMDQV_BASE_LEN);
|
||||
if (!smmu_cmdqv_base)
|
||||
return NV_ERR_NO_MEMORY;
|
||||
|
||||
parent_gpu->smmu_war.smmu_cmdqv_base = smmu_cmdqv_base;
|
||||
cmdqv_config = ioread32(smmu_cmdqv_base + SMMU_CMDQV_CONFIG);
|
||||
if (!(cmdqv_config & SMMU_CMDQV_CONFIG_CMDQV_EN)) {
|
||||
status = NV_ERR_OBJECT_NOT_FOUND;
|
||||
goto out;
|
||||
}
|
||||
|
||||
// Allocate SMMU CMDQ pages for WAR
|
||||
parent_gpu->smmu_war.smmu_cmdq = alloc_page(NV_UVM_GFP_FLAGS | __GFP_ZERO);
|
||||
if (!parent_gpu->smmu_war.smmu_cmdq) {
|
||||
status = NV_ERR_NO_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
// Initialise VINTF for the WAR
|
||||
smmu_vintf_write32(smmu_cmdqv_base, SMMU_VINTF_CONFIG, SMMU_VINTF_CONFIG_ENABLE | SMMU_VINTF_CONFIG_HYP_OWN);
|
||||
UVM_SPIN_WHILE(!(smmu_vintf_read32(smmu_cmdqv_base, SMMU_VINTF_STATUS) & SMMU_VINTF_STATUS_ENABLED), &spin);
|
||||
|
||||
// Allocate VCMDQ to VINTF
|
||||
iowrite32((VINTF << SMMU_CMDQV_CMDQ_ALLOC_MAP_VIRT_INTF_INDX_SHIFT) | SMMU_CMDQV_CMDQ_ALLOC_MAP_ALLOC,
|
||||
smmu_cmdqv_base + SMMU_CMDQV_CMDQ_ALLOC_MAP(VCMDQ));
|
||||
|
||||
BUILD_BUG_ON((SMMU_VCMDQ_CMDQ_BASE_LOG2SIZE + 3) > PAGE_SHIFT);
|
||||
smmu_vcmdq_write64(smmu_cmdqv_base, SMMU_VCMDQ_CMDQ_BASE,
|
||||
page_to_phys(parent_gpu->smmu_war.smmu_cmdq) | SMMU_VCMDQ_CMDQ_BASE_LOG2SIZE);
|
||||
smmu_vcmdq_write32(smmu_cmdqv_base, SMMU_VCMDQ_CONS, 0);
|
||||
smmu_vcmdq_write32(smmu_cmdqv_base, SMMU_VCMDQ_PROD, 0);
|
||||
smmu_vcmdq_write32(smmu_cmdqv_base, SMMU_VCMDQ_CONFIG, SMMU_VCMDQ_CONFIG_ENABLE);
|
||||
UVM_SPIN_WHILE(!(smmu_vcmdq_read32(smmu_cmdqv_base, SMMU_VCMDQ_STATUS) & SMMU_VCMDQ_STATUS_ENABLED), &spin);
|
||||
|
||||
uvm_mutex_init(&parent_gpu->smmu_war.smmu_lock, UVM_LOCK_ORDER_LEAF);
|
||||
parent_gpu->smmu_war.smmu_prod = 0;
|
||||
parent_gpu->smmu_war.smmu_cons = 0;
|
||||
|
||||
return NV_OK;
|
||||
|
||||
out:
|
||||
iounmap(parent_gpu->smmu_war.smmu_cmdqv_base);
|
||||
parent_gpu->smmu_war.smmu_cmdqv_base = NULL;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void uvm_ats_smmu_war_deinit(uvm_parent_gpu_t *parent_gpu)
|
||||
{
|
||||
void __iomem *smmu_cmdqv_base = parent_gpu->smmu_war.smmu_cmdqv_base;
|
||||
NvU32 cmdq_alloc_map;
|
||||
|
||||
if (parent_gpu->smmu_war.smmu_cmdqv_base) {
|
||||
smmu_vcmdq_write32(smmu_cmdqv_base, SMMU_VCMDQ_CONFIG, 0);
|
||||
cmdq_alloc_map = ioread32(smmu_cmdqv_base + SMMU_CMDQV_CMDQ_ALLOC_MAP(VCMDQ));
|
||||
iowrite32(cmdq_alloc_map & SMMU_CMDQV_CMDQ_ALLOC_MAP_ALLOC, smmu_cmdqv_base + SMMU_CMDQV_CMDQ_ALLOC_MAP(VCMDQ));
|
||||
smmu_vintf_write32(smmu_cmdqv_base, SMMU_VINTF_CONFIG, 0);
|
||||
}
|
||||
|
||||
if (parent_gpu->smmu_war.smmu_cmdq)
|
||||
__free_page(parent_gpu->smmu_war.smmu_cmdq);
|
||||
|
||||
if (parent_gpu->smmu_war.smmu_cmdqv_base)
|
||||
iounmap(parent_gpu->smmu_war.smmu_cmdqv_base);
|
||||
}
|
||||
|
||||
// The SMMU on ARM64 can run under different translation regimes depending on
|
||||
// what features the OS and CPU variant support. The CPU for GH180 supports
|
||||
// virtualisation extensions and starts the kernel at EL2 meaning SMMU operates
|
||||
// under the NS-EL2-E2H translation regime. Therefore we need to use the
|
||||
// TLBI_EL2_* commands which invalidate TLB entries created under this
|
||||
// translation regime.
|
||||
#define CMDQ_OP_TLBI_EL2_ASID 0x21;
|
||||
#define CMDQ_OP_TLBI_EL2_VA 0x22;
|
||||
#define CMDQ_OP_CMD_SYNC 0x46
|
||||
|
||||
// Use the same maximum as used for MAX_TLBI_OPS in the upstream
|
||||
// kernel.
|
||||
#define UVM_MAX_TLBI_OPS (1UL << (PAGE_SHIFT - 3))
|
||||
|
||||
#if UVM_ATS_SMMU_WAR_REQUIRED()
|
||||
void uvm_ats_smmu_invalidate_tlbs(uvm_gpu_va_space_t *gpu_va_space, NvU64 addr, size_t size)
|
||||
{
|
||||
struct mm_struct *mm = gpu_va_space->va_space->va_space_mm.mm;
|
||||
uvm_parent_gpu_t *parent_gpu = gpu_va_space->gpu->parent;
|
||||
struct {
|
||||
NvU64 low;
|
||||
NvU64 high;
|
||||
} *vcmdq;
|
||||
unsigned long vcmdq_prod;
|
||||
NvU64 end;
|
||||
uvm_spin_loop_t spin;
|
||||
NvU16 asid;
|
||||
|
||||
if (!parent_gpu->smmu_war.smmu_cmdqv_base)
|
||||
return;
|
||||
|
||||
asid = arm64_mm_context_get(mm);
|
||||
vcmdq = kmap(parent_gpu->smmu_war.smmu_cmdq);
|
||||
uvm_mutex_lock(&parent_gpu->smmu_war.smmu_lock);
|
||||
vcmdq_prod = parent_gpu->smmu_war.smmu_prod;
|
||||
|
||||
// Our queue management is very simple. The mutex prevents multiple
|
||||
// producers writing to the queue and all our commands require waiting for
|
||||
// the queue to drain so we know it's empty. If we can't fit enough commands
|
||||
// in the queue we just invalidate the whole ASID.
|
||||
//
|
||||
// The command queue is a cirular buffer with the MSB representing a wrap
|
||||
// bit that must toggle on each wrap. See the SMMU architecture
|
||||
// specification for more details.
|
||||
//
|
||||
// SMMU_VCMDQ_CMDQ_ENTRIES - 1 because we need to leave space for the
|
||||
// CMD_SYNC.
|
||||
if ((size >> PAGE_SHIFT) > min(UVM_MAX_TLBI_OPS, SMMU_VCMDQ_CMDQ_ENTRIES - 1)) {
|
||||
vcmdq[vcmdq_prod % SMMU_VCMDQ_CMDQ_ENTRIES].low = CMDQ_OP_TLBI_EL2_ASID;
|
||||
vcmdq[vcmdq_prod % SMMU_VCMDQ_CMDQ_ENTRIES].low |= (NvU64) asid << 48;
|
||||
vcmdq[vcmdq_prod % SMMU_VCMDQ_CMDQ_ENTRIES].high = 0;
|
||||
vcmdq_prod++;
|
||||
}
|
||||
else {
|
||||
for (end = addr + size; addr < end; addr += PAGE_SIZE) {
|
||||
vcmdq[vcmdq_prod % SMMU_VCMDQ_CMDQ_ENTRIES].low = CMDQ_OP_TLBI_EL2_VA;
|
||||
vcmdq[vcmdq_prod % SMMU_VCMDQ_CMDQ_ENTRIES].low |= (NvU64) asid << 48;
|
||||
vcmdq[vcmdq_prod % SMMU_VCMDQ_CMDQ_ENTRIES].high = addr & ~((1UL << 12) - 1);
|
||||
vcmdq_prod++;
|
||||
}
|
||||
}
|
||||
|
||||
vcmdq[vcmdq_prod % SMMU_VCMDQ_CMDQ_ENTRIES].low = CMDQ_OP_CMD_SYNC;
|
||||
vcmdq[vcmdq_prod % SMMU_VCMDQ_CMDQ_ENTRIES].high = 0x0;
|
||||
vcmdq_prod++;
|
||||
|
||||
// MSB is the wrap bit
|
||||
vcmdq_prod &= (1UL << (SMMU_VCMDQ_CMDQ_BASE_LOG2SIZE + 1)) - 1;
|
||||
parent_gpu->smmu_war.smmu_prod = vcmdq_prod;
|
||||
smmu_vcmdq_write32(parent_gpu->smmu_war.smmu_cmdqv_base, SMMU_VCMDQ_PROD, parent_gpu->smmu_war.smmu_prod);
|
||||
|
||||
UVM_SPIN_WHILE(
|
||||
(smmu_vcmdq_read32(parent_gpu->smmu_war.smmu_cmdqv_base, SMMU_VCMDQ_CONS) & GENMASK(19, 0)) != vcmdq_prod,
|
||||
&spin);
|
||||
|
||||
uvm_mutex_unlock(&parent_gpu->smmu_war.smmu_lock);
|
||||
kunmap(parent_gpu->smmu_war.smmu_cmdq);
|
||||
arm64_mm_context_put(mm);
|
||||
}
|
||||
#endif
|
||||
|
||||
NV_STATUS uvm_ats_sva_add_gpu(uvm_parent_gpu_t *parent_gpu)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = iommu_dev_enable_feature(&parent_gpu->pci_dev->dev, IOMMU_DEV_FEAT_SVA);
|
||||
if (ret)
|
||||
return errno_to_nv_status(ret);
|
||||
|
||||
return errno_to_nv_status(ret);
|
||||
if (UVM_ATS_SMMU_WAR_REQUIRED())
|
||||
return uvm_ats_smmu_war_init(parent_gpu);
|
||||
else
|
||||
return NV_OK;
|
||||
}
|
||||
|
||||
void uvm_ats_sva_remove_gpu(uvm_parent_gpu_t *parent_gpu)
|
||||
{
|
||||
if (UVM_ATS_SMMU_WAR_REQUIRED())
|
||||
uvm_ats_smmu_war_deinit(parent_gpu);
|
||||
|
||||
iommu_dev_disable_feature(&parent_gpu->pci_dev->dev, IOMMU_DEV_FEAT_SVA);
|
||||
}
|
||||
|
||||
|
@ -53,6 +53,17 @@
|
||||
#define UVM_ATS_SVA_SUPPORTED() 0
|
||||
#endif
|
||||
|
||||
// If NV_ARCH_INVALIDATE_SECONDARY_TLBS is defined it means the upstream fix is
|
||||
// in place so no need for the WAR from Bug 4130089: [GH180][r535] WAR for
|
||||
// kernel not issuing SMMU TLB invalidates on read-only
|
||||
#if defined(NV_ARCH_INVALIDATE_SECONDARY_TLBS)
|
||||
#define UVM_ATS_SMMU_WAR_REQUIRED() 0
|
||||
#elif NVCPU_IS_AARCH64
|
||||
#define UVM_ATS_SMMU_WAR_REQUIRED() 1
|
||||
#else
|
||||
#define UVM_ATS_SMMU_WAR_REQUIRED() 0
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int placeholder;
|
||||
@ -81,6 +92,17 @@ typedef struct
|
||||
|
||||
// LOCKING: None
|
||||
void uvm_ats_sva_unregister_gpu_va_space(uvm_gpu_va_space_t *gpu_va_space);
|
||||
|
||||
// Fix for Bug 4130089: [GH180][r535] WAR for kernel not issuing SMMU
|
||||
// TLB invalidates on read-only to read-write upgrades
|
||||
#if UVM_ATS_SMMU_WAR_REQUIRED()
|
||||
void uvm_ats_smmu_invalidate_tlbs(uvm_gpu_va_space_t *gpu_va_space, NvU64 addr, size_t size);
|
||||
#else
|
||||
static void uvm_ats_smmu_invalidate_tlbs(uvm_gpu_va_space_t *gpu_va_space, NvU64 addr, size_t size)
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
static NV_STATUS uvm_ats_sva_add_gpu(uvm_parent_gpu_t *parent_gpu)
|
||||
{
|
||||
@ -111,6 +133,11 @@ typedef struct
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void uvm_ats_smmu_invalidate_tlbs(uvm_gpu_va_space_t *gpu_va_space, NvU64 addr, size_t size)
|
||||
{
|
||||
|
||||
}
|
||||
#endif // UVM_ATS_SVA_SUPPORTED
|
||||
|
||||
#endif // __UVM_ATS_SVA_H__
|
||||
|
@ -2683,7 +2683,7 @@ static void init_channel_manager_conf(uvm_channel_manager_t *manager)
|
||||
// caches vidmem (and sysmem), we place GPFIFO and GPPUT on sysmem to avoid
|
||||
// cache thrash. The memory access latency is reduced, despite the required
|
||||
// access through the bus, because no cache coherence message is exchanged.
|
||||
if (uvm_gpu_is_coherent(gpu->parent)) {
|
||||
if (uvm_parent_gpu_is_coherent(gpu->parent)) {
|
||||
manager->conf.gpfifo_loc = UVM_BUFFER_LOCATION_SYS;
|
||||
|
||||
// On GPUs with limited ESCHED addressing range, e.g., Volta on P9, RM
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2013-2021 NVIDIA Corporation
|
||||
Copyright (c) 2013-2023 NVIDIA Corporation
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
@ -233,18 +233,6 @@ 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;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2013-2021 NVIDIA Corporation
|
||||
Copyright (c) 2013-2023 NVIDIA Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
@ -282,9 +282,6 @@ static inline void kmem_cache_destroy_safe(struct kmem_cache **ppCache)
|
||||
}
|
||||
}
|
||||
|
||||
static const uid_t UVM_ROOT_UID = 0;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
NvU64 start_time_ns;
|
||||
@ -335,7 +332,6 @@ NV_STATUS errno_to_nv_status(int errnoCode);
|
||||
int nv_status_to_errno(NV_STATUS status);
|
||||
unsigned uvm_get_stale_process_id(void);
|
||||
unsigned uvm_get_stale_thread_id(void);
|
||||
NvBool uvm_user_id_security_check(uid_t euidTarget);
|
||||
|
||||
extern int uvm_enable_builtin_tests;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2021-2023 NVIDIA Corporation
|
||||
Copyright (c) 2021 NVIDIA Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
@ -54,26 +54,23 @@ bool uvm_conf_computing_mode_is_hcc(const uvm_gpu_t *gpu)
|
||||
return uvm_conf_computing_get_mode(gpu->parent) == UVM_GPU_CONF_COMPUTE_MODE_HCC;
|
||||
}
|
||||
|
||||
void uvm_conf_computing_check_parent_gpu(const uvm_parent_gpu_t *parent)
|
||||
NV_STATUS uvm_conf_computing_init_parent_gpu(const uvm_parent_gpu_t *parent)
|
||||
{
|
||||
uvm_gpu_t *first_gpu;
|
||||
UvmGpuConfComputeMode cc, sys_cc;
|
||||
uvm_gpu_t *first;
|
||||
|
||||
uvm_assert_mutex_locked(&g_uvm_global.global_lock);
|
||||
|
||||
// The Confidential Computing state of the GPU should match that of the
|
||||
// system.
|
||||
UVM_ASSERT(uvm_conf_computing_mode_enabled_parent(parent) == g_uvm_global.conf_computing_enabled);
|
||||
|
||||
// TODO: Bug 2844714: since we have no routine to traverse parent GPUs,
|
||||
// find first child GPU and get its parent.
|
||||
first_gpu = uvm_global_processor_mask_find_first_gpu(&g_uvm_global.retained_gpus);
|
||||
if (first_gpu == NULL)
|
||||
return;
|
||||
first = uvm_global_processor_mask_find_first_gpu(&g_uvm_global.retained_gpus);
|
||||
if (!first)
|
||||
return NV_OK;
|
||||
|
||||
// All GPUs derive Confidential Computing status from their parent. By
|
||||
// current policy all parent GPUs have identical Confidential Computing
|
||||
// status.
|
||||
UVM_ASSERT(uvm_conf_computing_get_mode(parent) == uvm_conf_computing_get_mode(first_gpu->parent));
|
||||
sys_cc = uvm_conf_computing_get_mode(first->parent);
|
||||
cc = uvm_conf_computing_get_mode(parent);
|
||||
|
||||
return cc == sys_cc ? NV_OK : NV_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
static void dma_buffer_destroy_locked(uvm_conf_computing_dma_buffer_pool_t *dma_buffer_pool,
|
||||
|
@ -60,8 +60,10 @@
|
||||
// UVM_METHOD_SIZE * 2 * 10 = 80.
|
||||
#define UVM_CONF_COMPUTING_SIGN_BUF_MAX_SIZE 80
|
||||
|
||||
void uvm_conf_computing_check_parent_gpu(const uvm_parent_gpu_t *parent);
|
||||
|
||||
// All GPUs derive confidential computing status from their parent.
|
||||
// By current policy all parent GPUs have identical confidential
|
||||
// computing status.
|
||||
NV_STATUS uvm_conf_computing_init_parent_gpu(const uvm_parent_gpu_t *parent);
|
||||
bool uvm_conf_computing_mode_enabled_parent(const uvm_parent_gpu_t *parent);
|
||||
bool uvm_conf_computing_mode_enabled(const uvm_gpu_t *gpu);
|
||||
bool uvm_conf_computing_mode_is_hcc(const uvm_gpu_t *gpu);
|
||||
|
@ -71,6 +71,11 @@ static void uvm_unregister_callbacks(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void sev_init(const UvmPlatformInfo *platform_info)
|
||||
{
|
||||
g_uvm_global.sev_enabled = platform_info->sevEnabled;
|
||||
}
|
||||
|
||||
NV_STATUS uvm_global_init(void)
|
||||
{
|
||||
NV_STATUS status;
|
||||
@ -119,7 +124,8 @@ NV_STATUS uvm_global_init(void)
|
||||
|
||||
uvm_ats_init(&platform_info);
|
||||
g_uvm_global.num_simulated_devices = 0;
|
||||
g_uvm_global.conf_computing_enabled = platform_info.confComputingEnabled;
|
||||
|
||||
sev_init(&platform_info);
|
||||
|
||||
status = uvm_gpu_init();
|
||||
if (status != NV_OK) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2015-2023 NVIDIA Corporation
|
||||
Copyright (c) 2015-2021 NVIDIA Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
@ -143,16 +143,11 @@ struct uvm_global_struct
|
||||
struct page *page;
|
||||
} unload_state;
|
||||
|
||||
// True if the VM has AMD's SEV, or equivalent HW security extensions such
|
||||
// as Intel's TDX, enabled. The flag is always false on the host.
|
||||
//
|
||||
// This value moves in tandem with that of Confidential Computing in the
|
||||
// GPU(s) in all supported configurations, so it is used as a proxy for the
|
||||
// Confidential Computing state.
|
||||
//
|
||||
// This field is set once during global initialization (uvm_global_init),
|
||||
// and can be read afterwards without acquiring any locks.
|
||||
bool conf_computing_enabled;
|
||||
// AMD Secure Encrypted Virtualization (SEV) status. True if VM has SEV
|
||||
// enabled. This field is set once during global initialization
|
||||
// (uvm_global_init), and can be read afterwards without acquiring any
|
||||
// locks.
|
||||
bool sev_enabled;
|
||||
};
|
||||
|
||||
// Initialize global uvm state
|
||||
@ -238,10 +233,8 @@ static uvm_gpu_t *uvm_gpu_get_by_processor_id(uvm_processor_id_t id)
|
||||
return gpu;
|
||||
}
|
||||
|
||||
static uvmGpuSessionHandle uvm_gpu_session_handle(uvm_gpu_t *gpu)
|
||||
static uvmGpuSessionHandle uvm_global_session_handle(void)
|
||||
{
|
||||
if (gpu->parent->smc.enabled)
|
||||
return gpu->smc.rm_session_handle;
|
||||
return g_uvm_global.rm_session_handle;
|
||||
}
|
||||
|
||||
|
@ -99,8 +99,8 @@ static void fill_gpu_info(uvm_parent_gpu_t *parent_gpu, const UvmGpuInfo *gpu_in
|
||||
parent_gpu->system_bus.link_rate_mbyte_per_s = gpu_info->sysmemLinkRateMBps;
|
||||
|
||||
if (gpu_info->systemMemoryWindowSize > 0) {
|
||||
// memory_window_end is inclusive but uvm_gpu_is_coherent() checks
|
||||
// memory_window_end > memory_window_start as its condition.
|
||||
// memory_window_end is inclusive but uvm_parent_gpu_is_coherent()
|
||||
// checks memory_window_end > memory_window_start as its condition.
|
||||
UVM_ASSERT(gpu_info->systemMemoryWindowSize > 1);
|
||||
parent_gpu->system_bus.memory_window_start = gpu_info->systemMemoryWindowStart;
|
||||
parent_gpu->system_bus.memory_window_end = gpu_info->systemMemoryWindowStart +
|
||||
@ -136,12 +136,12 @@ static NV_STATUS get_gpu_caps(uvm_gpu_t *gpu)
|
||||
return status;
|
||||
|
||||
if (gpu_caps.numaEnabled) {
|
||||
UVM_ASSERT(uvm_gpu_is_coherent(gpu->parent));
|
||||
UVM_ASSERT(uvm_parent_gpu_is_coherent(gpu->parent));
|
||||
gpu->mem_info.numa.enabled = true;
|
||||
gpu->mem_info.numa.node_id = gpu_caps.numaNodeId;
|
||||
}
|
||||
else {
|
||||
UVM_ASSERT(!uvm_gpu_is_coherent(gpu->parent));
|
||||
UVM_ASSERT(!uvm_parent_gpu_is_coherent(gpu->parent));
|
||||
}
|
||||
|
||||
return NV_OK;
|
||||
@ -1089,7 +1089,7 @@ static NV_STATUS init_parent_gpu(uvm_parent_gpu_t *parent_gpu,
|
||||
{
|
||||
NV_STATUS status;
|
||||
|
||||
status = uvm_rm_locked_call(nvUvmInterfaceDeviceCreate(g_uvm_global.rm_session_handle,
|
||||
status = uvm_rm_locked_call(nvUvmInterfaceDeviceCreate(uvm_global_session_handle(),
|
||||
gpu_info,
|
||||
gpu_uuid,
|
||||
&parent_gpu->rm_device,
|
||||
@ -1099,7 +1099,12 @@ static NV_STATUS init_parent_gpu(uvm_parent_gpu_t *parent_gpu,
|
||||
return status;
|
||||
}
|
||||
|
||||
uvm_conf_computing_check_parent_gpu(parent_gpu);
|
||||
status = uvm_conf_computing_init_parent_gpu(parent_gpu);
|
||||
if (status != NV_OK) {
|
||||
UVM_ERR_PRINT("Confidential computing: %s, GPU %s\n",
|
||||
nvstatusToString(status), parent_gpu->name);
|
||||
return status;
|
||||
}
|
||||
|
||||
parent_gpu->pci_dev = gpu_platform_info->pci_dev;
|
||||
parent_gpu->closest_cpu_numa_node = dev_to_node(&parent_gpu->pci_dev->dev);
|
||||
@ -1161,19 +1166,8 @@ static NV_STATUS init_gpu(uvm_gpu_t *gpu, const UvmGpuInfo *gpu_info)
|
||||
{
|
||||
NV_STATUS status;
|
||||
|
||||
// Presently, an RM client can only subscribe to a single partition per
|
||||
// GPU. Therefore, UVM needs to create several RM clients. For simplicity,
|
||||
// and since P2P is not supported when SMC partitions are created, we
|
||||
// create a client (session) per GPU partition.
|
||||
if (gpu->parent->smc.enabled) {
|
||||
UvmPlatformInfo platform_info;
|
||||
status = uvm_rm_locked_call(nvUvmInterfaceSessionCreate(&gpu->smc.rm_session_handle, &platform_info));
|
||||
if (status != NV_OK) {
|
||||
UVM_ERR_PRINT("Creating RM session failed: %s\n", nvstatusToString(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
status = uvm_rm_locked_call(nvUvmInterfaceDeviceCreate(uvm_gpu_session_handle(gpu),
|
||||
status = uvm_rm_locked_call(nvUvmInterfaceDeviceCreate(uvm_global_session_handle(),
|
||||
gpu_info,
|
||||
uvm_gpu_uuid(gpu),
|
||||
&gpu->smc.rm_device,
|
||||
@ -1543,9 +1537,6 @@ static void deinit_gpu(uvm_gpu_t *gpu)
|
||||
if (gpu->parent->smc.enabled) {
|
||||
if (gpu->smc.rm_device != 0)
|
||||
uvm_rm_locked_call_void(nvUvmInterfaceDeviceDestroy(gpu->smc.rm_device));
|
||||
|
||||
if (gpu->smc.rm_session_handle != 0)
|
||||
uvm_rm_locked_call_void(nvUvmInterfaceSessionDestroy(gpu->smc.rm_session_handle));
|
||||
}
|
||||
|
||||
gpu->magic = 0;
|
||||
@ -2575,7 +2566,7 @@ static void disable_peer_access(uvm_gpu_t *gpu0, uvm_gpu_t *gpu1)
|
||||
uvm_mmu_destroy_peer_identity_mappings(gpu0, gpu1);
|
||||
uvm_mmu_destroy_peer_identity_mappings(gpu1, gpu0);
|
||||
|
||||
uvm_rm_locked_call_void(nvUvmInterfaceP2pObjectDestroy(uvm_gpu_session_handle(gpu0), p2p_handle));
|
||||
uvm_rm_locked_call_void(nvUvmInterfaceP2pObjectDestroy(uvm_global_session_handle(), p2p_handle));
|
||||
|
||||
UVM_ASSERT(uvm_gpu_get(gpu0->global_id) == gpu0);
|
||||
UVM_ASSERT(uvm_gpu_get(gpu1->global_id) == gpu1);
|
||||
@ -2701,9 +2692,9 @@ uvm_processor_id_t uvm_gpu_get_processor_id_by_address(uvm_gpu_t *gpu, uvm_gpu_p
|
||||
return id;
|
||||
}
|
||||
|
||||
uvm_gpu_peer_t *uvm_gpu_index_peer_caps(const uvm_gpu_id_t gpu_id1, const uvm_gpu_id_t gpu_id2)
|
||||
uvm_gpu_peer_t *uvm_gpu_index_peer_caps(const uvm_gpu_id_t gpu_id0, const uvm_gpu_id_t gpu_id1)
|
||||
{
|
||||
NvU32 table_index = uvm_gpu_peer_table_index(gpu_id1, gpu_id2);
|
||||
NvU32 table_index = uvm_gpu_peer_table_index(gpu_id0, gpu_id1);
|
||||
return &g_uvm_global.peers[table_index];
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2015-2022 NVIDIA Corporation
|
||||
Copyright (c) 2015-2023 NVIDIA Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
@ -167,7 +167,7 @@ struct uvm_service_block_context_struct
|
||||
} per_processor_masks[UVM_ID_MAX_PROCESSORS];
|
||||
|
||||
// State used by the VA block routines called by the servicing routine
|
||||
uvm_va_block_context_t block_context;
|
||||
uvm_va_block_context_t *block_context;
|
||||
|
||||
// Prefetch state hint
|
||||
uvm_perf_prefetch_hint_t prefetch_hint;
|
||||
@ -263,7 +263,10 @@ struct uvm_fault_service_batch_context_struct
|
||||
|
||||
NvU32 num_coalesced_faults;
|
||||
|
||||
bool has_fatal_faults;
|
||||
// One of the VA spaces in this batch which had fatal faults. If NULL, no
|
||||
// faults were fatal. More than one VA space could have fatal faults, but we
|
||||
// pick one to be the target of the cancel sequence.
|
||||
uvm_va_space_t *fatal_va_space;
|
||||
|
||||
bool has_throttled_faults;
|
||||
|
||||
@ -825,8 +828,6 @@ struct uvm_gpu_struct
|
||||
{
|
||||
NvU32 swizz_id;
|
||||
|
||||
uvmGpuSessionHandle rm_session_handle;
|
||||
|
||||
// RM device handle used in many of the UVM/RM APIs.
|
||||
//
|
||||
// Do not read this field directly, use uvm_gpu_device_handle instead.
|
||||
@ -1162,6 +1163,16 @@ struct uvm_parent_gpu_struct
|
||||
NvU64 memory_window_start;
|
||||
NvU64 memory_window_end;
|
||||
} system_bus;
|
||||
|
||||
// WAR to issue ATS TLB invalidation commands ourselves.
|
||||
struct
|
||||
{
|
||||
uvm_mutex_t smmu_lock;
|
||||
struct page *smmu_cmdq;
|
||||
void __iomem *smmu_cmdqv_base;
|
||||
unsigned long smmu_prod;
|
||||
unsigned long smmu_cons;
|
||||
} smmu_war;
|
||||
};
|
||||
|
||||
static const char *uvm_gpu_name(uvm_gpu_t *gpu)
|
||||
@ -1336,7 +1347,7 @@ static NvU64 uvm_gpu_retained_count(uvm_gpu_t *gpu)
|
||||
void uvm_parent_gpu_kref_put(uvm_parent_gpu_t *gpu);
|
||||
|
||||
// Calculates peer table index using GPU ids.
|
||||
NvU32 uvm_gpu_peer_table_index(uvm_gpu_id_t gpu_id1, uvm_gpu_id_t gpu_id2);
|
||||
NvU32 uvm_gpu_peer_table_index(const uvm_gpu_id_t gpu_id0, const uvm_gpu_id_t gpu_id1);
|
||||
|
||||
// Either retains an existing PCIe peer entry or creates a new one. In both
|
||||
// cases the two GPUs are also each retained.
|
||||
@ -1355,7 +1366,7 @@ uvm_aperture_t uvm_gpu_peer_aperture(uvm_gpu_t *local_gpu, uvm_gpu_t *remote_gpu
|
||||
uvm_processor_id_t uvm_gpu_get_processor_id_by_address(uvm_gpu_t *gpu, uvm_gpu_phys_address_t addr);
|
||||
|
||||
// Get the P2P capabilities between the gpus with the given indexes
|
||||
uvm_gpu_peer_t *uvm_gpu_index_peer_caps(uvm_gpu_id_t gpu_id1, uvm_gpu_id_t gpu_id2);
|
||||
uvm_gpu_peer_t *uvm_gpu_index_peer_caps(const uvm_gpu_id_t gpu_id0, const uvm_gpu_id_t gpu_id1);
|
||||
|
||||
// Get the P2P capabilities between the given gpus
|
||||
static uvm_gpu_peer_t *uvm_gpu_peer_caps(const uvm_gpu_t *gpu0, const uvm_gpu_t *gpu1)
|
||||
@ -1363,10 +1374,10 @@ static uvm_gpu_peer_t *uvm_gpu_peer_caps(const uvm_gpu_t *gpu0, const uvm_gpu_t
|
||||
return uvm_gpu_index_peer_caps(gpu0->id, gpu1->id);
|
||||
}
|
||||
|
||||
static bool uvm_gpus_are_nvswitch_connected(uvm_gpu_t *gpu1, uvm_gpu_t *gpu2)
|
||||
static bool uvm_gpus_are_nvswitch_connected(const uvm_gpu_t *gpu0, const uvm_gpu_t *gpu1)
|
||||
{
|
||||
if (gpu1->parent->nvswitch_info.is_nvswitch_connected && gpu2->parent->nvswitch_info.is_nvswitch_connected) {
|
||||
UVM_ASSERT(uvm_gpu_peer_caps(gpu1, gpu2)->link_type >= UVM_GPU_LINK_NVLINK_2);
|
||||
if (gpu0->parent->nvswitch_info.is_nvswitch_connected && gpu1->parent->nvswitch_info.is_nvswitch_connected) {
|
||||
UVM_ASSERT(uvm_gpu_peer_caps(gpu0, gpu1)->link_type >= UVM_GPU_LINK_NVLINK_2);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1511,7 +1522,7 @@ bool uvm_gpu_can_address_kernel(uvm_gpu_t *gpu, NvU64 addr, NvU64 size);
|
||||
// addresses.
|
||||
NvU64 uvm_parent_gpu_canonical_address(uvm_parent_gpu_t *parent_gpu, NvU64 addr);
|
||||
|
||||
static bool uvm_gpu_is_coherent(const uvm_parent_gpu_t *parent_gpu)
|
||||
static bool uvm_parent_gpu_is_coherent(const uvm_parent_gpu_t *parent_gpu)
|
||||
{
|
||||
return parent_gpu->system_bus.memory_window_end > parent_gpu->system_bus.memory_window_start;
|
||||
}
|
||||
|
@ -985,7 +985,7 @@ static NV_STATUS service_va_block_locked(uvm_processor_id_t processor,
|
||||
return NV_OK;
|
||||
|
||||
if (uvm_processor_mask_test(&va_block->resident, processor))
|
||||
residency_mask = uvm_va_block_resident_mask_get(va_block, processor);
|
||||
residency_mask = uvm_va_block_resident_mask_get(va_block, processor, NUMA_NO_NODE);
|
||||
else
|
||||
residency_mask = NULL;
|
||||
|
||||
@ -1036,8 +1036,8 @@ static NV_STATUS service_va_block_locked(uvm_processor_id_t processor,
|
||||
|
||||
// If the underlying VMA is gone, skip HMM migrations.
|
||||
if (uvm_va_block_is_hmm(va_block)) {
|
||||
status = uvm_hmm_find_vma(service_context->block_context.mm,
|
||||
&service_context->block_context.hmm.vma,
|
||||
status = uvm_hmm_find_vma(service_context->block_context->mm,
|
||||
&service_context->block_context->hmm.vma,
|
||||
address);
|
||||
if (status == NV_ERR_INVALID_ADDRESS)
|
||||
continue;
|
||||
@ -1048,7 +1048,7 @@ static NV_STATUS service_va_block_locked(uvm_processor_id_t processor,
|
||||
policy = uvm_va_policy_get(va_block, address);
|
||||
|
||||
new_residency = uvm_va_block_select_residency(va_block,
|
||||
&service_context->block_context,
|
||||
service_context->block_context,
|
||||
page_index,
|
||||
processor,
|
||||
uvm_fault_access_type_mask_bit(UVM_FAULT_ACCESS_TYPE_PREFETCH),
|
||||
@ -1083,7 +1083,7 @@ static NV_STATUS service_va_block_locked(uvm_processor_id_t processor,
|
||||
// Remove pages that are already resident in the destination processors
|
||||
for_each_id_in_mask(id, &update_processors) {
|
||||
bool migrate_pages;
|
||||
uvm_page_mask_t *residency_mask = uvm_va_block_resident_mask_get(va_block, id);
|
||||
uvm_page_mask_t *residency_mask = uvm_va_block_resident_mask_get(va_block, id, NUMA_NO_NODE);
|
||||
UVM_ASSERT(residency_mask);
|
||||
|
||||
migrate_pages = uvm_page_mask_andnot(&service_context->per_processor_masks[uvm_id_value(id)].new_residency,
|
||||
@ -1101,9 +1101,9 @@ static NV_STATUS service_va_block_locked(uvm_processor_id_t processor,
|
||||
|
||||
if (uvm_va_block_is_hmm(va_block)) {
|
||||
status = NV_ERR_INVALID_ADDRESS;
|
||||
if (service_context->block_context.mm) {
|
||||
if (service_context->block_context->mm) {
|
||||
status = uvm_hmm_find_policy_vma_and_outer(va_block,
|
||||
&service_context->block_context.hmm.vma,
|
||||
&service_context->block_context->hmm.vma,
|
||||
first_page_index,
|
||||
&policy,
|
||||
&outer);
|
||||
@ -1206,7 +1206,7 @@ static NV_STATUS service_phys_single_va_block(uvm_gpu_t *gpu,
|
||||
|
||||
service_context->operation = UVM_SERVICE_OPERATION_ACCESS_COUNTERS;
|
||||
service_context->num_retries = 0;
|
||||
service_context->block_context.mm = mm;
|
||||
service_context->block_context->mm = mm;
|
||||
|
||||
if (uvm_va_block_is_hmm(va_block)) {
|
||||
uvm_hmm_service_context_init(service_context);
|
||||
|
@ -292,6 +292,7 @@ NV_STATUS uvm_gpu_init_isr(uvm_parent_gpu_t *parent_gpu)
|
||||
{
|
||||
NV_STATUS status = NV_OK;
|
||||
char kthread_name[TASK_COMM_LEN + 1];
|
||||
uvm_va_block_context_t *block_context;
|
||||
|
||||
if (parent_gpu->replayable_faults_supported) {
|
||||
status = uvm_gpu_fault_buffer_init(parent_gpu);
|
||||
@ -311,6 +312,12 @@ NV_STATUS uvm_gpu_init_isr(uvm_parent_gpu_t *parent_gpu)
|
||||
if (!parent_gpu->isr.replayable_faults.stats.cpu_exec_count)
|
||||
return NV_ERR_NO_MEMORY;
|
||||
|
||||
block_context = uvm_va_block_context_alloc(NULL);
|
||||
if (!block_context)
|
||||
return NV_ERR_NO_MEMORY;
|
||||
|
||||
parent_gpu->fault_buffer_info.replayable.block_service_context.block_context = block_context;
|
||||
|
||||
parent_gpu->isr.replayable_faults.handling = true;
|
||||
|
||||
snprintf(kthread_name, sizeof(kthread_name), "UVM GPU%u BH", uvm_id_value(parent_gpu->id));
|
||||
@ -333,6 +340,12 @@ NV_STATUS uvm_gpu_init_isr(uvm_parent_gpu_t *parent_gpu)
|
||||
if (!parent_gpu->isr.non_replayable_faults.stats.cpu_exec_count)
|
||||
return NV_ERR_NO_MEMORY;
|
||||
|
||||
block_context = uvm_va_block_context_alloc(NULL);
|
||||
if (!block_context)
|
||||
return NV_ERR_NO_MEMORY;
|
||||
|
||||
parent_gpu->fault_buffer_info.non_replayable.block_service_context.block_context = block_context;
|
||||
|
||||
parent_gpu->isr.non_replayable_faults.handling = true;
|
||||
|
||||
snprintf(kthread_name, sizeof(kthread_name), "UVM GPU%u KC", uvm_id_value(parent_gpu->id));
|
||||
@ -356,6 +369,13 @@ NV_STATUS uvm_gpu_init_isr(uvm_parent_gpu_t *parent_gpu)
|
||||
return status;
|
||||
}
|
||||
|
||||
block_context = uvm_va_block_context_alloc(NULL);
|
||||
if (!block_context)
|
||||
return NV_ERR_NO_MEMORY;
|
||||
|
||||
parent_gpu->access_counter_buffer_info.batch_service_context.block_service_context.block_context =
|
||||
block_context;
|
||||
|
||||
nv_kthread_q_item_init(&parent_gpu->isr.access_counters.bottom_half_q_item,
|
||||
access_counters_isr_bottom_half_entry,
|
||||
parent_gpu);
|
||||
@ -410,6 +430,8 @@ void uvm_gpu_disable_isr(uvm_parent_gpu_t *parent_gpu)
|
||||
|
||||
void uvm_gpu_deinit_isr(uvm_parent_gpu_t *parent_gpu)
|
||||
{
|
||||
uvm_va_block_context_t *block_context;
|
||||
|
||||
// Return ownership to RM:
|
||||
if (parent_gpu->isr.replayable_faults.was_handling) {
|
||||
// No user threads could have anything left on
|
||||
@ -439,8 +461,18 @@ void uvm_gpu_deinit_isr(uvm_parent_gpu_t *parent_gpu)
|
||||
// It is safe to deinitialize access counters even if they have not been
|
||||
// successfully initialized.
|
||||
uvm_gpu_deinit_access_counters(parent_gpu);
|
||||
block_context =
|
||||
parent_gpu->access_counter_buffer_info.batch_service_context.block_service_context.block_context;
|
||||
uvm_va_block_context_free(block_context);
|
||||
}
|
||||
|
||||
if (parent_gpu->non_replayable_faults_supported) {
|
||||
block_context = parent_gpu->fault_buffer_info.non_replayable.block_service_context.block_context;
|
||||
uvm_va_block_context_free(block_context);
|
||||
}
|
||||
|
||||
block_context = parent_gpu->fault_buffer_info.replayable.block_service_context.block_context;
|
||||
uvm_va_block_context_free(block_context);
|
||||
uvm_kvfree(parent_gpu->isr.replayable_faults.stats.cpu_exec_count);
|
||||
uvm_kvfree(parent_gpu->isr.non_replayable_faults.stats.cpu_exec_count);
|
||||
uvm_kvfree(parent_gpu->isr.access_counters.stats.cpu_exec_count);
|
||||
|
@ -370,7 +370,7 @@ static NV_STATUS service_managed_fault_in_block_locked(uvm_gpu_t *gpu,
|
||||
|
||||
// Check logical permissions
|
||||
status = uvm_va_block_check_logical_permissions(va_block,
|
||||
&service_context->block_context,
|
||||
service_context->block_context,
|
||||
gpu->id,
|
||||
uvm_va_block_cpu_page_index(va_block,
|
||||
fault_entry->fault_address),
|
||||
@ -393,7 +393,7 @@ static NV_STATUS service_managed_fault_in_block_locked(uvm_gpu_t *gpu,
|
||||
|
||||
// Compute new residency and update the masks
|
||||
new_residency = uvm_va_block_select_residency(va_block,
|
||||
&service_context->block_context,
|
||||
service_context->block_context,
|
||||
page_index,
|
||||
gpu->id,
|
||||
fault_entry->access_type_mask,
|
||||
@ -629,7 +629,7 @@ static NV_STATUS service_fault(uvm_gpu_t *gpu, uvm_fault_buffer_entry_t *fault_e
|
||||
uvm_gpu_va_space_t *gpu_va_space;
|
||||
uvm_non_replayable_fault_buffer_info_t *non_replayable_faults = &gpu->parent->fault_buffer_info.non_replayable;
|
||||
uvm_va_block_context_t *va_block_context =
|
||||
&gpu->parent->fault_buffer_info.non_replayable.block_service_context.block_context;
|
||||
gpu->parent->fault_buffer_info.non_replayable.block_service_context.block_context;
|
||||
|
||||
status = uvm_gpu_fault_entry_to_va_space(gpu, fault_entry, &va_space);
|
||||
if (status != NV_OK) {
|
||||
@ -655,7 +655,7 @@ static NV_STATUS service_fault(uvm_gpu_t *gpu, uvm_fault_buffer_entry_t *fault_e
|
||||
// to remain valid until we release. If no mm is registered, we
|
||||
// can only service managed faults, not ATS/HMM faults.
|
||||
mm = uvm_va_space_mm_retain_lock(va_space);
|
||||
va_block_context->mm = mm;
|
||||
uvm_va_block_context_init(va_block_context, mm);
|
||||
|
||||
uvm_va_space_down_read(va_space);
|
||||
|
||||
|
@ -1180,7 +1180,11 @@ static void mark_fault_fatal(uvm_fault_service_batch_context_t *batch_context,
|
||||
fault_entry->replayable.cancel_va_mode = cancel_va_mode;
|
||||
|
||||
utlb->has_fatal_faults = true;
|
||||
batch_context->has_fatal_faults = true;
|
||||
|
||||
if (!batch_context->fatal_va_space) {
|
||||
UVM_ASSERT(fault_entry->va_space);
|
||||
batch_context->fatal_va_space = fault_entry->va_space;
|
||||
}
|
||||
}
|
||||
|
||||
static void fault_entry_duplicate_flags(uvm_fault_service_batch_context_t *batch_context,
|
||||
@ -1230,7 +1234,7 @@ static uvm_fault_access_type_t check_fault_access_permissions(uvm_gpu_t *gpu,
|
||||
UvmEventFatalReason fatal_reason;
|
||||
uvm_fault_cancel_va_mode_t cancel_va_mode;
|
||||
uvm_fault_access_type_t ret = UVM_FAULT_ACCESS_TYPE_COUNT;
|
||||
uvm_va_block_context_t *va_block_context = &service_block_context->block_context;
|
||||
uvm_va_block_context_t *va_block_context = service_block_context->block_context;
|
||||
|
||||
perm_status = uvm_va_block_check_logical_permissions(va_block,
|
||||
va_block_context,
|
||||
@ -1345,7 +1349,7 @@ static NV_STATUS service_fault_batch_block_locked(uvm_gpu_t *gpu,
|
||||
|
||||
if (uvm_va_block_is_hmm(va_block)) {
|
||||
policy = uvm_hmm_find_policy_end(va_block,
|
||||
block_context->block_context.hmm.vma,
|
||||
block_context->block_context->hmm.vma,
|
||||
ordered_fault_cache[first_fault_index]->fault_address,
|
||||
&end);
|
||||
}
|
||||
@ -1469,7 +1473,7 @@ static NV_STATUS service_fault_batch_block_locked(uvm_gpu_t *gpu,
|
||||
|
||||
// Compute new residency and update the masks
|
||||
new_residency = uvm_va_block_select_residency(va_block,
|
||||
&block_context->block_context,
|
||||
block_context->block_context,
|
||||
page_index,
|
||||
gpu->id,
|
||||
service_access_type_mask,
|
||||
@ -1511,8 +1515,8 @@ static NV_STATUS service_fault_batch_block_locked(uvm_gpu_t *gpu,
|
||||
|
||||
++block_context->num_retries;
|
||||
|
||||
if (status == NV_OK && batch_context->has_fatal_faults)
|
||||
status = uvm_va_block_set_cancel(va_block, &block_context->block_context, gpu);
|
||||
if (status == NV_OK && batch_context->fatal_va_space)
|
||||
status = uvm_va_block_set_cancel(va_block, block_context->block_context, gpu);
|
||||
|
||||
return status;
|
||||
}
|
||||
@ -1860,7 +1864,7 @@ static NV_STATUS service_fault_batch_dispatch(uvm_va_space_t *va_space,
|
||||
uvm_va_block_t *va_block;
|
||||
uvm_gpu_t *gpu = gpu_va_space->gpu;
|
||||
uvm_va_block_context_t *va_block_context =
|
||||
&gpu->parent->fault_buffer_info.replayable.block_service_context.block_context;
|
||||
gpu->parent->fault_buffer_info.replayable.block_service_context.block_context;
|
||||
uvm_fault_buffer_entry_t *current_entry = batch_context->ordered_fault_cache[fault_index];
|
||||
struct mm_struct *mm = va_block_context->mm;
|
||||
NvU64 fault_address = current_entry->fault_address;
|
||||
@ -1937,14 +1941,198 @@ static NV_STATUS service_fault_batch_dispatch(uvm_va_space_t *va_space,
|
||||
return status;
|
||||
}
|
||||
|
||||
// Called when a fault in the batch has been marked fatal. Flush the buffer
|
||||
// under the VA and mmap locks to remove any potential stale fatal faults, then
|
||||
// service all new faults for just that VA space and cancel those which are
|
||||
// fatal. Faults in other VA spaces are replayed when done and will be processed
|
||||
// when normal fault servicing resumes.
|
||||
static NV_STATUS service_fault_batch_for_cancel(uvm_gpu_t *gpu, uvm_fault_service_batch_context_t *batch_context)
|
||||
{
|
||||
NV_STATUS status = NV_OK;
|
||||
NvU32 i;
|
||||
uvm_va_space_t *va_space = batch_context->fatal_va_space;
|
||||
uvm_gpu_va_space_t *gpu_va_space = NULL;
|
||||
struct mm_struct *mm;
|
||||
uvm_replayable_fault_buffer_info_t *replayable_faults = &gpu->parent->fault_buffer_info.replayable;
|
||||
uvm_service_block_context_t *service_context = &gpu->parent->fault_buffer_info.replayable.block_service_context;
|
||||
uvm_va_block_context_t *va_block_context = service_context->block_context;
|
||||
|
||||
UVM_ASSERT(gpu->parent->replayable_faults_supported);
|
||||
UVM_ASSERT(va_space);
|
||||
|
||||
// Perform the flush and re-fetch while holding the mmap_lock and the
|
||||
// VA space lock. This avoids stale faults because it prevents any vma
|
||||
// modifications (mmap, munmap, mprotect) from happening between the time HW
|
||||
// takes the fault and we cancel it.
|
||||
mm = uvm_va_space_mm_retain_lock(va_space);
|
||||
uvm_va_block_context_init(va_block_context, mm);
|
||||
uvm_va_space_down_read(va_space);
|
||||
|
||||
// We saw fatal faults in this VA space before. Flush while holding
|
||||
// mmap_lock to make sure those faults come back (aren't stale).
|
||||
//
|
||||
// We need to wait until all old fault messages have arrived before
|
||||
// flushing, hence UVM_GPU_BUFFER_FLUSH_MODE_WAIT_UPDATE_PUT.
|
||||
status = fault_buffer_flush_locked(gpu,
|
||||
UVM_GPU_BUFFER_FLUSH_MODE_WAIT_UPDATE_PUT,
|
||||
UVM_FAULT_REPLAY_TYPE_START,
|
||||
batch_context);
|
||||
if (status != NV_OK)
|
||||
goto done;
|
||||
|
||||
// Wait for the flush's replay to finish to give the legitimate faults a
|
||||
// chance to show up in the buffer again.
|
||||
status = uvm_tracker_wait(&replayable_faults->replay_tracker);
|
||||
if (status != NV_OK)
|
||||
goto done;
|
||||
|
||||
// We expect all replayed faults to have arrived in the buffer so we can re-
|
||||
// service them. The replay-and-wait sequence above will ensure they're all
|
||||
// in the HW buffer. When GSP owns the HW buffer, we also have to wait for
|
||||
// GSP to copy all available faults from the HW buffer into the shadow
|
||||
// buffer.
|
||||
//
|
||||
// TODO: Bug 2533557: This flush does not actually guarantee that GSP will
|
||||
// copy over all faults.
|
||||
status = hw_fault_buffer_flush_locked(gpu->parent);
|
||||
if (status != NV_OK)
|
||||
goto done;
|
||||
|
||||
// If there is no GPU VA space for the GPU, ignore all faults in the VA
|
||||
// space. This can happen if the GPU VA space has been destroyed since we
|
||||
// unlocked the VA space in service_fault_batch. That means the fatal faults
|
||||
// are stale, because unregistering the GPU VA space requires preempting the
|
||||
// context and detaching all channels in that VA space. Restart fault
|
||||
// servicing from the top.
|
||||
gpu_va_space = uvm_gpu_va_space_get_by_parent_gpu(va_space, gpu->parent);
|
||||
if (!gpu_va_space)
|
||||
goto done;
|
||||
|
||||
// Re-parse the new faults
|
||||
batch_context->num_invalid_prefetch_faults = 0;
|
||||
batch_context->num_duplicate_faults = 0;
|
||||
batch_context->num_replays = 0;
|
||||
batch_context->fatal_va_space = NULL;
|
||||
batch_context->has_throttled_faults = false;
|
||||
|
||||
status = fetch_fault_buffer_entries(gpu, batch_context, FAULT_FETCH_MODE_ALL);
|
||||
if (status != NV_OK)
|
||||
goto done;
|
||||
|
||||
// No more faults left. Either the previously-seen fatal entry was stale, or
|
||||
// RM killed the context underneath us.
|
||||
if (batch_context->num_cached_faults == 0)
|
||||
goto done;
|
||||
|
||||
++batch_context->batch_id;
|
||||
|
||||
status = preprocess_fault_batch(gpu, batch_context);
|
||||
if (status != NV_OK) {
|
||||
if (status == NV_WARN_MORE_PROCESSING_REQUIRED) {
|
||||
// Another flush happened due to stale faults or a context-fatal
|
||||
// error. The previously-seen fatal fault might not exist anymore,
|
||||
// so restart fault servicing from the top.
|
||||
status = NV_OK;
|
||||
}
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Search for the target VA space
|
||||
for (i = 0; i < batch_context->num_coalesced_faults; i++) {
|
||||
uvm_fault_buffer_entry_t *current_entry = batch_context->ordered_fault_cache[i];
|
||||
UVM_ASSERT(current_entry->va_space);
|
||||
if (current_entry->va_space == va_space)
|
||||
break;
|
||||
}
|
||||
|
||||
while (i < batch_context->num_coalesced_faults) {
|
||||
uvm_fault_buffer_entry_t *current_entry = batch_context->ordered_fault_cache[i];
|
||||
|
||||
if (current_entry->va_space != va_space)
|
||||
break;
|
||||
|
||||
// service_fault_batch_dispatch() doesn't expect unserviceable faults.
|
||||
// Just cancel them directly.
|
||||
if (current_entry->is_fatal) {
|
||||
status = cancel_fault_precise_va(gpu, current_entry, UVM_FAULT_CANCEL_VA_MODE_ALL);
|
||||
if (status != NV_OK)
|
||||
break;
|
||||
|
||||
++i;
|
||||
}
|
||||
else {
|
||||
uvm_ats_fault_invalidate_t *ats_invalidate = &gpu->parent->fault_buffer_info.replayable.ats_invalidate;
|
||||
NvU32 block_faults;
|
||||
|
||||
ats_invalidate->write_faults_in_batch = false;
|
||||
uvm_hmm_service_context_init(service_context);
|
||||
|
||||
// Service all the faults that we can. We only really need to search
|
||||
// for fatal faults, but attempting to service all is the easiest
|
||||
// way to do that.
|
||||
status = service_fault_batch_dispatch(va_space, gpu_va_space, batch_context, i, &block_faults, false);
|
||||
if (status != NV_OK) {
|
||||
// TODO: Bug 3900733: clean up locking in service_fault_batch().
|
||||
// We need to drop lock and retry. That means flushing and
|
||||
// starting over.
|
||||
if (status == NV_WARN_MORE_PROCESSING_REQUIRED)
|
||||
status = NV_OK;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Invalidate TLBs before cancel to ensure that fatal faults don't
|
||||
// get stuck in HW behind non-fatal faults to the same line.
|
||||
status = uvm_ats_invalidate_tlbs(gpu_va_space, ats_invalidate, &batch_context->tracker);
|
||||
if (status != NV_OK)
|
||||
break;
|
||||
|
||||
while (block_faults-- > 0) {
|
||||
current_entry = batch_context->ordered_fault_cache[i];
|
||||
if (current_entry->is_fatal) {
|
||||
status = cancel_fault_precise_va(gpu, current_entry, current_entry->replayable.cancel_va_mode);
|
||||
if (status != NV_OK)
|
||||
break;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
uvm_va_space_up_read(va_space);
|
||||
uvm_va_space_mm_release_unlock(va_space, mm);
|
||||
|
||||
if (status == NV_OK) {
|
||||
// There are two reasons to flush the fault buffer here.
|
||||
//
|
||||
// 1) Functional. We need to replay both the serviced non-fatal faults
|
||||
// and the skipped faults in other VA spaces. The former need to be
|
||||
// restarted and the latter need to be replayed so the normal fault
|
||||
// service mechanism can fetch and process them.
|
||||
//
|
||||
// 2) Performance. After cancelling the fatal faults, a flush removes
|
||||
// any potential duplicated fault that may have been added while
|
||||
// processing the faults in this batch. This flush also avoids doing
|
||||
// unnecessary processing after the fatal faults have been cancelled,
|
||||
// so all the rest are unlikely to remain after a replay because the
|
||||
// context is probably in the process of dying.
|
||||
status = fault_buffer_flush_locked(gpu,
|
||||
UVM_GPU_BUFFER_FLUSH_MODE_UPDATE_PUT,
|
||||
UVM_FAULT_REPLAY_TYPE_START,
|
||||
batch_context);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
// Scan the ordered view of faults and group them by different va_blocks
|
||||
// (managed faults) and service faults for each va_block, in batch.
|
||||
// Service non-managed faults one at a time as they are encountered during the
|
||||
// scan.
|
||||
//
|
||||
// This function returns NV_WARN_MORE_PROCESSING_REQUIRED if the fault buffer
|
||||
// was flushed because the needs_fault_buffer_flush flag was set on some GPU VA
|
||||
// space
|
||||
// Fatal faults are marked for later processing by the caller.
|
||||
static NV_STATUS service_fault_batch(uvm_gpu_t *gpu,
|
||||
fault_service_mode_t service_mode,
|
||||
uvm_fault_service_batch_context_t *batch_context)
|
||||
@ -1959,7 +2147,7 @@ static NV_STATUS service_fault_batch(uvm_gpu_t *gpu,
|
||||
gpu->parent->fault_buffer_info.replayable.replay_policy == UVM_PERF_FAULT_REPLAY_POLICY_BLOCK;
|
||||
uvm_service_block_context_t *service_context =
|
||||
&gpu->parent->fault_buffer_info.replayable.block_service_context;
|
||||
uvm_va_block_context_t *va_block_context = &service_context->block_context;
|
||||
uvm_va_block_context_t *va_block_context = service_context->block_context;
|
||||
|
||||
UVM_ASSERT(gpu->parent->replayable_faults_supported);
|
||||
|
||||
@ -1995,41 +2183,28 @@ static NV_STATUS service_fault_batch(uvm_gpu_t *gpu,
|
||||
// to remain valid until we release. If no mm is registered, we
|
||||
// can only service managed faults, not ATS/HMM faults.
|
||||
mm = uvm_va_space_mm_retain_lock(va_space);
|
||||
va_block_context->mm = mm;
|
||||
uvm_va_block_context_init(va_block_context, mm);
|
||||
|
||||
uvm_va_space_down_read(va_space);
|
||||
|
||||
gpu_va_space = uvm_gpu_va_space_get_by_parent_gpu(va_space, gpu->parent);
|
||||
if (uvm_processor_mask_test_and_clear_atomic(&va_space->needs_fault_buffer_flush, gpu->id)) {
|
||||
status = fault_buffer_flush_locked(gpu,
|
||||
UVM_GPU_BUFFER_FLUSH_MODE_WAIT_UPDATE_PUT,
|
||||
UVM_FAULT_REPLAY_TYPE_START,
|
||||
batch_context);
|
||||
if (status == NV_OK)
|
||||
status = NV_WARN_MORE_PROCESSING_REQUIRED;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// The case where there is no valid GPU VA space for the GPU in this
|
||||
// VA space is handled next
|
||||
}
|
||||
|
||||
// Some faults could be already fatal if they cannot be handled by
|
||||
// the UVM driver
|
||||
if (current_entry->is_fatal) {
|
||||
++i;
|
||||
batch_context->has_fatal_faults = true;
|
||||
if (!batch_context->fatal_va_space)
|
||||
batch_context->fatal_va_space = va_space;
|
||||
|
||||
utlb->has_fatal_faults = true;
|
||||
UVM_ASSERT(utlb->num_pending_faults > 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!uvm_processor_mask_test(&va_space->registered_gpu_va_spaces, gpu->parent->id)) {
|
||||
if (!gpu_va_space) {
|
||||
// If there is no GPU VA space for the GPU, ignore the fault. This
|
||||
// can happen if a GPU VA space is destroyed without explicitly
|
||||
// freeing all memory ranges (destroying the VA range triggers a
|
||||
// flush of the fault buffer) and there are stale entries in the
|
||||
// freeing all memory ranges and there are stale entries in the
|
||||
// buffer that got fixed by the servicing in a previous batch.
|
||||
++i;
|
||||
continue;
|
||||
@ -2057,7 +2232,7 @@ static NV_STATUS service_fault_batch(uvm_gpu_t *gpu,
|
||||
i += block_faults;
|
||||
|
||||
// Don't issue replays in cancel mode
|
||||
if (replay_per_va_block && !batch_context->has_fatal_faults) {
|
||||
if (replay_per_va_block && !batch_context->fatal_va_space) {
|
||||
status = push_replay_on_gpu(gpu, UVM_FAULT_REPLAY_TYPE_START, batch_context);
|
||||
if (status != NV_OK)
|
||||
goto fail;
|
||||
@ -2069,8 +2244,6 @@ static NV_STATUS service_fault_batch(uvm_gpu_t *gpu,
|
||||
}
|
||||
}
|
||||
|
||||
// Only clobber status if invalidate_status != NV_OK, since status may also
|
||||
// contain NV_WARN_MORE_PROCESSING_REQUIRED.
|
||||
if (va_space != NULL) {
|
||||
NV_STATUS invalidate_status = uvm_ats_invalidate_tlbs(gpu_va_space, ats_invalidate, &batch_context->tracker);
|
||||
if (invalidate_status != NV_OK)
|
||||
@ -2278,64 +2451,6 @@ static NvU32 is_fatal_fault_in_buffer(uvm_fault_service_batch_context_t *batch_c
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cancel just the faults flagged as fatal in the given fault service batch
|
||||
// context.
|
||||
static NV_STATUS cancel_faults_precise_va(uvm_gpu_t *gpu, uvm_fault_service_batch_context_t *batch_context)
|
||||
{
|
||||
NV_STATUS status = NV_OK;
|
||||
NV_STATUS fault_status;
|
||||
uvm_va_space_t *va_space = NULL;
|
||||
NvU32 i;
|
||||
|
||||
UVM_ASSERT(gpu->parent->fault_cancel_va_supported);
|
||||
|
||||
for (i = 0; i < batch_context->num_coalesced_faults; ++i) {
|
||||
uvm_fault_buffer_entry_t *current_entry = batch_context->ordered_fault_cache[i];
|
||||
|
||||
UVM_ASSERT(current_entry->va_space);
|
||||
|
||||
if (current_entry->va_space != va_space) {
|
||||
// Fault on a different va_space, drop the lock of the old one...
|
||||
if (va_space != NULL)
|
||||
uvm_va_space_up_read(va_space);
|
||||
|
||||
va_space = current_entry->va_space;
|
||||
|
||||
// ... and take the lock of the new one
|
||||
uvm_va_space_down_read(va_space);
|
||||
|
||||
// We don't need to check whether a buffer flush is required
|
||||
// (due to VA range destruction). Once a fault is flagged as fatal
|
||||
// we need to cancel it, even if its VA range no longer exists.
|
||||
}
|
||||
|
||||
// See the comment for the same check in cancel_faults_all
|
||||
if (!uvm_processor_mask_test(&va_space->registered_gpu_va_spaces, gpu->parent->id))
|
||||
continue;
|
||||
|
||||
if (current_entry->is_fatal) {
|
||||
status = cancel_fault_precise_va(gpu, current_entry, current_entry->replayable.cancel_va_mode);
|
||||
if (status != NV_OK)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (va_space != NULL)
|
||||
uvm_va_space_up_read(va_space);
|
||||
|
||||
// See the comment on flushing in cancel_faults_all
|
||||
fault_status = fault_buffer_flush_locked(gpu,
|
||||
UVM_GPU_BUFFER_FLUSH_MODE_UPDATE_PUT,
|
||||
UVM_FAULT_REPLAY_TYPE_START,
|
||||
batch_context);
|
||||
|
||||
// We report the first encountered error.
|
||||
if (status == NV_OK)
|
||||
status = fault_status;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
// Cancel all faults in the given fault service batch context, even those not
|
||||
// marked as fatal.
|
||||
static NV_STATUS cancel_faults_all(uvm_gpu_t *gpu,
|
||||
@ -2344,56 +2459,51 @@ static NV_STATUS cancel_faults_all(uvm_gpu_t *gpu,
|
||||
{
|
||||
NV_STATUS status = NV_OK;
|
||||
NV_STATUS fault_status;
|
||||
uvm_va_space_t *va_space = NULL;
|
||||
NvU32 i;
|
||||
NvU32 i = 0;
|
||||
|
||||
UVM_ASSERT(gpu->parent->fault_cancel_va_supported);
|
||||
UVM_ASSERT(reason != UvmEventFatalReasonInvalid);
|
||||
|
||||
for (i = 0; i < batch_context->num_coalesced_faults; ++i) {
|
||||
while (i < batch_context->num_coalesced_faults && status == NV_OK) {
|
||||
uvm_fault_buffer_entry_t *current_entry = batch_context->ordered_fault_cache[i];
|
||||
uvm_fault_cancel_va_mode_t cancel_va_mode;
|
||||
uvm_va_space_t *va_space = current_entry->va_space;
|
||||
bool skip_va_space;
|
||||
|
||||
UVM_ASSERT(current_entry->va_space);
|
||||
UVM_ASSERT(va_space);
|
||||
|
||||
if (current_entry->va_space != va_space) {
|
||||
// Fault on a different va_space, drop the lock of the old one...
|
||||
if (va_space != NULL)
|
||||
uvm_va_space_up_read(va_space);
|
||||
uvm_va_space_down_read(va_space);
|
||||
|
||||
va_space = current_entry->va_space;
|
||||
// If there is no GPU VA space for the GPU, ignore all faults in
|
||||
// that VA space. This can happen if the GPU VA space has been
|
||||
// destroyed since we unlocked the VA space in service_fault_batch.
|
||||
// Ignoring the fault avoids targetting a PDB that might have been
|
||||
// reused by another process.
|
||||
skip_va_space = !uvm_gpu_va_space_get_by_parent_gpu(va_space, gpu->parent);
|
||||
|
||||
// ... and take the lock of the new one
|
||||
uvm_va_space_down_read(va_space);
|
||||
for (;
|
||||
i < batch_context->num_coalesced_faults && current_entry->va_space == va_space;
|
||||
current_entry = batch_context->ordered_fault_cache[++i]) {
|
||||
uvm_fault_cancel_va_mode_t cancel_va_mode;
|
||||
|
||||
if (skip_va_space)
|
||||
continue;
|
||||
|
||||
if (current_entry->is_fatal) {
|
||||
UVM_ASSERT(current_entry->fatal_reason != UvmEventFatalReasonInvalid);
|
||||
cancel_va_mode = current_entry->replayable.cancel_va_mode;
|
||||
}
|
||||
else {
|
||||
current_entry->fatal_reason = reason;
|
||||
cancel_va_mode = UVM_FAULT_CANCEL_VA_MODE_ALL;
|
||||
}
|
||||
|
||||
status = cancel_fault_precise_va(gpu, current_entry, cancel_va_mode);
|
||||
if (status != NV_OK)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!uvm_processor_mask_test(&va_space->registered_gpu_va_spaces, gpu->parent->id)) {
|
||||
// If there is no GPU VA space for the GPU, ignore the fault.
|
||||
// This can happen if the GPU VA did not exist in
|
||||
// service_fault_batch(), or it was destroyed since then.
|
||||
// This is to avoid targetting a PDB that might have been reused
|
||||
// by another process.
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the fault was already marked fatal, use its reason and cancel
|
||||
// mode. Otherwise use the provided reason.
|
||||
if (current_entry->is_fatal) {
|
||||
UVM_ASSERT(current_entry->fatal_reason != UvmEventFatalReasonInvalid);
|
||||
cancel_va_mode = current_entry->replayable.cancel_va_mode;
|
||||
}
|
||||
else {
|
||||
current_entry->fatal_reason = reason;
|
||||
cancel_va_mode = UVM_FAULT_CANCEL_VA_MODE_ALL;
|
||||
}
|
||||
|
||||
status = cancel_fault_precise_va(gpu, current_entry, cancel_va_mode);
|
||||
if (status != NV_OK)
|
||||
break;
|
||||
}
|
||||
|
||||
if (va_space != NULL)
|
||||
uvm_va_space_up_read(va_space);
|
||||
}
|
||||
|
||||
// Because each cancel itself triggers a replay, there may be a large number
|
||||
// of new duplicated faults in the buffer after cancelling all the known
|
||||
@ -2537,7 +2647,7 @@ static NV_STATUS cancel_faults_precise_tlb(uvm_gpu_t *gpu, uvm_fault_service_bat
|
||||
|
||||
batch_context->num_invalid_prefetch_faults = 0;
|
||||
batch_context->num_replays = 0;
|
||||
batch_context->has_fatal_faults = false;
|
||||
batch_context->fatal_va_space = NULL;
|
||||
batch_context->has_throttled_faults = false;
|
||||
|
||||
// 5) Fetch all faults from buffer
|
||||
@ -2584,9 +2694,6 @@ static NV_STATUS cancel_faults_precise_tlb(uvm_gpu_t *gpu, uvm_fault_service_bat
|
||||
// 8) Service all non-fatal faults and mark all non-serviceable faults
|
||||
// as fatal
|
||||
status = service_fault_batch(gpu, FAULT_SERVICE_MODE_CANCEL, batch_context);
|
||||
if (status == NV_WARN_MORE_PROCESSING_REQUIRED)
|
||||
continue;
|
||||
|
||||
UVM_ASSERT(batch_context->num_replays == 0);
|
||||
if (status == NV_ERR_NO_MEMORY)
|
||||
continue;
|
||||
@ -2594,7 +2701,7 @@ static NV_STATUS cancel_faults_precise_tlb(uvm_gpu_t *gpu, uvm_fault_service_bat
|
||||
break;
|
||||
|
||||
// No more fatal faults left, we are done
|
||||
if (!batch_context->has_fatal_faults)
|
||||
if (!batch_context->fatal_va_space)
|
||||
break;
|
||||
|
||||
// 9) Search for uTLBs that contain fatal faults and meet the
|
||||
@ -2616,9 +2723,9 @@ static NV_STATUS cancel_faults_precise_tlb(uvm_gpu_t *gpu, uvm_fault_service_bat
|
||||
|
||||
static NV_STATUS cancel_faults_precise(uvm_gpu_t *gpu, uvm_fault_service_batch_context_t *batch_context)
|
||||
{
|
||||
UVM_ASSERT(batch_context->has_fatal_faults);
|
||||
UVM_ASSERT(batch_context->fatal_va_space);
|
||||
if (gpu->parent->fault_cancel_va_supported)
|
||||
return cancel_faults_precise_va(gpu, batch_context);
|
||||
return service_fault_batch_for_cancel(gpu, batch_context);
|
||||
|
||||
return cancel_faults_precise_tlb(gpu, batch_context);
|
||||
}
|
||||
@ -2674,7 +2781,7 @@ void uvm_gpu_service_replayable_faults(uvm_gpu_t *gpu)
|
||||
batch_context->num_invalid_prefetch_faults = 0;
|
||||
batch_context->num_duplicate_faults = 0;
|
||||
batch_context->num_replays = 0;
|
||||
batch_context->has_fatal_faults = false;
|
||||
batch_context->fatal_va_space = NULL;
|
||||
batch_context->has_throttled_faults = false;
|
||||
|
||||
status = fetch_fault_buffer_entries(gpu, batch_context, FAULT_FETCH_MODE_BATCH_READY);
|
||||
@ -2702,9 +2809,6 @@ void uvm_gpu_service_replayable_faults(uvm_gpu_t *gpu)
|
||||
// was flushed
|
||||
num_replays += batch_context->num_replays;
|
||||
|
||||
if (status == NV_WARN_MORE_PROCESSING_REQUIRED)
|
||||
continue;
|
||||
|
||||
enable_disable_prefetch_faults(gpu->parent, batch_context);
|
||||
|
||||
if (status != NV_OK) {
|
||||
@ -2718,10 +2822,17 @@ void uvm_gpu_service_replayable_faults(uvm_gpu_t *gpu)
|
||||
break;
|
||||
}
|
||||
|
||||
if (batch_context->has_fatal_faults) {
|
||||
if (batch_context->fatal_va_space) {
|
||||
status = uvm_tracker_wait(&batch_context->tracker);
|
||||
if (status == NV_OK)
|
||||
if (status == NV_OK) {
|
||||
status = cancel_faults_precise(gpu, batch_context);
|
||||
if (status == NV_OK) {
|
||||
// Cancel handling should've issued at least one replay
|
||||
UVM_ASSERT(batch_context->num_replays > 0);
|
||||
++num_batches;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -794,7 +794,7 @@ uvm_membar_t uvm_hal_downgrade_membar_type(uvm_gpu_t *gpu, bool is_local_vidmem)
|
||||
// memory, including those from other processors like the CPU or peer GPUs,
|
||||
// must come through this GPU's L2. In all current architectures, MEMBAR_GPU
|
||||
// is sufficient to resolve ordering at the L2 level.
|
||||
if (is_local_vidmem && !uvm_gpu_is_coherent(gpu->parent) && !uvm_downgrade_force_membar_sys)
|
||||
if (is_local_vidmem && !uvm_parent_gpu_is_coherent(gpu->parent) && !uvm_downgrade_force_membar_sys)
|
||||
return UVM_MEMBAR_GPU;
|
||||
|
||||
// If the mapped memory was remote, or if a coherence protocol can cache
|
||||
|
@ -60,6 +60,8 @@ module_param(uvm_disable_hmm, bool, 0444);
|
||||
#include "uvm_gpu.h"
|
||||
#include "uvm_pmm_gpu.h"
|
||||
#include "uvm_hal_types.h"
|
||||
#include "uvm_push.h"
|
||||
#include "uvm_hal.h"
|
||||
#include "uvm_va_block_types.h"
|
||||
#include "uvm_va_space_mm.h"
|
||||
#include "uvm_va_space.h"
|
||||
@ -110,20 +112,7 @@ typedef struct
|
||||
|
||||
bool uvm_hmm_is_enabled_system_wide(void)
|
||||
{
|
||||
if (uvm_disable_hmm)
|
||||
return false;
|
||||
|
||||
if (g_uvm_global.ats.enabled)
|
||||
return false;
|
||||
|
||||
// Confidential Computing and HMM impose mutually exclusive constraints. In
|
||||
// Confidential Computing the GPU can only access pages resident in vidmem,
|
||||
// but in HMM pages may be required to be resident in sysmem: file backed
|
||||
// VMAs, huge pages, etc.
|
||||
if (g_uvm_global.conf_computing_enabled)
|
||||
return false;
|
||||
|
||||
return uvm_va_space_mm_enabled_system();
|
||||
return !uvm_disable_hmm && !g_uvm_global.ats.enabled && uvm_va_space_mm_enabled_system();
|
||||
}
|
||||
|
||||
bool uvm_hmm_is_enabled(uvm_va_space_t *va_space)
|
||||
@ -140,6 +129,100 @@ static uvm_va_block_t *hmm_va_block_from_node(uvm_range_tree_node_t *node)
|
||||
return container_of(node, uvm_va_block_t, hmm.node);
|
||||
}
|
||||
|
||||
// Copies the contents of the source device-private page to the
|
||||
// destination CPU page. This will invalidate mappings, so cannot be
|
||||
// called while holding any va_block locks.
|
||||
static NV_STATUS uvm_hmm_copy_devmem_page(struct page *dst_page, struct page *src_page, uvm_tracker_t *tracker)
|
||||
{
|
||||
uvm_gpu_phys_address_t src_addr;
|
||||
uvm_gpu_phys_address_t dst_addr;
|
||||
uvm_gpu_chunk_t *gpu_chunk;
|
||||
NvU64 dma_addr;
|
||||
uvm_push_t push;
|
||||
NV_STATUS status = NV_OK;
|
||||
uvm_gpu_t *gpu;
|
||||
|
||||
// Holding a reference on the device-private page ensures the gpu
|
||||
// is already retained. This is because when a GPU is unregistered
|
||||
// all device-private pages are migrated back to the CPU and freed
|
||||
// before releasing the GPU. Therefore if we could get a reference
|
||||
// to the page the GPU must be retained.
|
||||
UVM_ASSERT(is_device_private_page(src_page) && page_count(src_page));
|
||||
gpu_chunk = uvm_pmm_devmem_page_to_chunk(src_page);
|
||||
gpu = uvm_gpu_chunk_get_gpu(gpu_chunk);
|
||||
status = uvm_mmu_chunk_map(gpu_chunk);
|
||||
if (status != NV_OK)
|
||||
return status;
|
||||
|
||||
status = uvm_gpu_map_cpu_pages(gpu->parent, dst_page, PAGE_SIZE, &dma_addr);
|
||||
if (status != NV_OK)
|
||||
goto out_unmap_gpu;
|
||||
|
||||
dst_addr = uvm_gpu_phys_address(UVM_APERTURE_SYS, dma_addr);
|
||||
src_addr = uvm_gpu_phys_address(UVM_APERTURE_VID, gpu_chunk->address);
|
||||
status = uvm_push_begin_acquire(gpu->channel_manager,
|
||||
UVM_CHANNEL_TYPE_GPU_TO_CPU,
|
||||
tracker,
|
||||
&push,
|
||||
"Copy for remote process fault");
|
||||
if (status != NV_OK)
|
||||
goto out_unmap_cpu;
|
||||
|
||||
gpu->parent->ce_hal->memcopy(&push,
|
||||
uvm_gpu_address_copy(gpu, dst_addr),
|
||||
uvm_gpu_address_copy(gpu, src_addr),
|
||||
PAGE_SIZE);
|
||||
uvm_push_end(&push);
|
||||
status = uvm_tracker_add_push_safe(tracker, &push);
|
||||
|
||||
out_unmap_cpu:
|
||||
uvm_gpu_unmap_cpu_pages(gpu->parent, dma_addr, PAGE_SIZE);
|
||||
|
||||
out_unmap_gpu:
|
||||
uvm_mmu_chunk_unmap(gpu_chunk, NULL);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static NV_STATUS uvm_hmm_pmm_gpu_evict_pfn(unsigned long pfn)
|
||||
{
|
||||
unsigned long src_pfn = 0;
|
||||
unsigned long dst_pfn = 0;
|
||||
struct page *dst_page;
|
||||
NV_STATUS status = NV_OK;
|
||||
int ret;
|
||||
|
||||
ret = migrate_device_range(&src_pfn, pfn, 1);
|
||||
if (ret)
|
||||
return errno_to_nv_status(ret);
|
||||
|
||||
if (src_pfn & MIGRATE_PFN_MIGRATE) {
|
||||
uvm_tracker_t tracker = UVM_TRACKER_INIT();
|
||||
|
||||
dst_page = alloc_page(GFP_HIGHUSER_MOVABLE);
|
||||
if (!dst_page) {
|
||||
status = NV_ERR_NO_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
lock_page(dst_page);
|
||||
if (WARN_ON(uvm_hmm_copy_devmem_page(dst_page, migrate_pfn_to_page(src_pfn), &tracker) != NV_OK))
|
||||
memzero_page(dst_page, 0, PAGE_SIZE);
|
||||
|
||||
dst_pfn = migrate_pfn(page_to_pfn(dst_page));
|
||||
migrate_device_pages(&src_pfn, &dst_pfn, 1);
|
||||
uvm_tracker_wait_deinit(&tracker);
|
||||
}
|
||||
|
||||
out:
|
||||
migrate_device_finalize(&src_pfn, &dst_pfn, 1);
|
||||
|
||||
if (!(src_pfn & MIGRATE_PFN_MIGRATE))
|
||||
status = NV_ERR_BUSY_RETRY;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void uvm_hmm_va_space_initialize(uvm_va_space_t *va_space)
|
||||
{
|
||||
uvm_hmm_va_space_t *hmm_va_space = &va_space->hmm;
|
||||
@ -199,6 +282,9 @@ void uvm_hmm_unregister_gpu(uvm_va_space_t *va_space, uvm_gpu_t *gpu, struct mm_
|
||||
{
|
||||
uvm_range_tree_node_t *node;
|
||||
uvm_va_block_t *va_block;
|
||||
struct range range = gpu->pmm.devmem.pagemap.range;
|
||||
unsigned long pfn;
|
||||
bool retry;
|
||||
|
||||
if (!uvm_hmm_is_enabled(va_space))
|
||||
return;
|
||||
@ -207,6 +293,29 @@ void uvm_hmm_unregister_gpu(uvm_va_space_t *va_space, uvm_gpu_t *gpu, struct mm_
|
||||
uvm_assert_mmap_lock_locked(mm);
|
||||
uvm_assert_rwsem_locked_write(&va_space->lock);
|
||||
|
||||
// There could be pages with page->zone_device_data pointing to the va_space
|
||||
// which may be about to be freed. Migrate those back to the CPU so we don't
|
||||
// fault on them. Normally infinite retries are bad, but we don't have any
|
||||
// option here. Device-private pages can't be pinned so migration should
|
||||
// eventually succeed. Even if we did eventually bail out of the loop we'd
|
||||
// just stall in memunmap_pages() anyway.
|
||||
do {
|
||||
retry = false;
|
||||
|
||||
for (pfn = __phys_to_pfn(range.start); pfn <= __phys_to_pfn(range.end); pfn++) {
|
||||
struct page *page = pfn_to_page(pfn);
|
||||
|
||||
UVM_ASSERT(is_device_private_page(page));
|
||||
|
||||
// This check is racy because nothing stops the page being freed and
|
||||
// even reused. That doesn't matter though - worst case the
|
||||
// migration fails, we retry and find the va_space doesn't match.
|
||||
if (page->zone_device_data == va_space)
|
||||
if (uvm_hmm_pmm_gpu_evict_pfn(pfn) != NV_OK)
|
||||
retry = true;
|
||||
}
|
||||
} while (retry);
|
||||
|
||||
uvm_range_tree_for_each(node, &va_space->hmm.blocks) {
|
||||
va_block = hmm_va_block_from_node(node);
|
||||
|
||||
@ -568,7 +677,7 @@ bool uvm_hmm_check_context_vma_is_valid(uvm_va_block_t *va_block,
|
||||
void uvm_hmm_service_context_init(uvm_service_block_context_t *service_context)
|
||||
{
|
||||
// TODO: Bug 4050579: Remove this when swap cached pages can be migrated.
|
||||
service_context->block_context.hmm.swap_cached = false;
|
||||
service_context->block_context->hmm.swap_cached = false;
|
||||
}
|
||||
|
||||
NV_STATUS uvm_hmm_migrate_begin(uvm_va_block_t *va_block)
|
||||
@ -631,47 +740,6 @@ static NV_STATUS hmm_migrate_range(uvm_va_block_t *va_block,
|
||||
return status;
|
||||
}
|
||||
|
||||
void uvm_hmm_evict_va_blocks(uvm_va_space_t *va_space)
|
||||
{
|
||||
// We can't use uvm_va_space_mm_retain(), because the va_space_mm
|
||||
// should already be dead by now.
|
||||
struct mm_struct *mm = va_space->va_space_mm.mm;
|
||||
uvm_hmm_va_space_t *hmm_va_space = &va_space->hmm;
|
||||
uvm_range_tree_node_t *node, *next;
|
||||
uvm_va_block_t *va_block;
|
||||
uvm_va_block_context_t *block_context;
|
||||
|
||||
uvm_down_read_mmap_lock(mm);
|
||||
uvm_va_space_down_write(va_space);
|
||||
|
||||
uvm_range_tree_for_each_safe(node, next, &hmm_va_space->blocks) {
|
||||
uvm_va_block_region_t region;
|
||||
struct vm_area_struct *vma;
|
||||
|
||||
va_block = hmm_va_block_from_node(node);
|
||||
block_context = uvm_va_space_block_context(va_space, mm);
|
||||
uvm_hmm_migrate_begin_wait(va_block);
|
||||
uvm_mutex_lock(&va_block->lock);
|
||||
for_each_va_block_vma_region(va_block, mm, vma, ®ion) {
|
||||
if (!uvm_hmm_vma_is_valid(vma, vma->vm_start, false))
|
||||
continue;
|
||||
|
||||
block_context->hmm.vma = vma;
|
||||
uvm_hmm_va_block_migrate_locked(va_block,
|
||||
NULL,
|
||||
block_context,
|
||||
UVM_ID_CPU,
|
||||
region,
|
||||
UVM_MAKE_RESIDENT_CAUSE_API_MIGRATE);
|
||||
}
|
||||
uvm_mutex_unlock(&va_block->lock);
|
||||
uvm_hmm_migrate_finish(va_block);
|
||||
}
|
||||
|
||||
uvm_va_space_up_write(va_space);
|
||||
uvm_up_read_mmap_lock(mm);
|
||||
}
|
||||
|
||||
NV_STATUS uvm_hmm_test_va_block_inject_split_error(uvm_va_space_t *va_space, NvU64 addr)
|
||||
{
|
||||
uvm_va_block_test_t *block_test;
|
||||
@ -1476,40 +1544,59 @@ static NV_STATUS hmm_va_block_cpu_page_populate(uvm_va_block_t *va_block,
|
||||
return status;
|
||||
}
|
||||
|
||||
status = uvm_va_block_map_cpu_chunk_on_gpus(va_block, page_index);
|
||||
status = uvm_va_block_map_cpu_chunk_on_gpus(va_block, chunk, page_index);
|
||||
if (status != NV_OK) {
|
||||
uvm_cpu_chunk_remove_from_block(va_block, page_index);
|
||||
uvm_cpu_chunk_remove_from_block(va_block, page_to_nid(page), page_index);
|
||||
uvm_cpu_chunk_free(chunk);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void hmm_va_block_cpu_page_unpopulate(uvm_va_block_t *va_block,
|
||||
uvm_page_index_t page_index)
|
||||
static void hmm_va_block_cpu_unpopulate_chunk(uvm_va_block_t *va_block,
|
||||
uvm_cpu_chunk_t *chunk,
|
||||
int chunk_nid,
|
||||
uvm_page_index_t page_index)
|
||||
{
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(va_block, page_index);
|
||||
|
||||
UVM_ASSERT(uvm_va_block_is_hmm(va_block));
|
||||
|
||||
if (!chunk)
|
||||
return;
|
||||
|
||||
UVM_ASSERT(!uvm_processor_mask_test(&va_block->resident, UVM_ID_CPU) ||
|
||||
!uvm_page_mask_test(&va_block->cpu.resident, page_index));
|
||||
!uvm_va_block_cpu_is_page_resident_on(va_block, NUMA_NO_NODE, page_index));
|
||||
UVM_ASSERT(uvm_cpu_chunk_get_size(chunk) == PAGE_SIZE);
|
||||
|
||||
uvm_cpu_chunk_remove_from_block(va_block, page_index);
|
||||
uvm_cpu_chunk_remove_from_block(va_block, chunk_nid, page_index);
|
||||
uvm_va_block_unmap_cpu_chunk_on_gpus(va_block, chunk, page_index);
|
||||
uvm_cpu_chunk_free(chunk);
|
||||
}
|
||||
|
||||
static void hmm_va_block_cpu_page_unpopulate(uvm_va_block_t *va_block, uvm_page_index_t page_index, struct page *page)
|
||||
{
|
||||
uvm_cpu_chunk_t *chunk;
|
||||
|
||||
UVM_ASSERT(uvm_va_block_is_hmm(va_block));
|
||||
|
||||
if (page) {
|
||||
chunk = uvm_cpu_chunk_get_chunk_for_page(va_block, page_to_nid(page), page_index);
|
||||
hmm_va_block_cpu_unpopulate_chunk(va_block, chunk, page_to_nid(page), page_index);
|
||||
}
|
||||
else {
|
||||
int nid;
|
||||
|
||||
for_each_possible_uvm_node(nid) {
|
||||
chunk = uvm_cpu_chunk_get_chunk_for_page(va_block, nid, page_index);
|
||||
hmm_va_block_cpu_unpopulate_chunk(va_block, chunk, nid, page_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool hmm_va_block_cpu_page_is_same(uvm_va_block_t *va_block,
|
||||
uvm_page_index_t page_index,
|
||||
struct page *page)
|
||||
{
|
||||
struct page *old_page = uvm_cpu_chunk_get_cpu_page(va_block, page_index);
|
||||
struct page *old_page = uvm_va_block_get_cpu_page(va_block, page_index);
|
||||
|
||||
UVM_ASSERT(uvm_cpu_chunk_is_hmm(uvm_cpu_chunk_get_chunk_for_page(va_block, page_index)));
|
||||
UVM_ASSERT(uvm_cpu_chunk_is_hmm(uvm_cpu_chunk_get_chunk_for_page(va_block, page_to_nid(page), page_index)));
|
||||
return old_page == page;
|
||||
}
|
||||
|
||||
@ -1522,7 +1609,7 @@ static void clear_service_context_masks(uvm_service_block_context_t *service_con
|
||||
uvm_processor_id_t new_residency,
|
||||
uvm_page_index_t page_index)
|
||||
{
|
||||
uvm_page_mask_clear(&service_context->block_context.caller_page_mask, page_index);
|
||||
uvm_page_mask_clear(&service_context->block_context->caller_page_mask, page_index);
|
||||
|
||||
uvm_page_mask_clear(&service_context->per_processor_masks[uvm_id_value(new_residency)].new_residency,
|
||||
page_index);
|
||||
@ -1549,7 +1636,6 @@ static void cpu_mapping_set(uvm_va_block_t *va_block,
|
||||
uvm_page_index_t page_index)
|
||||
{
|
||||
uvm_processor_mask_set(&va_block->mapped, UVM_ID_CPU);
|
||||
uvm_page_mask_set(&va_block->maybe_mapped_pages, page_index);
|
||||
uvm_page_mask_set(&va_block->cpu.pte_bits[UVM_PTE_BITS_CPU_READ], page_index);
|
||||
if (is_write)
|
||||
uvm_page_mask_set(&va_block->cpu.pte_bits[UVM_PTE_BITS_CPU_WRITE], page_index);
|
||||
@ -1699,7 +1785,7 @@ static NV_STATUS sync_page_and_chunk_state(uvm_va_block_t *va_block,
|
||||
// migrate_vma_finalize() will release the reference so we should
|
||||
// clear our pointer to it.
|
||||
// TODO: Bug 3660922: Need to handle read duplication at some point.
|
||||
hmm_va_block_cpu_page_unpopulate(va_block, page_index);
|
||||
hmm_va_block_cpu_page_unpopulate(va_block, page_index, page);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1725,7 +1811,7 @@ static void clean_up_non_migrating_page(uvm_va_block_t *va_block,
|
||||
else {
|
||||
UVM_ASSERT(page_ref_count(dst_page) == 1);
|
||||
|
||||
hmm_va_block_cpu_page_unpopulate(va_block, page_index);
|
||||
hmm_va_block_cpu_page_unpopulate(va_block, page_index, dst_page);
|
||||
}
|
||||
|
||||
unlock_page(dst_page);
|
||||
@ -1760,7 +1846,7 @@ static void lock_block_cpu_page(uvm_va_block_t *va_block,
|
||||
unsigned long *dst_pfns,
|
||||
uvm_page_mask_t *same_devmem_page_mask)
|
||||
{
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(va_block, page_index);
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(va_block, page_to_nid(src_page), page_index);
|
||||
uvm_va_block_region_t chunk_region;
|
||||
struct page *dst_page;
|
||||
|
||||
@ -1786,7 +1872,7 @@ static void lock_block_cpu_page(uvm_va_block_t *va_block,
|
||||
// hmm_va_block_cpu_page_unpopulate() or block_kill(). If the page
|
||||
// does not migrate, it will be freed though.
|
||||
UVM_ASSERT(!uvm_processor_mask_test(&va_block->resident, UVM_ID_CPU) ||
|
||||
!uvm_page_mask_test(&va_block->cpu.resident, page_index));
|
||||
!uvm_va_block_cpu_is_page_resident_on(va_block, NUMA_NO_NODE, page_index));
|
||||
UVM_ASSERT(chunk->type == UVM_CPU_CHUNK_TYPE_PHYSICAL);
|
||||
UVM_ASSERT(page_ref_count(dst_page) == 1);
|
||||
uvm_cpu_chunk_make_hmm(chunk);
|
||||
@ -1934,7 +2020,7 @@ static NV_STATUS alloc_and_copy_to_cpu(uvm_va_block_t *va_block,
|
||||
}
|
||||
|
||||
UVM_ASSERT(!uvm_processor_mask_test(&va_block->resident, UVM_ID_CPU) ||
|
||||
!uvm_page_mask_test(&va_block->cpu.resident, page_index));
|
||||
!uvm_va_block_cpu_is_page_resident_on(va_block, NUMA_NO_NODE, page_index));
|
||||
|
||||
// Allocate a user system memory page for the destination.
|
||||
// This is the typical case since Linux will free the source page when
|
||||
@ -2012,8 +2098,8 @@ static NV_STATUS uvm_hmm_devmem_fault_alloc_and_copy(uvm_hmm_devmem_fault_contex
|
||||
service_context = devmem_fault_context->service_context;
|
||||
va_block_retry = devmem_fault_context->va_block_retry;
|
||||
va_block = devmem_fault_context->va_block;
|
||||
src_pfns = service_context->block_context.hmm.src_pfns;
|
||||
dst_pfns = service_context->block_context.hmm.dst_pfns;
|
||||
src_pfns = service_context->block_context->hmm.src_pfns;
|
||||
dst_pfns = service_context->block_context->hmm.dst_pfns;
|
||||
|
||||
// Build the migration page mask.
|
||||
// Note that thrashing pinned pages and prefetch pages are already
|
||||
@ -2022,7 +2108,7 @@ static NV_STATUS uvm_hmm_devmem_fault_alloc_and_copy(uvm_hmm_devmem_fault_contex
|
||||
uvm_page_mask_copy(page_mask, &service_context->per_processor_masks[UVM_ID_CPU_VALUE].new_residency);
|
||||
|
||||
status = alloc_and_copy_to_cpu(va_block,
|
||||
service_context->block_context.hmm.vma,
|
||||
service_context->block_context->hmm.vma,
|
||||
src_pfns,
|
||||
dst_pfns,
|
||||
service_context->region,
|
||||
@ -2057,8 +2143,8 @@ static NV_STATUS uvm_hmm_devmem_fault_finalize_and_map(uvm_hmm_devmem_fault_cont
|
||||
prefetch_hint = &service_context->prefetch_hint;
|
||||
va_block = devmem_fault_context->va_block;
|
||||
va_block_retry = devmem_fault_context->va_block_retry;
|
||||
src_pfns = service_context->block_context.hmm.src_pfns;
|
||||
dst_pfns = service_context->block_context.hmm.dst_pfns;
|
||||
src_pfns = service_context->block_context->hmm.src_pfns;
|
||||
dst_pfns = service_context->block_context->hmm.dst_pfns;
|
||||
region = service_context->region;
|
||||
|
||||
page_mask = &devmem_fault_context->page_mask;
|
||||
@ -2165,8 +2251,7 @@ static NV_STATUS populate_region(uvm_va_block_t *va_block,
|
||||
|
||||
// Since we have a stable snapshot of the CPU pages, we can
|
||||
// update the residency and protection information.
|
||||
uvm_processor_mask_set(&va_block->resident, UVM_ID_CPU);
|
||||
uvm_page_mask_set(&va_block->cpu.resident, page_index);
|
||||
uvm_va_block_cpu_set_resident_page(va_block, page_to_nid(page), page_index);
|
||||
|
||||
cpu_mapping_set(va_block, pfns[page_index] & HMM_PFN_WRITE, page_index);
|
||||
}
|
||||
@ -2253,7 +2338,7 @@ static void hmm_release_atomic_pages(uvm_va_block_t *va_block,
|
||||
uvm_page_index_t page_index;
|
||||
|
||||
for_each_va_block_page_in_region(page_index, region) {
|
||||
struct page *page = service_context->block_context.hmm.pages[page_index];
|
||||
struct page *page = service_context->block_context->hmm.pages[page_index];
|
||||
|
||||
if (!page)
|
||||
continue;
|
||||
@ -2269,14 +2354,14 @@ static NV_STATUS hmm_block_atomic_fault_locked(uvm_processor_id_t processor_id,
|
||||
uvm_service_block_context_t *service_context)
|
||||
{
|
||||
uvm_va_block_region_t region = service_context->region;
|
||||
struct page **pages = service_context->block_context.hmm.pages;
|
||||
struct page **pages = service_context->block_context->hmm.pages;
|
||||
int npages;
|
||||
uvm_page_index_t page_index;
|
||||
uvm_make_resident_cause_t cause;
|
||||
NV_STATUS status;
|
||||
|
||||
if (!uvm_processor_mask_test(&va_block->resident, UVM_ID_CPU) ||
|
||||
!uvm_page_mask_region_full(&va_block->cpu.resident, region)) {
|
||||
!uvm_va_block_cpu_is_region_resident_on(va_block, NUMA_NO_NODE, region)) {
|
||||
// There is an atomic GPU fault. We need to make sure no pages are
|
||||
// GPU resident so that make_device_exclusive_range() doesn't call
|
||||
// migrate_to_ram() and cause a va_space lock recursion problem.
|
||||
@ -2289,7 +2374,7 @@ static NV_STATUS hmm_block_atomic_fault_locked(uvm_processor_id_t processor_id,
|
||||
|
||||
status = uvm_hmm_va_block_migrate_locked(va_block,
|
||||
va_block_retry,
|
||||
&service_context->block_context,
|
||||
service_context->block_context,
|
||||
UVM_ID_CPU,
|
||||
region,
|
||||
cause);
|
||||
@ -2299,7 +2384,7 @@ static NV_STATUS hmm_block_atomic_fault_locked(uvm_processor_id_t processor_id,
|
||||
// make_device_exclusive_range() will try to call migrate_to_ram()
|
||||
// and deadlock with ourself if the data isn't CPU resident.
|
||||
if (!uvm_processor_mask_test(&va_block->resident, UVM_ID_CPU) ||
|
||||
!uvm_page_mask_region_full(&va_block->cpu.resident, region)) {
|
||||
!uvm_va_block_cpu_is_region_resident_on(va_block, NUMA_NO_NODE, region)) {
|
||||
status = NV_WARN_MORE_PROCESSING_REQUIRED;
|
||||
goto done;
|
||||
}
|
||||
@ -2309,7 +2394,7 @@ static NV_STATUS hmm_block_atomic_fault_locked(uvm_processor_id_t processor_id,
|
||||
// mmap() files so we check for that here and report a fatal fault.
|
||||
// Otherwise with the current Linux 6.1 make_device_exclusive_range(),
|
||||
// it doesn't make the page exclusive and we end up in an endless loop.
|
||||
if (service_context->block_context.hmm.vma->vm_flags & VM_SHARED) {
|
||||
if (service_context->block_context->hmm.vma->vm_flags & (VM_SHARED | VM_HUGETLB)) {
|
||||
status = NV_ERR_NOT_SUPPORTED;
|
||||
goto done;
|
||||
}
|
||||
@ -2318,7 +2403,7 @@ static NV_STATUS hmm_block_atomic_fault_locked(uvm_processor_id_t processor_id,
|
||||
|
||||
uvm_mutex_unlock(&va_block->lock);
|
||||
|
||||
npages = make_device_exclusive_range(service_context->block_context.mm,
|
||||
npages = make_device_exclusive_range(service_context->block_context->mm,
|
||||
uvm_va_block_cpu_page_address(va_block, region.first),
|
||||
uvm_va_block_cpu_page_address(va_block, region.outer - 1) + PAGE_SIZE,
|
||||
pages + region.first,
|
||||
@ -2356,15 +2441,13 @@ static NV_STATUS hmm_block_atomic_fault_locked(uvm_processor_id_t processor_id,
|
||||
if (uvm_page_mask_test(&va_block->cpu.allocated, page_index)) {
|
||||
UVM_ASSERT(hmm_va_block_cpu_page_is_same(va_block, page_index, page));
|
||||
UVM_ASSERT(uvm_processor_mask_test(&va_block->resident, UVM_ID_CPU));
|
||||
UVM_ASSERT(uvm_page_mask_test(&va_block->cpu.resident, page_index));
|
||||
UVM_ASSERT(uvm_va_block_cpu_is_page_resident_on(va_block, NUMA_NO_NODE, page_index));
|
||||
}
|
||||
else {
|
||||
NV_STATUS s = hmm_va_block_cpu_page_populate(va_block, page_index, page);
|
||||
|
||||
if (s == NV_OK) {
|
||||
uvm_processor_mask_set(&va_block->resident, UVM_ID_CPU);
|
||||
uvm_page_mask_set(&va_block->cpu.resident, page_index);
|
||||
}
|
||||
if (s == NV_OK)
|
||||
uvm_va_block_cpu_set_resident_page(va_block, page_to_nid(page), page_index);
|
||||
}
|
||||
|
||||
cpu_mapping_clear(va_block, page_index);
|
||||
@ -2419,7 +2502,7 @@ static NV_STATUS hmm_block_cpu_fault_locked(uvm_processor_id_t processor_id,
|
||||
uvm_service_block_context_t *service_context)
|
||||
{
|
||||
uvm_va_block_region_t region = service_context->region;
|
||||
struct migrate_vma *args = &service_context->block_context.hmm.migrate_vma_args;
|
||||
struct migrate_vma *args = &service_context->block_context->hmm.migrate_vma_args;
|
||||
NV_STATUS status;
|
||||
int ret;
|
||||
uvm_hmm_devmem_fault_context_t fault_context = {
|
||||
@ -2453,8 +2536,8 @@ static NV_STATUS hmm_block_cpu_fault_locked(uvm_processor_id_t processor_id,
|
||||
}
|
||||
|
||||
status = hmm_make_resident_cpu(va_block,
|
||||
service_context->block_context.hmm.vma,
|
||||
service_context->block_context.hmm.src_pfns,
|
||||
service_context->block_context->hmm.vma,
|
||||
service_context->block_context->hmm.src_pfns,
|
||||
region,
|
||||
service_context->access_type,
|
||||
&fault_context.same_devmem_page_mask);
|
||||
@ -2476,9 +2559,9 @@ static NV_STATUS hmm_block_cpu_fault_locked(uvm_processor_id_t processor_id,
|
||||
}
|
||||
}
|
||||
|
||||
args->vma = service_context->block_context.hmm.vma;
|
||||
args->src = service_context->block_context.hmm.src_pfns + region.first;
|
||||
args->dst = service_context->block_context.hmm.dst_pfns + region.first;
|
||||
args->vma = service_context->block_context->hmm.vma;
|
||||
args->src = service_context->block_context->hmm.src_pfns + region.first;
|
||||
args->dst = service_context->block_context->hmm.dst_pfns + region.first;
|
||||
args->start = uvm_va_block_region_start(va_block, region);
|
||||
args->end = uvm_va_block_region_end(va_block, region) + 1;
|
||||
args->flags = MIGRATE_VMA_SELECT_DEVICE_PRIVATE;
|
||||
@ -2558,7 +2641,7 @@ static NV_STATUS dmamap_src_sysmem_pages(uvm_va_block_t *va_block,
|
||||
// TODO: Bug 4050579: Remove this when swap cached pages can be
|
||||
// migrated.
|
||||
if (service_context) {
|
||||
service_context->block_context.hmm.swap_cached = true;
|
||||
service_context->block_context->hmm.swap_cached = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2574,7 +2657,7 @@ static NV_STATUS dmamap_src_sysmem_pages(uvm_va_block_t *va_block,
|
||||
if (uvm_page_mask_test(&va_block->cpu.allocated, page_index)) {
|
||||
UVM_ASSERT(hmm_va_block_cpu_page_is_same(va_block, page_index, src_page));
|
||||
UVM_ASSERT(uvm_processor_mask_test(&va_block->resident, UVM_ID_CPU));
|
||||
UVM_ASSERT(uvm_page_mask_test(&va_block->cpu.resident, page_index));
|
||||
UVM_ASSERT(uvm_va_block_cpu_is_page_resident_on(va_block, NUMA_NO_NODE, page_index));
|
||||
}
|
||||
else {
|
||||
status = hmm_va_block_cpu_page_populate(va_block, page_index, src_page);
|
||||
@ -2588,8 +2671,7 @@ static NV_STATUS dmamap_src_sysmem_pages(uvm_va_block_t *va_block,
|
||||
|
||||
// migrate_vma_setup() was able to isolate and lock the page;
|
||||
// therefore, it is CPU resident and not mapped.
|
||||
uvm_processor_mask_set(&va_block->resident, UVM_ID_CPU);
|
||||
uvm_page_mask_set(&va_block->cpu.resident, page_index);
|
||||
uvm_va_block_cpu_set_resident_page(va_block, page_to_nid(src_page), page_index);
|
||||
}
|
||||
|
||||
// The call to migrate_vma_setup() will have inserted a migration
|
||||
@ -2604,7 +2686,7 @@ static NV_STATUS dmamap_src_sysmem_pages(uvm_va_block_t *va_block,
|
||||
if (uvm_page_mask_test(&va_block->cpu.allocated, page_index)) {
|
||||
UVM_ASSERT(!uvm_va_block_page_resident_processors_count(va_block, page_index));
|
||||
|
||||
hmm_va_block_cpu_page_unpopulate(va_block, page_index);
|
||||
hmm_va_block_cpu_page_unpopulate(va_block, page_index, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2618,7 +2700,7 @@ static NV_STATUS dmamap_src_sysmem_pages(uvm_va_block_t *va_block,
|
||||
}
|
||||
|
||||
if (uvm_page_mask_empty(page_mask) ||
|
||||
(service_context && service_context->block_context.hmm.swap_cached))
|
||||
(service_context && service_context->block_context->hmm.swap_cached))
|
||||
status = NV_WARN_MORE_PROCESSING_REQUIRED;
|
||||
|
||||
if (status != NV_OK)
|
||||
@ -2649,8 +2731,8 @@ static NV_STATUS uvm_hmm_gpu_fault_alloc_and_copy(struct vm_area_struct *vma,
|
||||
service_context = uvm_hmm_gpu_fault_event->service_context;
|
||||
region = service_context->region;
|
||||
prefetch_hint = &service_context->prefetch_hint;
|
||||
src_pfns = service_context->block_context.hmm.src_pfns;
|
||||
dst_pfns = service_context->block_context.hmm.dst_pfns;
|
||||
src_pfns = service_context->block_context->hmm.src_pfns;
|
||||
dst_pfns = service_context->block_context->hmm.dst_pfns;
|
||||
|
||||
// Build the migration mask.
|
||||
// Note that thrashing pinned pages are already accounted for in
|
||||
@ -2708,8 +2790,8 @@ static NV_STATUS uvm_hmm_gpu_fault_finalize_and_map(uvm_hmm_gpu_fault_event_t *u
|
||||
va_block = uvm_hmm_gpu_fault_event->va_block;
|
||||
va_block_retry = uvm_hmm_gpu_fault_event->va_block_retry;
|
||||
service_context = uvm_hmm_gpu_fault_event->service_context;
|
||||
src_pfns = service_context->block_context.hmm.src_pfns;
|
||||
dst_pfns = service_context->block_context.hmm.dst_pfns;
|
||||
src_pfns = service_context->block_context->hmm.src_pfns;
|
||||
dst_pfns = service_context->block_context->hmm.dst_pfns;
|
||||
region = service_context->region;
|
||||
page_mask = &uvm_hmm_gpu_fault_event->page_mask;
|
||||
|
||||
@ -2752,11 +2834,11 @@ NV_STATUS uvm_hmm_va_block_service_locked(uvm_processor_id_t processor_id,
|
||||
uvm_va_block_retry_t *va_block_retry,
|
||||
uvm_service_block_context_t *service_context)
|
||||
{
|
||||
struct mm_struct *mm = service_context->block_context.mm;
|
||||
struct vm_area_struct *vma = service_context->block_context.hmm.vma;
|
||||
struct mm_struct *mm = service_context->block_context->mm;
|
||||
struct vm_area_struct *vma = service_context->block_context->hmm.vma;
|
||||
uvm_va_block_region_t region = service_context->region;
|
||||
uvm_hmm_gpu_fault_event_t uvm_hmm_gpu_fault_event;
|
||||
struct migrate_vma *args = &service_context->block_context.hmm.migrate_vma_args;
|
||||
struct migrate_vma *args = &service_context->block_context->hmm.migrate_vma_args;
|
||||
int ret;
|
||||
NV_STATUS status = NV_ERR_INVALID_ADDRESS;
|
||||
|
||||
@ -2780,8 +2862,8 @@ NV_STATUS uvm_hmm_va_block_service_locked(uvm_processor_id_t processor_id,
|
||||
uvm_hmm_gpu_fault_event.service_context = service_context;
|
||||
|
||||
args->vma = vma;
|
||||
args->src = service_context->block_context.hmm.src_pfns + region.first;
|
||||
args->dst = service_context->block_context.hmm.dst_pfns + region.first;
|
||||
args->src = service_context->block_context->hmm.src_pfns + region.first;
|
||||
args->dst = service_context->block_context->hmm.dst_pfns + region.first;
|
||||
args->start = uvm_va_block_region_start(va_block, region);
|
||||
args->end = uvm_va_block_region_end(va_block, region) + 1;
|
||||
args->flags = MIGRATE_VMA_SELECT_DEVICE_PRIVATE | MIGRATE_VMA_SELECT_SYSTEM;
|
||||
@ -2815,8 +2897,8 @@ NV_STATUS uvm_hmm_va_block_service_locked(uvm_processor_id_t processor_id,
|
||||
// since migrate_vma_setup() would have reported that information.
|
||||
// Try to make it resident in system memory and retry the migration.
|
||||
status = hmm_make_resident_cpu(va_block,
|
||||
service_context->block_context.hmm.vma,
|
||||
service_context->block_context.hmm.src_pfns,
|
||||
service_context->block_context->hmm.vma,
|
||||
service_context->block_context->hmm.src_pfns,
|
||||
region,
|
||||
service_context->access_type,
|
||||
NULL);
|
||||
@ -2962,16 +3044,6 @@ static NV_STATUS uvm_hmm_migrate_finalize(uvm_hmm_migrate_event_t *uvm_hmm_migra
|
||||
&uvm_hmm_migrate_event->same_devmem_page_mask);
|
||||
}
|
||||
|
||||
static bool is_resident(uvm_va_block_t *va_block,
|
||||
uvm_processor_id_t dest_id,
|
||||
uvm_va_block_region_t region)
|
||||
{
|
||||
if (!uvm_processor_mask_test(&va_block->resident, dest_id))
|
||||
return false;
|
||||
|
||||
return uvm_page_mask_region_full(uvm_va_block_resident_mask_get(va_block, dest_id), region);
|
||||
}
|
||||
|
||||
// Note that migrate_vma_*() doesn't handle asynchronous migrations so the
|
||||
// migration flag UVM_MIGRATE_FLAG_SKIP_CPU_MAP doesn't have an effect.
|
||||
// TODO: Bug 3900785: investigate ways to implement async migration.
|
||||
@ -3063,9 +3135,7 @@ NV_STATUS uvm_hmm_va_block_migrate_locked(uvm_va_block_t *va_block,
|
||||
uvm_page_mask_init_from_region(page_mask, region, NULL);
|
||||
|
||||
for_each_id_in_mask(id, &va_block->resident) {
|
||||
if (!uvm_page_mask_andnot(page_mask,
|
||||
page_mask,
|
||||
uvm_va_block_resident_mask_get(va_block, id)))
|
||||
if (!uvm_page_mask_andnot(page_mask, page_mask, uvm_va_block_resident_mask_get(va_block, id, NUMA_NO_NODE)))
|
||||
return NV_OK;
|
||||
}
|
||||
|
||||
@ -3193,6 +3263,7 @@ static NV_STATUS hmm_va_block_evict_chunks(uvm_va_block_t *va_block,
|
||||
uvm_page_mask_t *page_mask = &uvm_hmm_migrate_event.page_mask;
|
||||
const uvm_va_policy_t *policy;
|
||||
uvm_va_policy_node_t *node;
|
||||
uvm_page_mask_t *cpu_resident_mask = uvm_va_block_resident_mask_get(va_block, UVM_ID_CPU, NUMA_NO_NODE);
|
||||
unsigned long npages;
|
||||
NV_STATUS status;
|
||||
|
||||
@ -3215,7 +3286,7 @@ static NV_STATUS hmm_va_block_evict_chunks(uvm_va_block_t *va_block,
|
||||
// Pages resident on the GPU should not have a resident page in system
|
||||
// memory.
|
||||
// TODO: Bug 3660922: Need to handle read duplication at some point.
|
||||
UVM_ASSERT(uvm_page_mask_region_empty(&va_block->cpu.resident, region));
|
||||
UVM_ASSERT(uvm_page_mask_region_empty(cpu_resident_mask, region));
|
||||
|
||||
status = alloc_and_copy_to_cpu(va_block,
|
||||
NULL,
|
||||
@ -3314,35 +3385,34 @@ NV_STATUS uvm_hmm_va_block_evict_pages_from_gpu(uvm_va_block_t *va_block,
|
||||
NULL);
|
||||
}
|
||||
|
||||
NV_STATUS uvm_hmm_pmm_gpu_evict_pfn(unsigned long pfn)
|
||||
NV_STATUS uvm_hmm_remote_cpu_fault(struct vm_fault *vmf)
|
||||
{
|
||||
unsigned long src_pfn = 0;
|
||||
unsigned long dst_pfn = 0;
|
||||
struct page *dst_page;
|
||||
NV_STATUS status = NV_OK;
|
||||
unsigned long src_pfn;
|
||||
unsigned long dst_pfn;
|
||||
struct migrate_vma args;
|
||||
struct page *src_page = vmf->page;
|
||||
uvm_tracker_t tracker = UVM_TRACKER_INIT();
|
||||
int ret;
|
||||
|
||||
ret = migrate_device_range(&src_pfn, pfn, 1);
|
||||
if (ret)
|
||||
return errno_to_nv_status(ret);
|
||||
args.vma = vmf->vma;
|
||||
args.src = &src_pfn;
|
||||
args.dst = &dst_pfn;
|
||||
args.start = nv_page_fault_va(vmf);
|
||||
args.end = args.start + PAGE_SIZE;
|
||||
args.pgmap_owner = &g_uvm_global;
|
||||
args.flags = MIGRATE_VMA_SELECT_DEVICE_PRIVATE;
|
||||
args.fault_page = src_page;
|
||||
|
||||
// We don't call migrate_vma_setup_locked() here because we don't
|
||||
// have a va_block and don't want to ignore invalidations.
|
||||
ret = migrate_vma_setup(&args);
|
||||
UVM_ASSERT(!ret);
|
||||
|
||||
if (src_pfn & MIGRATE_PFN_MIGRATE) {
|
||||
// All the code for copying a vidmem page to sysmem relies on
|
||||
// having a va_block. However certain combinations of mremap()
|
||||
// and fork() can result in device-private pages being mapped
|
||||
// in a child process without a va_block.
|
||||
//
|
||||
// We don't expect the above to be a common occurance so for
|
||||
// now we allocate a fresh zero page when evicting without a
|
||||
// va_block. However this results in child processes losing
|
||||
// data so make sure we warn about it. Ideally we would just
|
||||
// not migrate and SIGBUS the child if it tries to access the
|
||||
// page. However that would prevent unloading of the driver so
|
||||
// we're stuck with this until we fix the problem.
|
||||
// TODO: Bug 3902536: add code to migrate GPU memory without having a
|
||||
// va_block.
|
||||
WARN_ON(1);
|
||||
dst_page = alloc_page(GFP_HIGHUSER_MOVABLE | __GFP_ZERO);
|
||||
struct page *dst_page;
|
||||
|
||||
dst_page = alloc_page(GFP_HIGHUSER_MOVABLE);
|
||||
if (!dst_page) {
|
||||
status = NV_ERR_NO_MEMORY;
|
||||
goto out;
|
||||
@ -3351,11 +3421,15 @@ NV_STATUS uvm_hmm_pmm_gpu_evict_pfn(unsigned long pfn)
|
||||
lock_page(dst_page);
|
||||
dst_pfn = migrate_pfn(page_to_pfn(dst_page));
|
||||
|
||||
migrate_device_pages(&src_pfn, &dst_pfn, 1);
|
||||
status = uvm_hmm_copy_devmem_page(dst_page, src_page, &tracker);
|
||||
if (status == NV_OK)
|
||||
status = uvm_tracker_wait_deinit(&tracker);
|
||||
}
|
||||
|
||||
migrate_vma_pages(&args);
|
||||
|
||||
out:
|
||||
migrate_device_finalize(&src_pfn, &dst_pfn, 1);
|
||||
migrate_vma_finalize(&args);
|
||||
|
||||
return status;
|
||||
}
|
||||
@ -3606,4 +3680,3 @@ bool uvm_hmm_must_use_sysmem(uvm_va_block_t *va_block,
|
||||
}
|
||||
|
||||
#endif // UVM_IS_CONFIG_HMM()
|
||||
|
||||
|
@ -307,10 +307,10 @@ typedef struct
|
||||
uvm_migrate_mode_t mode,
|
||||
uvm_tracker_t *out_tracker);
|
||||
|
||||
// Evicts all va_blocks in the va_space to the CPU. Unlike the
|
||||
// other va_block eviction functions this is based on virtual
|
||||
// address and therefore takes mmap_lock for read.
|
||||
void uvm_hmm_evict_va_blocks(uvm_va_space_t *va_space);
|
||||
// Handle a fault to a device-private page from a process other than the
|
||||
// process which created the va_space that originally allocated the
|
||||
// device-private page.
|
||||
NV_STATUS uvm_hmm_remote_cpu_fault(struct vm_fault *vmf);
|
||||
|
||||
// This sets the va_block_context->hmm.src_pfns[] to the ZONE_DEVICE private
|
||||
// PFN for the GPU chunk memory.
|
||||
@ -343,14 +343,6 @@ typedef struct
|
||||
const uvm_page_mask_t *pages_to_evict,
|
||||
uvm_va_block_region_t region);
|
||||
|
||||
// Migrate a GPU device-private page to system memory. This is
|
||||
// called to remove CPU page table references to device private
|
||||
// struct pages for the given GPU after all other references in
|
||||
// va_blocks have been released and the GPU is in the process of
|
||||
// being removed/torn down. Note that there is no mm, VMA,
|
||||
// va_block or any user channel activity on this GPU.
|
||||
NV_STATUS uvm_hmm_pmm_gpu_evict_pfn(unsigned long pfn);
|
||||
|
||||
// This returns what would be the intersection of va_block start/end and
|
||||
// VMA start/end-1 for the given 'lookup_address' if
|
||||
// uvm_hmm_va_block_find_create() was called.
|
||||
@ -592,8 +584,10 @@ typedef struct
|
||||
return NV_ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
static void uvm_hmm_evict_va_blocks(uvm_va_space_t *va_space)
|
||||
static NV_STATUS uvm_hmm_remote_cpu_fault(struct vm_fault *vmf)
|
||||
{
|
||||
UVM_ASSERT(0);
|
||||
return NV_ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
static NV_STATUS uvm_hmm_va_block_evict_chunk_prep(uvm_va_block_t *va_block,
|
||||
@ -622,11 +616,6 @@ typedef struct
|
||||
return NV_OK;
|
||||
}
|
||||
|
||||
static NV_STATUS uvm_hmm_pmm_gpu_evict_pfn(unsigned long pfn)
|
||||
{
|
||||
return NV_OK;
|
||||
}
|
||||
|
||||
static NV_STATUS uvm_hmm_va_block_range_bounds(uvm_va_space_t *va_space,
|
||||
struct mm_struct *mm,
|
||||
NvU64 lookup_address,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2020-2022 NVIDIA Corporation
|
||||
Copyright (c) 2020-2023 NVIDIA Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
@ -59,12 +59,12 @@ void uvm_hal_hopper_arch_init_properties(uvm_parent_gpu_t *parent_gpu)
|
||||
|
||||
// Physical CE writes to vidmem are non-coherent with respect to the CPU on
|
||||
// GH180.
|
||||
parent_gpu->ce_phys_vidmem_write_supported = !uvm_gpu_is_coherent(parent_gpu);
|
||||
parent_gpu->ce_phys_vidmem_write_supported = !uvm_parent_gpu_is_coherent(parent_gpu);
|
||||
|
||||
// TODO: Bug 4174553: [HGX-SkinnyJoe][GH180] channel errors discussion/debug
|
||||
// portion for the uvm tests became nonresponsive after
|
||||
// some time and then failed even after reboot
|
||||
parent_gpu->peer_copy_mode = uvm_gpu_is_coherent(parent_gpu) ?
|
||||
parent_gpu->peer_copy_mode = uvm_parent_gpu_is_coherent(parent_gpu) ?
|
||||
UVM_GPU_PEER_COPY_MODE_VIRTUAL : g_uvm_global.peer_copy_mode;
|
||||
|
||||
// All GR context buffers may be mapped to 57b wide VAs. All "compute" units
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2020-2023 NVIDIA Corporation
|
||||
Copyright (c) 2020-2022 NVIDIA Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
@ -368,10 +368,7 @@ static NvU64 small_half_pde_hopper(uvm_mmu_page_table_alloc_t *phys_alloc)
|
||||
return pde_bits;
|
||||
}
|
||||
|
||||
static void make_pde_hopper(void *entry,
|
||||
uvm_mmu_page_table_alloc_t **phys_allocs,
|
||||
NvU32 depth,
|
||||
uvm_page_directory_t *child_dir)
|
||||
static void make_pde_hopper(void *entry, uvm_mmu_page_table_alloc_t **phys_allocs, NvU32 depth)
|
||||
{
|
||||
NvU32 entry_count = entries_per_index_hopper(depth);
|
||||
NvU64 *entry_bits = (NvU64 *)entry;
|
||||
|
@ -128,8 +128,9 @@ static inline const struct cpumask *uvm_cpumask_of_node(int node)
|
||||
// present if we see the callback.
|
||||
//
|
||||
// The callback was added in commit 0f0a327fa12cd55de5e7f8c05a70ac3d047f405e,
|
||||
// v3.19 (2014-11-13).
|
||||
#if defined(NV_MMU_NOTIFIER_OPS_HAS_INVALIDATE_RANGE)
|
||||
// v3.19 (2014-11-13) and renamed in commit 1af5a8109904.
|
||||
#if defined(NV_MMU_NOTIFIER_OPS_HAS_INVALIDATE_RANGE) || \
|
||||
defined(NV_MMU_NOTIFIER_OPS_HAS_ARCH_INVALIDATE_SECONDARY_TLBS)
|
||||
#define UVM_CAN_USE_MMU_NOTIFIERS() 1
|
||||
#else
|
||||
#define UVM_CAN_USE_MMU_NOTIFIERS() 0
|
||||
@ -348,6 +349,47 @@ static inline NvU64 NV_GETTIME(void)
|
||||
(bit) = find_next_zero_bit((addr), (size), (bit) + 1))
|
||||
#endif
|
||||
|
||||
#if !defined(NV_FIND_NEXT_BIT_WRAP_PRESENT)
|
||||
static inline unsigned long find_next_bit_wrap(const unsigned long *addr, unsigned long size, unsigned long offset)
|
||||
{
|
||||
unsigned long bit = find_next_bit(addr, size, offset);
|
||||
|
||||
if (bit < size)
|
||||
return bit;
|
||||
|
||||
bit = find_first_bit(addr, offset);
|
||||
return bit < offset ? bit : size;
|
||||
}
|
||||
#endif
|
||||
|
||||
// for_each_set_bit_wrap and __for_each_wrap were introduced in v6.1-rc1
|
||||
// by commit 4fe49b3b97c2640147c46519c2a6fdb06df34f5f
|
||||
#if !defined(for_each_set_bit_wrap)
|
||||
static inline unsigned long __for_each_wrap(const unsigned long *bitmap,
|
||||
unsigned long size,
|
||||
unsigned long start,
|
||||
unsigned long n)
|
||||
{
|
||||
unsigned long bit;
|
||||
|
||||
if (n > start) {
|
||||
bit = find_next_bit(bitmap, size, n);
|
||||
if (bit < size)
|
||||
return bit;
|
||||
|
||||
n = 0;
|
||||
}
|
||||
|
||||
bit = find_next_bit(bitmap, start, n);
|
||||
return bit < start ? bit : size;
|
||||
}
|
||||
|
||||
#define for_each_set_bit_wrap(bit, addr, size, start) \
|
||||
for ((bit) = find_next_bit_wrap((addr), (size), (start)); \
|
||||
(bit) < (size); \
|
||||
(bit) = __for_each_wrap((addr), (size), (start), (bit) + 1))
|
||||
#endif
|
||||
|
||||
// Added in 2.6.24
|
||||
#ifndef ACCESS_ONCE
|
||||
#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))
|
||||
@ -579,4 +621,5 @@ static inline pgprot_t uvm_pgprot_decrypted(pgprot_t prot)
|
||||
#include <asm/page.h>
|
||||
#define page_to_virt(x) __va(PFN_PHYS(page_to_pfn(x)))
|
||||
#endif
|
||||
|
||||
#endif // _UVM_LINUX_H
|
||||
|
@ -355,6 +355,7 @@ static uvm_membar_t va_range_downgrade_membar(uvm_va_range_t *va_range, uvm_ext_
|
||||
if (!ext_gpu_map->mem_handle)
|
||||
return UVM_MEMBAR_GPU;
|
||||
|
||||
// EGM uses the same barriers as sysmem.
|
||||
return uvm_hal_downgrade_membar_type(ext_gpu_map->gpu,
|
||||
!ext_gpu_map->is_sysmem && ext_gpu_map->gpu == ext_gpu_map->owning_gpu);
|
||||
}
|
||||
@ -633,6 +634,8 @@ static NV_STATUS set_ext_gpu_map_location(uvm_ext_gpu_map_t *ext_gpu_map,
|
||||
const UvmGpuMemoryInfo *mem_info)
|
||||
{
|
||||
uvm_gpu_t *owning_gpu;
|
||||
if (mem_info->egm)
|
||||
UVM_ASSERT(mem_info->sysmem);
|
||||
|
||||
if (!mem_info->deviceDescendant && !mem_info->sysmem) {
|
||||
ext_gpu_map->owning_gpu = NULL;
|
||||
@ -641,6 +644,7 @@ static NV_STATUS set_ext_gpu_map_location(uvm_ext_gpu_map_t *ext_gpu_map,
|
||||
}
|
||||
// This is a local or peer allocation, so the owning GPU must have been
|
||||
// registered.
|
||||
// This also checks for if EGM owning GPU is registered.
|
||||
owning_gpu = uvm_va_space_get_gpu_by_uuid(va_space, &mem_info->uuid);
|
||||
if (!owning_gpu)
|
||||
return NV_ERR_INVALID_DEVICE;
|
||||
@ -651,13 +655,10 @@ static NV_STATUS set_ext_gpu_map_location(uvm_ext_gpu_map_t *ext_gpu_map,
|
||||
// crashes when it's eventually freed.
|
||||
// TODO: Bug 1811006: Bug tracking the RM issue, its fix might change the
|
||||
// semantics of sysmem allocations.
|
||||
if (mem_info->sysmem) {
|
||||
ext_gpu_map->owning_gpu = owning_gpu;
|
||||
ext_gpu_map->is_sysmem = true;
|
||||
return NV_OK;
|
||||
}
|
||||
|
||||
if (owning_gpu != mapping_gpu) {
|
||||
// Check if peer access for peer memory is enabled.
|
||||
// This path also handles EGM allocations.
|
||||
if (owning_gpu != mapping_gpu && (!mem_info->sysmem || mem_info->egm)) {
|
||||
// TODO: Bug 1757136: In SLI, the returned UUID may be different but a
|
||||
// local mapping must be used. We need to query SLI groups to know
|
||||
// that.
|
||||
@ -666,7 +667,9 @@ static NV_STATUS set_ext_gpu_map_location(uvm_ext_gpu_map_t *ext_gpu_map,
|
||||
}
|
||||
|
||||
ext_gpu_map->owning_gpu = owning_gpu;
|
||||
ext_gpu_map->is_sysmem = false;
|
||||
ext_gpu_map->is_sysmem = mem_info->sysmem;
|
||||
ext_gpu_map->is_egm = mem_info->egm;
|
||||
|
||||
return NV_OK;
|
||||
}
|
||||
|
||||
@ -719,6 +722,7 @@ static NV_STATUS uvm_ext_gpu_map_split(uvm_range_tree_t *tree,
|
||||
new->gpu = existing_map->gpu;
|
||||
new->owning_gpu = existing_map->owning_gpu;
|
||||
new->is_sysmem = existing_map->is_sysmem;
|
||||
new->is_egm = existing_map->is_egm;
|
||||
|
||||
// Initialize the new ext_gpu_map tracker as a copy of the existing_map tracker.
|
||||
// This way, any operations on any of the two ext_gpu_maps will be able to
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2016-2023 NVIDIA Corporation
|
||||
Copyright (c) 2016-2021 NVIDIA Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
@ -106,10 +106,7 @@ static NvU64 small_half_pde_maxwell(uvm_mmu_page_table_alloc_t *phys_alloc)
|
||||
return pde_bits;
|
||||
}
|
||||
|
||||
static void make_pde_maxwell(void *entry,
|
||||
uvm_mmu_page_table_alloc_t **phys_allocs,
|
||||
NvU32 depth,
|
||||
uvm_page_directory_t *child_dir)
|
||||
static void make_pde_maxwell(void *entry, uvm_mmu_page_table_alloc_t **phys_allocs, NvU32 depth)
|
||||
{
|
||||
NvU64 pde_bits = 0;
|
||||
UVM_ASSERT(depth == 0);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2016-2023 NVIDIA Corporation
|
||||
Copyright (c) 2016-2022 NVIDIA Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
@ -93,9 +93,8 @@ static bool sysmem_can_be_mapped_on_gpu(uvm_mem_t *sysmem)
|
||||
{
|
||||
UVM_ASSERT(uvm_mem_is_sysmem(sysmem));
|
||||
|
||||
// In Confidential Computing, only unprotected memory can be mapped on the
|
||||
// GPU
|
||||
if (g_uvm_global.conf_computing_enabled)
|
||||
// If SEV is enabled, only unprotected memory can be mapped
|
||||
if (g_uvm_global.sev_enabled)
|
||||
return uvm_mem_is_sysmem_dma(sysmem);
|
||||
|
||||
return true;
|
||||
@ -738,7 +737,7 @@ static NV_STATUS mem_map_cpu_to_sysmem_kernel(uvm_mem_t *mem)
|
||||
pages[page_index] = mem_cpu_page(mem, page_index * PAGE_SIZE);
|
||||
}
|
||||
|
||||
if (g_uvm_global.conf_computing_enabled && uvm_mem_is_sysmem_dma(mem))
|
||||
if (g_uvm_global.sev_enabled && uvm_mem_is_sysmem_dma(mem))
|
||||
prot = uvm_pgprot_decrypted(PAGE_KERNEL_NOENC);
|
||||
|
||||
mem->kernel.cpu_addr = vmap(pages, num_pages, VM_MAP, prot);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2016-2023 NVIDIA Corporation
|
||||
Copyright (c) 2016-2021 NVIDIA Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
@ -44,10 +44,10 @@ static NvU32 first_page_size(NvU32 page_sizes)
|
||||
|
||||
static inline NV_STATUS __alloc_map_sysmem(NvU64 size, uvm_gpu_t *gpu, uvm_mem_t **sys_mem)
|
||||
{
|
||||
if (g_uvm_global.conf_computing_enabled)
|
||||
if (g_uvm_global.sev_enabled)
|
||||
return uvm_mem_alloc_sysmem_dma_and_map_cpu_kernel(size, gpu, current->mm, sys_mem);
|
||||
|
||||
return uvm_mem_alloc_sysmem_and_map_cpu_kernel(size, current->mm, sys_mem);
|
||||
else
|
||||
return uvm_mem_alloc_sysmem_and_map_cpu_kernel(size, current->mm, sys_mem);
|
||||
}
|
||||
|
||||
static NV_STATUS check_accessible_from_gpu(uvm_gpu_t *gpu, uvm_mem_t *mem)
|
||||
@ -335,6 +335,9 @@ error:
|
||||
|
||||
static bool should_test_page_size(size_t alloc_size, NvU32 page_size)
|
||||
{
|
||||
if (g_uvm_global.sev_enabled)
|
||||
return false;
|
||||
|
||||
if (g_uvm_global.num_simulated_devices == 0)
|
||||
return true;
|
||||
|
||||
|
@ -130,9 +130,9 @@ static NV_STATUS block_migrate_map_unmapped_pages(uvm_va_block_t *va_block,
|
||||
NV_STATUS status = NV_OK;
|
||||
NV_STATUS tracker_status;
|
||||
|
||||
// Save the mask of unmapped pages because it will change after the
|
||||
// Get the mask of unmapped pages because it will change after the
|
||||
// first map operation
|
||||
uvm_page_mask_complement(&va_block_context->caller_page_mask, &va_block->maybe_mapped_pages);
|
||||
uvm_va_block_unmapped_pages_get(va_block, region, &va_block_context->caller_page_mask);
|
||||
|
||||
if (uvm_va_block_is_hmm(va_block) && !UVM_ID_IS_CPU(dest_id)) {
|
||||
// Do not map pages that are already resident on the CPU. This is in
|
||||
@ -147,7 +147,7 @@ static NV_STATUS block_migrate_map_unmapped_pages(uvm_va_block_t *va_block,
|
||||
// such pages at all, when migrating.
|
||||
uvm_page_mask_andnot(&va_block_context->caller_page_mask,
|
||||
&va_block_context->caller_page_mask,
|
||||
uvm_va_block_resident_mask_get(va_block, UVM_ID_CPU));
|
||||
uvm_va_block_resident_mask_get(va_block, UVM_ID_CPU, NUMA_NO_NODE));
|
||||
}
|
||||
|
||||
// Only map those pages that are not mapped anywhere else (likely due
|
||||
@ -377,7 +377,7 @@ static bool va_block_should_do_cpu_preunmap(uvm_va_block_t *va_block,
|
||||
|
||||
mapped_pages_cpu = uvm_va_block_map_mask_get(va_block, UVM_ID_CPU);
|
||||
if (uvm_processor_mask_test(&va_block->resident, dest_id)) {
|
||||
const uvm_page_mask_t *resident_pages_dest = uvm_va_block_resident_mask_get(va_block, dest_id);
|
||||
const uvm_page_mask_t *resident_pages_dest = uvm_va_block_resident_mask_get(va_block, dest_id, NUMA_NO_NODE);
|
||||
uvm_page_mask_t *do_not_unmap_pages = &va_block_context->scratch_page_mask;
|
||||
|
||||
// TODO: Bug 1877578
|
||||
|
@ -672,14 +672,6 @@ static NV_STATUS nv_migrate_vma(struct migrate_vma *args, migrate_vma_state_t *s
|
||||
.finalize_and_map = uvm_migrate_vma_finalize_and_map_helper,
|
||||
};
|
||||
|
||||
// WAR for Bug 4130089: [GH180][r535] WAR for kernel not issuing SMMU TLB
|
||||
// invalidates on read-only to read-write upgrades
|
||||
//
|
||||
// This code path isn't used on GH180 but we need to maintain consistent
|
||||
// behaviour on systems that do.
|
||||
if (!vma_is_anonymous(args->vma))
|
||||
return NV_WARN_NOTHING_TO_DO;
|
||||
|
||||
ret = migrate_vma(&uvm_migrate_vma_ops, args->vma, args->start, args->end, args->src, args->dst, state);
|
||||
if (ret < 0)
|
||||
return errno_to_nv_status(ret);
|
||||
@ -693,24 +685,6 @@ static NV_STATUS nv_migrate_vma(struct migrate_vma *args, migrate_vma_state_t *s
|
||||
if (ret < 0)
|
||||
return errno_to_nv_status(ret);
|
||||
|
||||
// TODO: Bug 2419180: support file-backed pages in migrate_vma, when
|
||||
// support for it is added to the Linux kernel
|
||||
//
|
||||
// A side-effect of migrate_vma_setup() is it calls mmu notifiers even if a
|
||||
// page can't be migrated (eg. because it's a non-anonymous mapping). We
|
||||
// need this side-effect for SMMU on GH180 to ensure any cached read-only
|
||||
// entries are flushed from SMMU on permission upgrade.
|
||||
//
|
||||
// TODO: Bug 4130089: [GH180][r535] WAR for kernel not issuing SMMU TLB
|
||||
// invalidates on read-only to read-write upgrades
|
||||
//
|
||||
// The above WAR doesn't work for HugeTLBfs mappings because
|
||||
// migrate_vma_setup() will fail in that case.
|
||||
if (!vma_is_anonymous(args->vma)) {
|
||||
migrate_vma_finalize(args);
|
||||
return NV_WARN_NOTHING_TO_DO;
|
||||
}
|
||||
|
||||
uvm_migrate_vma_alloc_and_copy(args, state);
|
||||
if (state->status == NV_OK) {
|
||||
migrate_vma_pages(args);
|
||||
@ -884,13 +858,9 @@ static NV_STATUS migrate_pageable_vma(struct vm_area_struct *vma,
|
||||
start = max(start, vma->vm_start);
|
||||
outer = min(outer, vma->vm_end);
|
||||
|
||||
// migrate_vma only supports anonymous VMAs. We check for those after
|
||||
// calling migrate_vma_setup() to workaround Bug 4130089. We need to check
|
||||
// for HugeTLB VMAs here because migrate_vma_setup() will return a fatal
|
||||
// error for those.
|
||||
// TODO: Bug 4130089: [GH180][r535] WAR for kernel not issuing SMMU TLB
|
||||
// invalidates on read-only to read-write upgrades
|
||||
if (is_vm_hugetlb_page(vma))
|
||||
// TODO: Bug 2419180: support file-backed pages in migrate_vma, when
|
||||
// support for it is added to the Linux kernel
|
||||
if (!vma_is_anonymous(vma))
|
||||
return NV_WARN_NOTHING_TO_DO;
|
||||
|
||||
if (uvm_processor_mask_empty(&va_space->registered_gpus))
|
||||
|
@ -51,7 +51,7 @@ typedef struct
|
||||
#if defined(CONFIG_MIGRATE_VMA_HELPER)
|
||||
#define UVM_MIGRATE_VMA_SUPPORTED 1
|
||||
#else
|
||||
#if NV_IS_EXPORT_SYMBOL_PRESENT_migrate_vma_setup
|
||||
#if defined(CONFIG_DEVICE_PRIVATE) && defined(NV_MIGRATE_VMA_SETUP_PRESENT)
|
||||
#define UVM_MIGRATE_VMA_SUPPORTED 1
|
||||
#endif
|
||||
#endif
|
||||
|
@ -323,153 +323,37 @@ static void uvm_mmu_page_table_cpu_memset_16(uvm_gpu_t *gpu,
|
||||
uvm_mmu_page_table_cpu_unmap(gpu, phys_alloc);
|
||||
}
|
||||
|
||||
static void pde_fill_cpu(uvm_page_tree_t *tree,
|
||||
uvm_page_directory_t *directory,
|
||||
NvU32 start_index,
|
||||
NvU32 pde_count,
|
||||
uvm_mmu_page_table_alloc_t **phys_addr)
|
||||
{
|
||||
NvU64 pde_data[2], entry_size;
|
||||
NvU32 i;
|
||||
|
||||
UVM_ASSERT(uvm_mmu_use_cpu(tree));
|
||||
|
||||
entry_size = tree->hal->entry_size(directory->depth);
|
||||
UVM_ASSERT(sizeof(pde_data) >= entry_size);
|
||||
|
||||
for (i = 0; i < pde_count; i++) {
|
||||
tree->hal->make_pde(pde_data, phys_addr, directory->depth, directory->entries[start_index + i]);
|
||||
|
||||
if (entry_size == sizeof(pde_data[0]))
|
||||
uvm_mmu_page_table_cpu_memset_8(tree->gpu, &directory->phys_alloc, start_index + i, pde_data[0], 1);
|
||||
else
|
||||
uvm_mmu_page_table_cpu_memset_16(tree->gpu, &directory->phys_alloc, start_index + i, pde_data, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void pde_fill_gpu(uvm_page_tree_t *tree,
|
||||
uvm_page_directory_t *directory,
|
||||
NvU32 start_index,
|
||||
NvU32 pde_count,
|
||||
uvm_mmu_page_table_alloc_t **phys_addr,
|
||||
uvm_push_t *push)
|
||||
{
|
||||
NvU64 pde_data[2], entry_size;
|
||||
uvm_gpu_address_t pde_entry_addr = uvm_mmu_gpu_address(tree->gpu, directory->phys_alloc.addr);
|
||||
NvU32 max_inline_entries;
|
||||
uvm_push_flag_t push_membar_flag = UVM_PUSH_FLAG_COUNT;
|
||||
uvm_gpu_address_t inline_data_addr;
|
||||
uvm_push_inline_data_t inline_data;
|
||||
NvU32 entry_count, i, j;
|
||||
|
||||
UVM_ASSERT(!uvm_mmu_use_cpu(tree));
|
||||
|
||||
entry_size = tree->hal->entry_size(directory->depth);
|
||||
UVM_ASSERT(sizeof(pde_data) >= entry_size);
|
||||
|
||||
max_inline_entries = UVM_PUSH_INLINE_DATA_MAX_SIZE / entry_size;
|
||||
|
||||
if (uvm_push_get_and_reset_flag(push, UVM_PUSH_FLAG_NEXT_MEMBAR_NONE))
|
||||
push_membar_flag = UVM_PUSH_FLAG_NEXT_MEMBAR_NONE;
|
||||
else if (uvm_push_get_and_reset_flag(push, UVM_PUSH_FLAG_NEXT_MEMBAR_GPU))
|
||||
push_membar_flag = UVM_PUSH_FLAG_NEXT_MEMBAR_GPU;
|
||||
|
||||
pde_entry_addr.address += start_index * entry_size;
|
||||
|
||||
for (i = 0; i < pde_count;) {
|
||||
// All but the first memory operation can be pipelined. We respect the
|
||||
// caller's pipelining settings for the first push.
|
||||
if (i != 0)
|
||||
uvm_push_set_flag(push, UVM_PUSH_FLAG_CE_NEXT_PIPELINED);
|
||||
|
||||
entry_count = min(pde_count - i, max_inline_entries);
|
||||
|
||||
// No membar is needed until the last memory operation. Otherwise,
|
||||
// use caller's membar flag.
|
||||
if ((i + entry_count) < pde_count)
|
||||
uvm_push_set_flag(push, UVM_PUSH_FLAG_NEXT_MEMBAR_NONE);
|
||||
else if (push_membar_flag != UVM_PUSH_FLAG_COUNT)
|
||||
uvm_push_set_flag(push, push_membar_flag);
|
||||
|
||||
uvm_push_inline_data_begin(push, &inline_data);
|
||||
for (j = 0; j < entry_count; j++) {
|
||||
tree->hal->make_pde(pde_data, phys_addr, directory->depth, directory->entries[start_index + i + j]);
|
||||
uvm_push_inline_data_add(&inline_data, pde_data, entry_size);
|
||||
}
|
||||
inline_data_addr = uvm_push_inline_data_end(&inline_data);
|
||||
|
||||
tree->gpu->parent->ce_hal->memcopy(push, pde_entry_addr, inline_data_addr, entry_count * entry_size);
|
||||
|
||||
i += entry_count;
|
||||
pde_entry_addr.address += entry_size * entry_count;
|
||||
}
|
||||
}
|
||||
|
||||
// pde_fill() populates pde_count PDE entries (starting at start_index) with
|
||||
// the same mapping, i.e., with the same physical address (phys_addr).
|
||||
// pde_fill() is optimized for pde_count == 1, which is the common case. The
|
||||
// map_remap() function is the only case where pde_count > 1, only used on GA100
|
||||
// GPUs for 512MB page size mappings.
|
||||
static void pde_fill(uvm_page_tree_t *tree,
|
||||
uvm_page_directory_t *directory,
|
||||
NvU32 start_index,
|
||||
NvU32 pde_count,
|
||||
uvm_mmu_page_table_alloc_t **phys_addr,
|
||||
uvm_push_t *push)
|
||||
{
|
||||
UVM_ASSERT(start_index + pde_count <= uvm_mmu_page_tree_entries(tree, directory->depth, UVM_PAGE_SIZE_AGNOSTIC));
|
||||
|
||||
if (push)
|
||||
pde_fill_gpu(tree, directory, start_index, pde_count, phys_addr, push);
|
||||
else
|
||||
pde_fill_cpu(tree, directory, start_index, pde_count, phys_addr);
|
||||
}
|
||||
|
||||
static void phys_mem_init(uvm_page_tree_t *tree, NvU32 page_size, uvm_page_directory_t *dir, uvm_push_t *push)
|
||||
{
|
||||
NvU32 entries_count = uvm_mmu_page_tree_entries(tree, dir->depth, page_size);
|
||||
NvU64 clear_bits[2];
|
||||
uvm_mmu_mode_hal_t *hal = tree->hal;
|
||||
|
||||
// Passing in NULL for the phys_allocs will mark the child entries as
|
||||
// invalid.
|
||||
uvm_mmu_page_table_alloc_t *phys_allocs[2] = {NULL, NULL};
|
||||
|
||||
// Init with an invalid PTE or clean PDE. Only Maxwell PDEs can have more
|
||||
// than 512 entries. We initialize them all with the same clean PDE.
|
||||
// Additionally, only ATS systems may require clean PDEs bit settings based
|
||||
// on the mapping VA.
|
||||
if (dir->depth == tree->hal->page_table_depth(page_size) || (entries_count > 512 && !g_uvm_global.ats.enabled)) {
|
||||
NvU64 clear_bits[2];
|
||||
|
||||
// If it is not a PTE, make a clean PDE.
|
||||
if (dir->depth != tree->hal->page_table_depth(page_size)) {
|
||||
tree->hal->make_pde(clear_bits, phys_allocs, dir->depth, dir->entries[0]);
|
||||
|
||||
// Make sure that using only clear_bits[0] will work.
|
||||
UVM_ASSERT(tree->hal->entry_size(dir->depth) == sizeof(clear_bits[0]) || clear_bits[0] == clear_bits[1]);
|
||||
}
|
||||
else {
|
||||
*clear_bits = 0;
|
||||
}
|
||||
|
||||
// Initialize the memory to a reasonable value.
|
||||
if (push) {
|
||||
tree->gpu->parent->ce_hal->memset_8(push,
|
||||
uvm_mmu_gpu_address(tree->gpu, dir->phys_alloc.addr),
|
||||
*clear_bits,
|
||||
dir->phys_alloc.size);
|
||||
}
|
||||
else {
|
||||
uvm_mmu_page_table_cpu_memset_8(tree->gpu,
|
||||
&dir->phys_alloc,
|
||||
0,
|
||||
*clear_bits,
|
||||
dir->phys_alloc.size / sizeof(*clear_bits));
|
||||
}
|
||||
if (dir->depth == tree->hal->page_table_depth(page_size)) {
|
||||
*clear_bits = 0; // Invalid PTE
|
||||
}
|
||||
else {
|
||||
pde_fill(tree, dir, 0, entries_count, phys_allocs, push);
|
||||
// passing in NULL for the phys_allocs will mark the child entries as invalid
|
||||
uvm_mmu_page_table_alloc_t *phys_allocs[2] = {NULL, NULL};
|
||||
hal->make_pde(clear_bits, phys_allocs, dir->depth);
|
||||
|
||||
// Make sure that using only clear_bits[0] will work
|
||||
UVM_ASSERT(hal->entry_size(dir->depth) == sizeof(clear_bits[0]) || clear_bits[0] == clear_bits[1]);
|
||||
}
|
||||
|
||||
// initialize the memory to a reasonable value
|
||||
if (push) {
|
||||
tree->gpu->parent->ce_hal->memset_8(push,
|
||||
uvm_mmu_gpu_address(tree->gpu, dir->phys_alloc.addr),
|
||||
*clear_bits,
|
||||
dir->phys_alloc.size);
|
||||
}
|
||||
else {
|
||||
uvm_mmu_page_table_cpu_memset_8(tree->gpu,
|
||||
&dir->phys_alloc,
|
||||
0,
|
||||
*clear_bits,
|
||||
dir->phys_alloc.size / sizeof(*clear_bits));
|
||||
}
|
||||
}
|
||||
|
||||
static uvm_page_directory_t *allocate_directory(uvm_page_tree_t *tree,
|
||||
@ -483,10 +367,8 @@ static uvm_page_directory_t *allocate_directory(uvm_page_tree_t *tree,
|
||||
NvLength phys_alloc_size = hal->allocation_size(depth, page_size);
|
||||
uvm_page_directory_t *dir;
|
||||
|
||||
// The page tree doesn't cache PTEs so space is not allocated for entries
|
||||
// that are always PTEs.
|
||||
// 2M PTEs may later become PDEs so pass UVM_PAGE_SIZE_AGNOSTIC, not
|
||||
// page_size.
|
||||
// The page tree doesn't cache PTEs so space is not allocated for entries that are always PTEs.
|
||||
// 2M PTEs may later become PDEs so pass UVM_PAGE_SIZE_AGNOSTIC, not page_size.
|
||||
if (depth == hal->page_table_depth(UVM_PAGE_SIZE_AGNOSTIC))
|
||||
entry_count = 0;
|
||||
else
|
||||
@ -527,6 +409,108 @@ static inline NvU32 index_to_entry(uvm_mmu_mode_hal_t *hal, NvU32 entry_index, N
|
||||
return hal->entries_per_index(depth) * entry_index + hal->entry_offset(depth, page_size);
|
||||
}
|
||||
|
||||
static void pde_fill_cpu(uvm_page_tree_t *tree,
|
||||
NvU32 depth,
|
||||
uvm_mmu_page_table_alloc_t *directory,
|
||||
NvU32 start_index,
|
||||
NvU32 pde_count,
|
||||
uvm_mmu_page_table_alloc_t **phys_addr)
|
||||
{
|
||||
NvU64 pde_data[2], entry_size;
|
||||
|
||||
UVM_ASSERT(uvm_mmu_use_cpu(tree));
|
||||
entry_size = tree->hal->entry_size(depth);
|
||||
UVM_ASSERT(sizeof(pde_data) >= entry_size);
|
||||
|
||||
tree->hal->make_pde(pde_data, phys_addr, depth);
|
||||
|
||||
if (entry_size == sizeof(pde_data[0]))
|
||||
uvm_mmu_page_table_cpu_memset_8(tree->gpu, directory, start_index, pde_data[0], pde_count);
|
||||
else
|
||||
uvm_mmu_page_table_cpu_memset_16(tree->gpu, directory, start_index, pde_data, pde_count);
|
||||
}
|
||||
|
||||
static void pde_fill_gpu(uvm_page_tree_t *tree,
|
||||
NvU32 depth,
|
||||
uvm_mmu_page_table_alloc_t *directory,
|
||||
NvU32 start_index,
|
||||
NvU32 pde_count,
|
||||
uvm_mmu_page_table_alloc_t **phys_addr,
|
||||
uvm_push_t *push)
|
||||
{
|
||||
NvU64 pde_data[2], entry_size;
|
||||
uvm_gpu_address_t pde_entry_addr = uvm_mmu_gpu_address(tree->gpu, directory->addr);
|
||||
|
||||
UVM_ASSERT(!uvm_mmu_use_cpu(tree));
|
||||
|
||||
entry_size = tree->hal->entry_size(depth);
|
||||
UVM_ASSERT(sizeof(pde_data) >= entry_size);
|
||||
|
||||
tree->hal->make_pde(pde_data, phys_addr, depth);
|
||||
pde_entry_addr.address += start_index * entry_size;
|
||||
|
||||
if (entry_size == sizeof(pde_data[0])) {
|
||||
tree->gpu->parent->ce_hal->memset_8(push, pde_entry_addr, pde_data[0], sizeof(pde_data[0]) * pde_count);
|
||||
}
|
||||
else {
|
||||
NvU32 max_inline_entries = UVM_PUSH_INLINE_DATA_MAX_SIZE / sizeof(pde_data);
|
||||
uvm_gpu_address_t inline_data_addr;
|
||||
uvm_push_inline_data_t inline_data;
|
||||
uvm_push_flag_t push_membar_flag = UVM_PUSH_FLAG_COUNT;
|
||||
NvU32 i;
|
||||
|
||||
if (uvm_push_get_and_reset_flag(push, UVM_PUSH_FLAG_NEXT_MEMBAR_NONE))
|
||||
push_membar_flag = UVM_PUSH_FLAG_NEXT_MEMBAR_NONE;
|
||||
else if (uvm_push_get_and_reset_flag(push, UVM_PUSH_FLAG_NEXT_MEMBAR_GPU))
|
||||
push_membar_flag = UVM_PUSH_FLAG_NEXT_MEMBAR_GPU;
|
||||
|
||||
for (i = 0; i < pde_count;) {
|
||||
NvU32 j;
|
||||
NvU32 entry_count = min(pde_count - i, max_inline_entries);
|
||||
|
||||
uvm_push_inline_data_begin(push, &inline_data);
|
||||
for (j = 0; j < entry_count; j++)
|
||||
uvm_push_inline_data_add(&inline_data, pde_data, sizeof(pde_data));
|
||||
inline_data_addr = uvm_push_inline_data_end(&inline_data);
|
||||
|
||||
// All but the first memcopy can be pipelined. We respect the
|
||||
// caller's pipelining settings for the first push.
|
||||
if (i != 0)
|
||||
uvm_push_set_flag(push, UVM_PUSH_FLAG_CE_NEXT_PIPELINED);
|
||||
|
||||
// No membar is needed until the last copy. Otherwise, use
|
||||
// caller's membar flag.
|
||||
if (i + entry_count < pde_count)
|
||||
uvm_push_set_flag(push, UVM_PUSH_FLAG_NEXT_MEMBAR_NONE);
|
||||
else if (push_membar_flag != UVM_PUSH_FLAG_COUNT)
|
||||
uvm_push_set_flag(push, push_membar_flag);
|
||||
|
||||
tree->gpu->parent->ce_hal->memcopy(push, pde_entry_addr, inline_data_addr, entry_count * sizeof(pde_data));
|
||||
|
||||
i += entry_count;
|
||||
pde_entry_addr.address += sizeof(pde_data) * entry_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pde_fill() populates pde_count PDE entries (starting at start_index) with
|
||||
// the same mapping, i.e., with the same physical address (phys_addr).
|
||||
static void pde_fill(uvm_page_tree_t *tree,
|
||||
NvU32 depth,
|
||||
uvm_mmu_page_table_alloc_t *directory,
|
||||
NvU32 start_index,
|
||||
NvU32 pde_count,
|
||||
uvm_mmu_page_table_alloc_t **phys_addr,
|
||||
uvm_push_t *push)
|
||||
{
|
||||
UVM_ASSERT(start_index + pde_count <= uvm_mmu_page_tree_entries(tree, depth, UVM_PAGE_SIZE_AGNOSTIC));
|
||||
|
||||
if (push)
|
||||
pde_fill_gpu(tree, depth, directory, start_index, pde_count, phys_addr, push);
|
||||
else
|
||||
pde_fill_cpu(tree, depth, directory, start_index, pde_count, phys_addr);
|
||||
}
|
||||
|
||||
static uvm_page_directory_t *host_pde_write(uvm_page_directory_t *dir,
|
||||
uvm_page_directory_t *parent,
|
||||
NvU32 index_in_parent)
|
||||
@ -556,7 +540,7 @@ static void pde_write(uvm_page_tree_t *tree,
|
||||
phys_allocs[i] = &entry->phys_alloc;
|
||||
}
|
||||
|
||||
pde_fill(tree, dir, entry_index, 1, phys_allocs, push);
|
||||
pde_fill(tree, dir->depth, &dir->phys_alloc, entry_index, 1, phys_allocs, push);
|
||||
}
|
||||
|
||||
static void host_pde_clear(uvm_page_tree_t *tree, uvm_page_directory_t *dir, NvU32 entry_index, NvU32 page_size)
|
||||
@ -829,11 +813,8 @@ static NV_STATUS allocate_page_table(uvm_page_tree_t *tree, NvU32 page_size, uvm
|
||||
|
||||
static void map_remap_deinit(uvm_page_tree_t *tree)
|
||||
{
|
||||
if (tree->map_remap.pde0) {
|
||||
phys_mem_deallocate(tree, &tree->map_remap.pde0->phys_alloc);
|
||||
uvm_kvfree(tree->map_remap.pde0);
|
||||
tree->map_remap.pde0 = NULL;
|
||||
}
|
||||
if (tree->map_remap.pde0.size)
|
||||
phys_mem_deallocate(tree, &tree->map_remap.pde0);
|
||||
|
||||
if (tree->map_remap.ptes_invalid_4k.size)
|
||||
phys_mem_deallocate(tree, &tree->map_remap.ptes_invalid_4k);
|
||||
@ -858,16 +839,10 @@ static NV_STATUS map_remap_init(uvm_page_tree_t *tree)
|
||||
// PDE1-depth(512M) PTE. We first map it to the pde0 directory, then we
|
||||
// return the PTE for the get_ptes()'s caller.
|
||||
if (tree->hal->page_sizes() & UVM_PAGE_SIZE_512M) {
|
||||
tree->map_remap.pde0 = allocate_directory(tree,
|
||||
UVM_PAGE_SIZE_2M,
|
||||
tree->hal->page_table_depth(UVM_PAGE_SIZE_2M),
|
||||
UVM_PMM_ALLOC_FLAGS_EVICT);
|
||||
if (tree->map_remap.pde0 == NULL) {
|
||||
status = NV_ERR_NO_MEMORY;
|
||||
status = allocate_page_table(tree, UVM_PAGE_SIZE_2M, &tree->map_remap.pde0);
|
||||
if (status != NV_OK)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
status = page_tree_begin_acquire(tree, &tree->tracker, &push, "map remap init");
|
||||
if (status != NV_OK)
|
||||
goto error;
|
||||
@ -889,23 +864,22 @@ static NV_STATUS map_remap_init(uvm_page_tree_t *tree)
|
||||
uvm_mmu_page_table_alloc_t *phys_allocs[2] = {NULL, NULL};
|
||||
NvU32 depth = tree->hal->page_table_depth(UVM_PAGE_SIZE_4K) - 1;
|
||||
size_t index_4k = tree->hal->entry_offset(depth, UVM_PAGE_SIZE_4K);
|
||||
NvU32 pde0_entries = tree->map_remap.pde0->phys_alloc.size / tree->hal->entry_size(tree->map_remap.pde0->depth);
|
||||
|
||||
// pde0 depth equals UVM_PAGE_SIZE_2M.
|
||||
NvU32 pde0_depth = tree->hal->page_table_depth(UVM_PAGE_SIZE_2M);
|
||||
NvU32 pde0_entries = tree->map_remap.pde0.size / tree->hal->entry_size(pde0_depth);
|
||||
|
||||
// The big-page entry is NULL which makes it an invalid entry.
|
||||
phys_allocs[index_4k] = &tree->map_remap.ptes_invalid_4k;
|
||||
|
||||
// By default CE operations include a MEMBAR_SYS. MEMBAR_GPU is
|
||||
// sufficient when pde0 is allocated in VIDMEM.
|
||||
if (tree->map_remap.pde0->phys_alloc.addr.aperture == UVM_APERTURE_VID)
|
||||
if (tree->map_remap.pde0.addr.aperture == UVM_APERTURE_VID)
|
||||
uvm_push_set_flag(&push, UVM_PUSH_FLAG_NEXT_MEMBAR_GPU);
|
||||
|
||||
// This is an orphan directory, make_pde() requires a directory to
|
||||
// compute the VA. The UVM depth map_remap() operates on is not in the
|
||||
// range make_pde() must operate. We only need to supply the fields used
|
||||
// by make_pde() to not access invalid memory addresses.
|
||||
|
||||
pde_fill(tree,
|
||||
tree->map_remap.pde0,
|
||||
pde0_depth,
|
||||
&tree->map_remap.pde0,
|
||||
0,
|
||||
pde0_entries,
|
||||
(uvm_mmu_page_table_alloc_t **)&phys_allocs,
|
||||
@ -932,10 +906,11 @@ error:
|
||||
// --------------|-------------------------||----------------|----------------
|
||||
// vidmem | - || vidmem | false
|
||||
// sysmem | - || sysmem | false
|
||||
// default | <not set> || vidmem | true
|
||||
// default | <not set> || vidmem | true (1)
|
||||
// default | vidmem || vidmem | false
|
||||
// default | sysmem || sysmem | false
|
||||
//
|
||||
// (1) When SEV mode is enabled, the fallback path is disabled.
|
||||
//
|
||||
// In SR-IOV heavy the the page tree must be in vidmem, to prevent guest drivers
|
||||
// from updating GPU page tables without hypervisor knowledge.
|
||||
@ -951,27 +926,28 @@ error:
|
||||
//
|
||||
static void page_tree_set_location(uvm_page_tree_t *tree, uvm_aperture_t location)
|
||||
{
|
||||
bool should_location_be_vidmem;
|
||||
UVM_ASSERT(tree->gpu != NULL);
|
||||
UVM_ASSERT_MSG((location == UVM_APERTURE_VID) ||
|
||||
(location == UVM_APERTURE_SYS) ||
|
||||
(location == UVM_APERTURE_DEFAULT),
|
||||
"Invalid location %s (%d)\n", uvm_aperture_string(location), (int)location);
|
||||
|
||||
// The page tree of a "fake" GPU used during page tree testing can be in
|
||||
// sysmem in scenarios where a "real" GPU must be in vidmem. Fake GPUs can
|
||||
// be identified by having no channel manager.
|
||||
if (tree->gpu->channel_manager != NULL) {
|
||||
should_location_be_vidmem = uvm_gpu_is_virt_mode_sriov_heavy(tree->gpu)
|
||||
|| uvm_conf_computing_mode_enabled(tree->gpu);
|
||||
|
||||
if (uvm_gpu_is_virt_mode_sriov_heavy(tree->gpu))
|
||||
UVM_ASSERT(location == UVM_APERTURE_VID);
|
||||
else if (uvm_conf_computing_mode_enabled(tree->gpu))
|
||||
UVM_ASSERT(location == UVM_APERTURE_VID);
|
||||
}
|
||||
// The page tree of a "fake" GPU used during page tree testing can be in
|
||||
// sysmem even if should_location_be_vidmem is true. A fake GPU can be
|
||||
// identified by having no channel manager.
|
||||
if ((tree->gpu->channel_manager != NULL) && should_location_be_vidmem)
|
||||
UVM_ASSERT(location == UVM_APERTURE_VID);
|
||||
|
||||
if (location == UVM_APERTURE_DEFAULT) {
|
||||
if (page_table_aperture == UVM_APERTURE_DEFAULT) {
|
||||
tree->location = UVM_APERTURE_VID;
|
||||
tree->location_sys_fallback = true;
|
||||
|
||||
// See the comment (1) above.
|
||||
tree->location_sys_fallback = !g_uvm_global.sev_enabled;
|
||||
}
|
||||
else {
|
||||
tree->location = page_table_aperture;
|
||||
@ -1358,9 +1334,10 @@ static NV_STATUS map_remap(uvm_page_tree_t *tree, NvU64 start, NvLength size, uv
|
||||
if (uvm_page_table_range_aperture(range) == UVM_APERTURE_VID)
|
||||
uvm_push_set_flag(&push, UVM_PUSH_FLAG_NEXT_MEMBAR_GPU);
|
||||
|
||||
phys_alloc[0] = &tree->map_remap.pde0->phys_alloc;
|
||||
phys_alloc[0] = &tree->map_remap.pde0;
|
||||
pde_fill(tree,
|
||||
range->table,
|
||||
range->table->depth,
|
||||
&range->table->phys_alloc,
|
||||
range->start_index,
|
||||
range->entry_count,
|
||||
(uvm_mmu_page_table_alloc_t **)&phys_alloc,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2015-2023 NVIDIA Corporation
|
||||
Copyright (c) 2015-2022 NVIDIA Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
@ -219,7 +219,7 @@ struct uvm_mmu_mode_hal_struct
|
||||
// point to two items for dual PDEs).
|
||||
// any of allocs are allowed to be NULL, in which case they are to be
|
||||
// treated as empty.
|
||||
void (*make_pde)(void *entry, uvm_mmu_page_table_alloc_t **allocs, NvU32 depth, uvm_page_directory_t *child_dir);
|
||||
void (*make_pde)(void *entry, uvm_mmu_page_table_alloc_t **allocs, NvU32 depth);
|
||||
|
||||
// size of an entry in a directory/table. Generally either 8 or 16 bytes.
|
||||
// (in the case of Pascal dual PDEs)
|
||||
@ -229,7 +229,7 @@ struct uvm_mmu_mode_hal_struct
|
||||
NvU32 (*entries_per_index)(NvU32 depth);
|
||||
|
||||
// For dual PDEs, this is ether 1 or 0, depending on the page size.
|
||||
// This is used to index the host copy only. GPU PDEs are always entirely
|
||||
// This is used to index the host copy only. GPU PDEs are always entirely
|
||||
// re-written using make_pde.
|
||||
NvLength (*entry_offset)(NvU32 depth, NvU32 page_size);
|
||||
|
||||
@ -295,8 +295,9 @@ struct uvm_page_tree_struct
|
||||
|
||||
// PDE0 where all big-page entries are invalid, and small-page entries
|
||||
// point to ptes_invalid_4k.
|
||||
// pde0 is used on Pascal+ GPUs, i.e., they have the same PDE format.
|
||||
uvm_page_directory_t *pde0;
|
||||
// pde0 is only used on Pascal-Ampere, i.e., they have the same PDE
|
||||
// format.
|
||||
uvm_mmu_page_table_alloc_t pde0;
|
||||
} map_remap;
|
||||
|
||||
// Tracker for all GPU operations on the tree
|
||||
@ -364,32 +365,21 @@ void uvm_page_tree_deinit(uvm_page_tree_t *tree);
|
||||
// the same page size without an intervening put_ptes. To duplicate a subset of
|
||||
// an existing range or change the size of an existing range, use
|
||||
// uvm_page_table_range_get_upper() and/or uvm_page_table_range_shrink().
|
||||
NV_STATUS uvm_page_tree_get_ptes(uvm_page_tree_t *tree,
|
||||
NvU32 page_size,
|
||||
NvU64 start,
|
||||
NvLength size,
|
||||
uvm_pmm_alloc_flags_t pmm_flags,
|
||||
uvm_page_table_range_t *range);
|
||||
NV_STATUS uvm_page_tree_get_ptes(uvm_page_tree_t *tree, NvU32 page_size, NvU64 start, NvLength size,
|
||||
uvm_pmm_alloc_flags_t pmm_flags, uvm_page_table_range_t *range);
|
||||
|
||||
// Same as uvm_page_tree_get_ptes(), but doesn't synchronize the GPU work.
|
||||
//
|
||||
// All pending operations can be waited on with uvm_page_tree_wait().
|
||||
NV_STATUS uvm_page_tree_get_ptes_async(uvm_page_tree_t *tree,
|
||||
NvU32 page_size,
|
||||
NvU64 start,
|
||||
NvLength size,
|
||||
uvm_pmm_alloc_flags_t pmm_flags,
|
||||
uvm_page_table_range_t *range);
|
||||
NV_STATUS uvm_page_tree_get_ptes_async(uvm_page_tree_t *tree, NvU32 page_size, NvU64 start, NvLength size,
|
||||
uvm_pmm_alloc_flags_t pmm_flags, uvm_page_table_range_t *range);
|
||||
|
||||
// Returns a single-entry page table range for the addresses passed.
|
||||
// The size parameter must be a page size supported by this tree.
|
||||
// This is equivalent to calling uvm_page_tree_get_ptes() with size equal to
|
||||
// page_size.
|
||||
NV_STATUS uvm_page_tree_get_entry(uvm_page_tree_t *tree,
|
||||
NvU32 page_size,
|
||||
NvU64 start,
|
||||
uvm_pmm_alloc_flags_t pmm_flags,
|
||||
uvm_page_table_range_t *single);
|
||||
NV_STATUS uvm_page_tree_get_entry(uvm_page_tree_t *tree, NvU32 page_size, NvU64 start,
|
||||
uvm_pmm_alloc_flags_t pmm_flags, uvm_page_table_range_t *single);
|
||||
|
||||
// For a single-entry page table range, write the PDE (which could be a dual
|
||||
// PDE) to the GPU.
|
||||
@ -488,8 +478,8 @@ NV_STATUS uvm_page_table_range_vec_create(uvm_page_tree_t *tree,
|
||||
// new_range_vec will contain the upper portion of range_vec, starting at
|
||||
// new_end + 1.
|
||||
//
|
||||
// new_end + 1 is required to be within the address range of range_vec and be
|
||||
// aligned to range_vec's page_size.
|
||||
// new_end + 1 is required to be within the address range of range_vec and be aligned to
|
||||
// range_vec's page_size.
|
||||
//
|
||||
// On failure, the original range vector is left unmodified.
|
||||
NV_STATUS uvm_page_table_range_vec_split_upper(uvm_page_table_range_vec_t *range_vec,
|
||||
@ -511,22 +501,18 @@ void uvm_page_table_range_vec_destroy(uvm_page_table_range_vec_t *range_vec);
|
||||
// for each offset.
|
||||
// The caller_data pointer is what the caller passed in as caller_data to
|
||||
// uvm_page_table_range_vec_write_ptes().
|
||||
typedef NvU64 (*uvm_page_table_range_pte_maker_t)(uvm_page_table_range_vec_t *range_vec,
|
||||
NvU64 offset,
|
||||
void *caller_data);
|
||||
typedef NvU64 (*uvm_page_table_range_pte_maker_t)(uvm_page_table_range_vec_t *range_vec, NvU64 offset,
|
||||
void *caller_data);
|
||||
|
||||
// Write all PTEs covered by the range vector using the given PTE making
|
||||
// function.
|
||||
// Write all PTEs covered by the range vector using the given PTE making function.
|
||||
//
|
||||
// After writing all the PTEs a TLB invalidate operation is performed including
|
||||
// the passed in tlb_membar.
|
||||
//
|
||||
// See comments about uvm_page_table_range_pte_maker_t for details about the
|
||||
// PTE making callback.
|
||||
NV_STATUS uvm_page_table_range_vec_write_ptes(uvm_page_table_range_vec_t *range_vec,
|
||||
uvm_membar_t tlb_membar,
|
||||
uvm_page_table_range_pte_maker_t pte_maker,
|
||||
void *caller_data);
|
||||
NV_STATUS uvm_page_table_range_vec_write_ptes(uvm_page_table_range_vec_t *range_vec, uvm_membar_t tlb_membar,
|
||||
uvm_page_table_range_pte_maker_t pte_maker, void *caller_data);
|
||||
|
||||
// Set all PTEs covered by the range vector to an empty PTE
|
||||
//
|
||||
@ -650,9 +636,8 @@ static NvU64 uvm_page_table_range_size(uvm_page_table_range_t *range)
|
||||
|
||||
// Get the physical address of the entry at entry_index within the range
|
||||
// (counted from range->start_index).
|
||||
static uvm_gpu_phys_address_t uvm_page_table_range_entry_address(uvm_page_tree_t *tree,
|
||||
uvm_page_table_range_t *range,
|
||||
size_t entry_index)
|
||||
static uvm_gpu_phys_address_t uvm_page_table_range_entry_address(uvm_page_tree_t *tree, uvm_page_table_range_t *range,
|
||||
size_t entry_index)
|
||||
{
|
||||
NvU32 entry_size = uvm_mmu_pte_size(tree, range->page_size);
|
||||
uvm_gpu_phys_address_t entry = range->table->phys_alloc.addr;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2015-2023 NVIDIA Corporation
|
||||
Copyright (c) 2015-2022 NVIDIA Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
@ -146,15 +146,9 @@ static void fake_tlb_invals_disable(void)
|
||||
g_fake_tlb_invals_tracking_enabled = false;
|
||||
}
|
||||
|
||||
// Fake TLB invalidate VA that just saves off the parameters so that they can be
|
||||
// verified later.
|
||||
static void fake_tlb_invalidate_va(uvm_push_t *push,
|
||||
uvm_gpu_phys_address_t pdb,
|
||||
NvU32 depth,
|
||||
NvU64 base,
|
||||
NvU64 size,
|
||||
NvU32 page_size,
|
||||
uvm_membar_t membar)
|
||||
// Fake TLB invalidate VA that just saves off the parameters so that they can be verified later
|
||||
static void fake_tlb_invalidate_va(uvm_push_t *push, uvm_gpu_phys_address_t pdb,
|
||||
NvU32 depth, NvU64 base, NvU64 size, NvU32 page_size, uvm_membar_t membar)
|
||||
{
|
||||
if (!g_fake_tlb_invals_tracking_enabled)
|
||||
return;
|
||||
@ -216,8 +210,8 @@ static bool assert_and_reset_last_invalidate(NvU32 expected_depth, bool expected
|
||||
}
|
||||
if ((g_last_fake_inval->membar == UVM_MEMBAR_NONE) == expected_membar) {
|
||||
UVM_TEST_PRINT("Expected %s membar, got %s instead\n",
|
||||
expected_membar ? "a" : "no",
|
||||
uvm_membar_string(g_last_fake_inval->membar));
|
||||
expected_membar ? "a" : "no",
|
||||
uvm_membar_string(g_last_fake_inval->membar));
|
||||
result = false;
|
||||
}
|
||||
|
||||
@ -236,8 +230,7 @@ static bool assert_last_invalidate_all(NvU32 expected_depth, bool expected_memba
|
||||
}
|
||||
if (g_last_fake_inval->base != 0 || g_last_fake_inval->size != -1) {
|
||||
UVM_TEST_PRINT("Expected invalidate all but got range [0x%llx, 0x%llx) instead\n",
|
||||
g_last_fake_inval->base,
|
||||
g_last_fake_inval->base + g_last_fake_inval->size);
|
||||
g_last_fake_inval->base, g_last_fake_inval->base + g_last_fake_inval->size);
|
||||
return false;
|
||||
}
|
||||
if (g_last_fake_inval->depth != expected_depth) {
|
||||
@ -254,16 +247,15 @@ static bool assert_invalidate_range_specific(fake_tlb_invalidate_t *inval,
|
||||
UVM_ASSERT(g_fake_tlb_invals_tracking_enabled);
|
||||
|
||||
if (g_fake_invals_count == 0) {
|
||||
UVM_TEST_PRINT("Expected an invalidate for range [0x%llx, 0x%llx), but got none\n", base, base + size);
|
||||
UVM_TEST_PRINT("Expected an invalidate for range [0x%llx, 0x%llx), but got none\n",
|
||||
base, base + size);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((inval->base != base || inval->size != size) && inval->base != 0 && inval->size != -1) {
|
||||
UVM_TEST_PRINT("Expected invalidate range [0x%llx, 0x%llx), but got range [0x%llx, 0x%llx) instead\n",
|
||||
base,
|
||||
base + size,
|
||||
inval->base,
|
||||
inval->base + inval->size);
|
||||
base, base + size,
|
||||
inval->base, inval->base + inval->size);
|
||||
return false;
|
||||
}
|
||||
if (inval->depth != expected_depth) {
|
||||
@ -278,13 +270,7 @@ static bool assert_invalidate_range_specific(fake_tlb_invalidate_t *inval,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool assert_invalidate_range(NvU64 base,
|
||||
NvU64 size,
|
||||
NvU32 page_size,
|
||||
bool allow_inval_all,
|
||||
NvU32 range_depth,
|
||||
NvU32 all_depth,
|
||||
bool expected_membar)
|
||||
static bool assert_invalidate_range(NvU64 base, NvU64 size, NvU32 page_size, bool allow_inval_all, NvU32 range_depth, NvU32 all_depth, bool expected_membar)
|
||||
{
|
||||
NvU32 i;
|
||||
|
||||
@ -502,6 +488,7 @@ static NV_STATUS alloc_adjacent_pde_64k_memory(uvm_gpu_t *gpu)
|
||||
return NV_OK;
|
||||
}
|
||||
|
||||
|
||||
static NV_STATUS alloc_nearby_pde_64k_memory(uvm_gpu_t *gpu)
|
||||
{
|
||||
uvm_page_tree_t tree;
|
||||
@ -855,7 +842,6 @@ static NV_STATUS get_two_free_apart(uvm_gpu_t *gpu)
|
||||
TEST_CHECK_RET(range2.entry_count == 256);
|
||||
TEST_CHECK_RET(range2.table->ref_count == 512);
|
||||
TEST_CHECK_RET(range1.table == range2.table);
|
||||
|
||||
// 4k page is second entry in a dual PDE
|
||||
TEST_CHECK_RET(range1.table == tree.root->entries[0]->entries[0]->entries[0]->entries[1]);
|
||||
TEST_CHECK_RET(range1.start_index == 256);
|
||||
@ -885,7 +871,6 @@ static NV_STATUS get_overlapping_dual_pdes(uvm_gpu_t *gpu)
|
||||
MEM_NV_CHECK_RET(test_page_tree_get_ptes(&tree, UVM_PAGE_SIZE_64K, size, size, &range64k), NV_OK);
|
||||
TEST_CHECK_RET(range64k.entry_count == 16);
|
||||
TEST_CHECK_RET(range64k.table->ref_count == 16);
|
||||
|
||||
// 4k page is second entry in a dual PDE
|
||||
TEST_CHECK_RET(range64k.table == tree.root->entries[0]->entries[0]->entries[0]->entries[0]);
|
||||
TEST_CHECK_RET(range64k.start_index == 16);
|
||||
@ -1045,13 +1030,10 @@ static NV_STATUS test_tlb_invalidates(uvm_gpu_t *gpu)
|
||||
|
||||
// Depth 4
|
||||
NvU64 extent_pte = UVM_PAGE_SIZE_2M;
|
||||
|
||||
// Depth 3
|
||||
NvU64 extent_pde0 = extent_pte * (1ull << 8);
|
||||
|
||||
// Depth 2
|
||||
NvU64 extent_pde1 = extent_pde0 * (1ull << 9);
|
||||
|
||||
// Depth 1
|
||||
NvU64 extent_pde2 = extent_pde1 * (1ull << 9);
|
||||
|
||||
@ -1099,11 +1081,7 @@ static NV_STATUS test_tlb_invalidates(uvm_gpu_t *gpu)
|
||||
return status;
|
||||
}
|
||||
|
||||
static NV_STATUS test_tlb_batch_invalidates_case(uvm_page_tree_t *tree,
|
||||
NvU64 base,
|
||||
NvU64 size,
|
||||
NvU32 min_page_size,
|
||||
NvU32 max_page_size)
|
||||
static NV_STATUS test_tlb_batch_invalidates_case(uvm_page_tree_t *tree, NvU64 base, NvU64 size, NvU32 min_page_size, NvU32 max_page_size)
|
||||
{
|
||||
NV_STATUS status = NV_OK;
|
||||
uvm_push_t push;
|
||||
@ -1227,11 +1205,7 @@ static bool assert_range_vec_ptes(uvm_page_table_range_vec_t *range_vec, bool ex
|
||||
NvU64 expected_pte = expecting_cleared ? 0 : range_vec->size + offset;
|
||||
if (*pte != expected_pte) {
|
||||
UVM_TEST_PRINT("PTE is 0x%llx instead of 0x%llx for offset 0x%llx within range [0x%llx, 0x%llx)\n",
|
||||
*pte,
|
||||
expected_pte,
|
||||
offset,
|
||||
range_vec->start,
|
||||
range_vec->size);
|
||||
*pte, expected_pte, offset, range_vec->start, range_vec->size);
|
||||
return false;
|
||||
}
|
||||
offset += range_vec->page_size;
|
||||
@ -1252,11 +1226,7 @@ static NV_STATUS test_range_vec_write_ptes(uvm_page_table_range_vec_t *range_vec
|
||||
TEST_CHECK_RET(data.status == NV_OK);
|
||||
TEST_CHECK_RET(data.count == range_vec->size / range_vec->page_size);
|
||||
TEST_CHECK_RET(assert_invalidate_range_specific(g_last_fake_inval,
|
||||
range_vec->start,
|
||||
range_vec->size,
|
||||
range_vec->page_size,
|
||||
page_table_depth,
|
||||
membar != UVM_MEMBAR_NONE));
|
||||
range_vec->start, range_vec->size, range_vec->page_size, page_table_depth, membar != UVM_MEMBAR_NONE));
|
||||
TEST_CHECK_RET(assert_range_vec_ptes(range_vec, false));
|
||||
|
||||
fake_tlb_invals_disable();
|
||||
@ -1279,11 +1249,7 @@ static NV_STATUS test_range_vec_clear_ptes(uvm_page_table_range_vec_t *range_vec
|
||||
return NV_OK;
|
||||
}
|
||||
|
||||
static NV_STATUS test_range_vec_create(uvm_page_tree_t *tree,
|
||||
NvU64 start,
|
||||
NvU64 size,
|
||||
NvU32 page_size,
|
||||
uvm_page_table_range_vec_t **range_vec_out)
|
||||
static NV_STATUS test_range_vec_create(uvm_page_tree_t *tree, NvU64 start, NvU64 size, NvU32 page_size, uvm_page_table_range_vec_t **range_vec_out)
|
||||
{
|
||||
uvm_page_table_range_vec_t *range_vec;
|
||||
uvm_pmm_alloc_flags_t pmm_flags = UVM_PMM_ALLOC_FLAGS_EVICT;
|
||||
@ -1586,17 +1552,17 @@ static NV_STATUS entry_test_maxwell(uvm_gpu_t *gpu)
|
||||
|
||||
memset(phys_allocs, 0, sizeof(phys_allocs));
|
||||
|
||||
hal->make_pde(&pde_bits, phys_allocs, 0, NULL);
|
||||
hal->make_pde(&pde_bits, phys_allocs, 0);
|
||||
TEST_CHECK_RET(pde_bits == 0x0L);
|
||||
|
||||
phys_allocs[0] = &alloc_sys;
|
||||
phys_allocs[1] = &alloc_vid;
|
||||
hal->make_pde(&pde_bits, phys_allocs, 0, NULL);
|
||||
hal->make_pde(&pde_bits, phys_allocs, 0);
|
||||
TEST_CHECK_RET(pde_bits == 0x1BBBBBBD99999992LL);
|
||||
|
||||
phys_allocs[0] = &alloc_vid;
|
||||
phys_allocs[1] = &alloc_sys;
|
||||
hal->make_pde(&pde_bits, phys_allocs, 0, NULL);
|
||||
hal->make_pde(&pde_bits, phys_allocs, 0);
|
||||
TEST_CHECK_RET(pde_bits == 0x9999999E1BBBBBB1LL);
|
||||
|
||||
for (j = 0; j <= 2; j++) {
|
||||
@ -1666,7 +1632,6 @@ static NV_STATUS entry_test_pascal(uvm_gpu_t *gpu, entry_test_page_size_func ent
|
||||
uvm_mmu_page_table_alloc_t *phys_allocs[2] = {NULL, NULL};
|
||||
uvm_mmu_page_table_alloc_t alloc_sys = fake_table_alloc(UVM_APERTURE_SYS, 0x399999999999000LL);
|
||||
uvm_mmu_page_table_alloc_t alloc_vid = fake_table_alloc(UVM_APERTURE_VID, 0x1BBBBBB000LL);
|
||||
|
||||
// big versions have [11:8] set as well to test the page table merging
|
||||
uvm_mmu_page_table_alloc_t alloc_big_sys = fake_table_alloc(UVM_APERTURE_SYS, 0x399999999999900LL);
|
||||
uvm_mmu_page_table_alloc_t alloc_big_vid = fake_table_alloc(UVM_APERTURE_VID, 0x1BBBBBBB00LL);
|
||||
@ -1674,31 +1639,31 @@ static NV_STATUS entry_test_pascal(uvm_gpu_t *gpu, entry_test_page_size_func ent
|
||||
uvm_mmu_mode_hal_t *hal = gpu->parent->arch_hal->mmu_mode_hal(UVM_PAGE_SIZE_64K);
|
||||
|
||||
// Make sure cleared PDEs work as expected
|
||||
hal->make_pde(pde_bits, phys_allocs, 0, NULL);
|
||||
hal->make_pde(pde_bits, phys_allocs, 0);
|
||||
TEST_CHECK_RET(pde_bits[0] == 0);
|
||||
|
||||
memset(pde_bits, 0xFF, sizeof(pde_bits));
|
||||
hal->make_pde(pde_bits, phys_allocs, 3, NULL);
|
||||
hal->make_pde(pde_bits, phys_allocs, 3);
|
||||
TEST_CHECK_RET(pde_bits[0] == 0 && pde_bits[1] == 0);
|
||||
|
||||
// Sys and vidmem PDEs
|
||||
phys_allocs[0] = &alloc_sys;
|
||||
hal->make_pde(pde_bits, phys_allocs, 0, NULL);
|
||||
hal->make_pde(pde_bits, phys_allocs, 0);
|
||||
TEST_CHECK_RET(pde_bits[0] == 0x3999999999990C);
|
||||
|
||||
phys_allocs[0] = &alloc_vid;
|
||||
hal->make_pde(pde_bits, phys_allocs, 0, NULL);
|
||||
hal->make_pde(pde_bits, phys_allocs, 0);
|
||||
TEST_CHECK_RET(pde_bits[0] == 0x1BBBBBB0A);
|
||||
|
||||
// Dual PDEs
|
||||
phys_allocs[0] = &alloc_big_sys;
|
||||
phys_allocs[1] = &alloc_vid;
|
||||
hal->make_pde(pde_bits, phys_allocs, 3, NULL);
|
||||
hal->make_pde(pde_bits, phys_allocs, 3);
|
||||
TEST_CHECK_RET(pde_bits[0] == 0x3999999999999C && pde_bits[1] == 0x1BBBBBB0A);
|
||||
|
||||
phys_allocs[0] = &alloc_big_vid;
|
||||
phys_allocs[1] = &alloc_sys;
|
||||
hal->make_pde(pde_bits, phys_allocs, 3, NULL);
|
||||
hal->make_pde(pde_bits, phys_allocs, 3);
|
||||
TEST_CHECK_RET(pde_bits[0] == 0x1BBBBBBBA && pde_bits[1] == 0x3999999999990C);
|
||||
|
||||
// uncached, i.e., the sysmem data is not cached in GPU's L2 cache. Clear
|
||||
@ -1762,36 +1727,36 @@ static NV_STATUS entry_test_volta(uvm_gpu_t *gpu, entry_test_page_size_func entr
|
||||
uvm_mmu_mode_hal_t *hal = gpu->parent->arch_hal->mmu_mode_hal(UVM_PAGE_SIZE_64K);
|
||||
|
||||
// Make sure cleared PDEs work as expected
|
||||
hal->make_pde(pde_bits, phys_allocs, 0, NULL);
|
||||
hal->make_pde(pde_bits, phys_allocs, 0);
|
||||
TEST_CHECK_RET(pde_bits[0] == 0);
|
||||
|
||||
memset(pde_bits, 0xFF, sizeof(pde_bits));
|
||||
hal->make_pde(pde_bits, phys_allocs, 3, NULL);
|
||||
hal->make_pde(pde_bits, phys_allocs, 3);
|
||||
TEST_CHECK_RET(pde_bits[0] == 0 && pde_bits[1] == 0);
|
||||
|
||||
// Sys and vidmem PDEs
|
||||
phys_allocs[0] = &alloc_sys;
|
||||
hal->make_pde(pde_bits, phys_allocs, 0, NULL);
|
||||
hal->make_pde(pde_bits, phys_allocs, 0);
|
||||
TEST_CHECK_RET(pde_bits[0] == 0x3999999999990C);
|
||||
|
||||
phys_allocs[0] = &alloc_vid;
|
||||
hal->make_pde(pde_bits, phys_allocs, 0, NULL);
|
||||
hal->make_pde(pde_bits, phys_allocs, 0);
|
||||
TEST_CHECK_RET(pde_bits[0] == 0x1BBBBBB0A);
|
||||
|
||||
// Dual PDEs
|
||||
phys_allocs[0] = &alloc_big_sys;
|
||||
phys_allocs[1] = &alloc_vid;
|
||||
hal->make_pde(pde_bits, phys_allocs, 3, NULL);
|
||||
hal->make_pde(pde_bits, phys_allocs, 3);
|
||||
TEST_CHECK_RET(pde_bits[0] == 0x3999999999999C && pde_bits[1] == 0x1BBBBBB0A);
|
||||
|
||||
phys_allocs[0] = &alloc_big_vid;
|
||||
phys_allocs[1] = &alloc_sys;
|
||||
hal->make_pde(pde_bits, phys_allocs, 3, NULL);
|
||||
hal->make_pde(pde_bits, phys_allocs, 3);
|
||||
TEST_CHECK_RET(pde_bits[0] == 0x1BBBBBBBA && pde_bits[1] == 0x3999999999990C);
|
||||
|
||||
// NO_ATS PDE1 (depth 2)
|
||||
phys_allocs[0] = &alloc_vid;
|
||||
hal->make_pde(pde_bits, phys_allocs, 2, NULL);
|
||||
hal->make_pde(pde_bits, phys_allocs, 2);
|
||||
if (g_uvm_global.ats.enabled)
|
||||
TEST_CHECK_RET(pde_bits[0] == 0x1BBBBBB2A);
|
||||
else
|
||||
@ -1840,32 +1805,32 @@ static NV_STATUS entry_test_hopper(uvm_gpu_t *gpu, entry_test_page_size_func ent
|
||||
uvm_mmu_mode_hal_t *hal = gpu->parent->arch_hal->mmu_mode_hal(UVM_PAGE_SIZE_64K);
|
||||
|
||||
// Make sure cleared PDEs work as expected
|
||||
hal->make_pde(pde_bits, phys_allocs, 0, NULL);
|
||||
hal->make_pde(pde_bits, phys_allocs, 0);
|
||||
TEST_CHECK_RET(pde_bits[0] == 0);
|
||||
|
||||
// Cleared PDEs work as expected for big and small PDEs.
|
||||
memset(pde_bits, 0xFF, sizeof(pde_bits));
|
||||
hal->make_pde(pde_bits, phys_allocs, 4, NULL);
|
||||
hal->make_pde(pde_bits, phys_allocs, 4);
|
||||
TEST_CHECK_RET(pde_bits[0] == 0 && pde_bits[1] == 0);
|
||||
|
||||
// Sys and vidmem PDEs, uncached ATS allowed.
|
||||
phys_allocs[0] = &alloc_sys;
|
||||
hal->make_pde(pde_bits, phys_allocs, 0, NULL);
|
||||
hal->make_pde(pde_bits, phys_allocs, 0);
|
||||
TEST_CHECK_RET(pde_bits[0] == 0x999999999900C);
|
||||
|
||||
phys_allocs[0] = &alloc_vid;
|
||||
hal->make_pde(pde_bits, phys_allocs, 0, NULL);
|
||||
hal->make_pde(pde_bits, phys_allocs, 0);
|
||||
TEST_CHECK_RET(pde_bits[0] == 0xBBBBBBB00A);
|
||||
|
||||
// Dual PDEs, uncached.
|
||||
phys_allocs[0] = &alloc_big_sys;
|
||||
phys_allocs[1] = &alloc_vid;
|
||||
hal->make_pde(pde_bits, phys_allocs, 4, NULL);
|
||||
hal->make_pde(pde_bits, phys_allocs, 4);
|
||||
TEST_CHECK_RET(pde_bits[0] == 0x999999999991C && pde_bits[1] == 0xBBBBBBB01A);
|
||||
|
||||
phys_allocs[0] = &alloc_big_vid;
|
||||
phys_allocs[1] = &alloc_sys;
|
||||
hal->make_pde(pde_bits, phys_allocs, 4, NULL);
|
||||
hal->make_pde(pde_bits, phys_allocs, 4);
|
||||
TEST_CHECK_RET(pde_bits[0] == 0xBBBBBBBB1A && pde_bits[1] == 0x999999999901C);
|
||||
|
||||
// uncached, i.e., the sysmem data is not cached in GPU's L2 cache, and
|
||||
@ -2338,8 +2303,7 @@ NV_STATUS uvm_test_page_tree(UVM_TEST_PAGE_TREE_PARAMS *params, struct file *fil
|
||||
gpu->parent = parent_gpu;
|
||||
|
||||
// At least test_tlb_invalidates() relies on global state
|
||||
// (g_tlb_invalidate_*) so make sure only one test instance can run at a
|
||||
// time.
|
||||
// (g_tlb_invalidate_*) so make sure only one test instance can run at a time.
|
||||
uvm_mutex_lock(&g_uvm_global.global_lock);
|
||||
|
||||
// Allocate the fake TLB tracking state. Notably tests still need to enable
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2015-2023 NVIDIA Corporation
|
||||
Copyright (c) 2015-2020 NVIDIA Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
@ -140,10 +140,7 @@ static NvU64 small_half_pde_pascal(uvm_mmu_page_table_alloc_t *phys_alloc)
|
||||
return pde_bits;
|
||||
}
|
||||
|
||||
static void make_pde_pascal(void *entry,
|
||||
uvm_mmu_page_table_alloc_t **phys_allocs,
|
||||
NvU32 depth,
|
||||
uvm_page_directory_t *child_dir)
|
||||
static void make_pde_pascal(void *entry, uvm_mmu_page_table_alloc_t **phys_allocs, NvU32 depth)
|
||||
{
|
||||
NvU32 entry_count = entries_per_index_pascal(depth);
|
||||
NvU64 *entry_bits = (NvU64 *)entry;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2016-2019 NVIDIA Corporation
|
||||
Copyright (c) 2016-2023 NVIDIA Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
@ -22,10 +22,7 @@
|
||||
*******************************************************************************/
|
||||
|
||||
#include "uvm_perf_events.h"
|
||||
#include "uvm_va_block.h"
|
||||
#include "uvm_va_range.h"
|
||||
#include "uvm_va_space.h"
|
||||
#include "uvm_kvmalloc.h"
|
||||
#include "uvm_test.h"
|
||||
|
||||
// Global variable used to check that callbacks are correctly executed
|
||||
@ -46,10 +43,7 @@ static NV_STATUS test_events(uvm_va_space_t *va_space)
|
||||
NV_STATUS status;
|
||||
uvm_perf_event_data_t event_data;
|
||||
|
||||
uvm_va_block_t block;
|
||||
|
||||
test_data = 0;
|
||||
|
||||
memset(&event_data, 0, sizeof(event_data));
|
||||
|
||||
// Use CPU id to avoid triggering the GPU stats update code
|
||||
@ -58,6 +52,7 @@ static NV_STATUS test_events(uvm_va_space_t *va_space)
|
||||
// Register a callback for page fault
|
||||
status = uvm_perf_register_event_callback(&va_space->perf_events, UVM_PERF_EVENT_FAULT, callback_inc_1);
|
||||
TEST_CHECK_GOTO(status == NV_OK, done);
|
||||
|
||||
// Register a callback for page fault
|
||||
status = uvm_perf_register_event_callback(&va_space->perf_events, UVM_PERF_EVENT_FAULT, callback_inc_2);
|
||||
TEST_CHECK_GOTO(status == NV_OK, done);
|
||||
@ -65,13 +60,14 @@ static NV_STATUS test_events(uvm_va_space_t *va_space)
|
||||
// va_space read lock is required for page fault event notification
|
||||
uvm_va_space_down_read(va_space);
|
||||
|
||||
// Notify (fake) page fault. The two registered callbacks for this event increment the value of test_value
|
||||
event_data.fault.block = █
|
||||
// Notify (fake) page fault. The two registered callbacks for this event
|
||||
// increment the value of test_value
|
||||
uvm_perf_event_notify(&va_space->perf_events, UVM_PERF_EVENT_FAULT, &event_data);
|
||||
|
||||
uvm_va_space_up_read(va_space);
|
||||
|
||||
// test_data was initialized to zero. It should have been incremented by 1 and 2, respectively in the callbacks
|
||||
// test_data was initialized to zero. It should have been incremented by 1
|
||||
// and 2, respectively in the callbacks
|
||||
TEST_CHECK_GOTO(test_data == 3, done);
|
||||
|
||||
done:
|
||||
@ -96,4 +92,3 @@ NV_STATUS uvm_test_perf_events_sanity(UVM_TEST_PERF_EVENTS_SANITY_PARAMS *params
|
||||
done:
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -355,7 +355,7 @@ static NvU32 uvm_perf_prefetch_prenotify_fault_migrations(uvm_va_block_t *va_blo
|
||||
uvm_page_mask_zero(prefetch_pages);
|
||||
|
||||
if (UVM_ID_IS_CPU(new_residency) || va_block->gpus[uvm_id_gpu_index(new_residency)] != NULL)
|
||||
resident_mask = uvm_va_block_resident_mask_get(va_block, new_residency);
|
||||
resident_mask = uvm_va_block_resident_mask_get(va_block, new_residency, NUMA_NO_NODE);
|
||||
|
||||
// If this is a first-touch fault and the destination processor is the
|
||||
// preferred location, populate the whole max_prefetch_region.
|
||||
|
@ -164,7 +164,7 @@ typedef struct
|
||||
|
||||
uvm_spinlock_t lock;
|
||||
|
||||
uvm_va_block_context_t va_block_context;
|
||||
uvm_va_block_context_t *va_block_context;
|
||||
|
||||
// Flag used to avoid scheduling delayed unpinning operations after
|
||||
// uvm_perf_thrashing_stop has been called.
|
||||
@ -601,6 +601,14 @@ static va_space_thrashing_info_t *va_space_thrashing_info_create(uvm_va_space_t
|
||||
|
||||
va_space_thrashing = uvm_kvmalloc_zero(sizeof(*va_space_thrashing));
|
||||
if (va_space_thrashing) {
|
||||
uvm_va_block_context_t *block_context = uvm_va_block_context_alloc(NULL);
|
||||
|
||||
if (!block_context) {
|
||||
uvm_kvfree(va_space_thrashing);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
va_space_thrashing->pinned_pages.va_block_context = block_context;
|
||||
va_space_thrashing->va_space = va_space;
|
||||
|
||||
va_space_thrashing_info_init_params(va_space_thrashing);
|
||||
@ -621,6 +629,7 @@ static void va_space_thrashing_info_destroy(uvm_va_space_t *va_space)
|
||||
|
||||
if (va_space_thrashing) {
|
||||
uvm_perf_module_type_unset_data(va_space->perf_modules_data, UVM_PERF_MODULE_TYPE_THRASHING);
|
||||
uvm_va_block_context_free(va_space_thrashing->pinned_pages.va_block_context);
|
||||
uvm_kvfree(va_space_thrashing);
|
||||
}
|
||||
}
|
||||
@ -1104,7 +1113,7 @@ static NV_STATUS unmap_remote_pinned_pages(uvm_va_block_t *va_block,
|
||||
!uvm_processor_mask_test(&policy->accessed_by, processor_id));
|
||||
|
||||
if (uvm_processor_mask_test(&va_block->resident, processor_id)) {
|
||||
const uvm_page_mask_t *resident_mask = uvm_va_block_resident_mask_get(va_block, processor_id);
|
||||
const uvm_page_mask_t *resident_mask = uvm_va_block_resident_mask_get(va_block, processor_id, NUMA_NO_NODE);
|
||||
|
||||
if (!uvm_page_mask_andnot(&va_block_context->caller_page_mask,
|
||||
&block_thrashing->pinned_pages.mask,
|
||||
@ -1312,9 +1321,8 @@ void thrashing_event_cb(uvm_perf_event_t event_id, uvm_perf_event_data_t *event_
|
||||
|
||||
if (block_thrashing->last_time_stamp == 0 ||
|
||||
uvm_id_equal(block_thrashing->last_processor, processor_id) ||
|
||||
time_stamp - block_thrashing->last_time_stamp > va_space_thrashing->params.lapse_ns) {
|
||||
time_stamp - block_thrashing->last_time_stamp > va_space_thrashing->params.lapse_ns)
|
||||
goto done;
|
||||
}
|
||||
|
||||
num_block_pages = uvm_va_block_size(va_block) / PAGE_SIZE;
|
||||
|
||||
@ -1803,7 +1811,7 @@ static void thrashing_unpin_pages(struct work_struct *work)
|
||||
struct delayed_work *dwork = to_delayed_work(work);
|
||||
va_space_thrashing_info_t *va_space_thrashing = container_of(dwork, va_space_thrashing_info_t, pinned_pages.dwork);
|
||||
uvm_va_space_t *va_space = va_space_thrashing->va_space;
|
||||
uvm_va_block_context_t *va_block_context = &va_space_thrashing->pinned_pages.va_block_context;
|
||||
uvm_va_block_context_t *va_block_context = va_space_thrashing->pinned_pages.va_block_context;
|
||||
|
||||
// Take the VA space lock so that VA blocks don't go away during this
|
||||
// operation.
|
||||
@ -1937,7 +1945,6 @@ void uvm_perf_thrashing_unload(uvm_va_space_t *va_space)
|
||||
|
||||
// Make sure that there are not pending work items
|
||||
if (va_space_thrashing) {
|
||||
UVM_ASSERT(va_space_thrashing->pinned_pages.in_va_space_teardown);
|
||||
UVM_ASSERT(list_empty(&va_space_thrashing->pinned_pages.list));
|
||||
|
||||
va_space_thrashing_info_destroy(va_space);
|
||||
|
@ -3377,76 +3377,47 @@ uvm_gpu_id_t uvm_pmm_devmem_page_to_gpu_id(struct page *page)
|
||||
return gpu->id;
|
||||
}
|
||||
|
||||
static void evict_orphan_pages(uvm_pmm_gpu_t *pmm, uvm_gpu_chunk_t *chunk)
|
||||
{
|
||||
NvU32 i;
|
||||
|
||||
UVM_ASSERT(chunk->state == UVM_PMM_GPU_CHUNK_STATE_IS_SPLIT);
|
||||
UVM_ASSERT(chunk->suballoc);
|
||||
|
||||
for (i = 0; i < num_subchunks(chunk); i++) {
|
||||
uvm_gpu_chunk_t *subchunk = chunk->suballoc->subchunks[i];
|
||||
|
||||
uvm_spin_lock(&pmm->list_lock);
|
||||
|
||||
if (subchunk->state == UVM_PMM_GPU_CHUNK_STATE_IS_SPLIT) {
|
||||
uvm_spin_unlock(&pmm->list_lock);
|
||||
|
||||
evict_orphan_pages(pmm, subchunk);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (subchunk->state == UVM_PMM_GPU_CHUNK_STATE_ALLOCATED && subchunk->is_referenced) {
|
||||
unsigned long pfn = uvm_pmm_gpu_devmem_get_pfn(pmm, subchunk);
|
||||
|
||||
// TODO: Bug 3368756: add support for large GPU pages.
|
||||
UVM_ASSERT(uvm_gpu_chunk_get_size(subchunk) == PAGE_SIZE);
|
||||
uvm_spin_unlock(&pmm->list_lock);
|
||||
|
||||
// The above check for subchunk state is racy because the
|
||||
// chunk may be freed after the lock is dropped. It is
|
||||
// still safe to proceed in that case because the struct
|
||||
// page reference will have dropped to zero and cannot
|
||||
// have been re-allocated as this is only called during
|
||||
// GPU teardown. Therefore migrate_device_range() will
|
||||
// simply fail.
|
||||
uvm_hmm_pmm_gpu_evict_pfn(pfn);
|
||||
continue;
|
||||
}
|
||||
|
||||
uvm_spin_unlock(&pmm->list_lock);
|
||||
}
|
||||
}
|
||||
|
||||
// Free any orphan pages.
|
||||
// This should be called as part of removing a GPU: after all work is stopped
|
||||
// and all va_blocks have been destroyed. There normally won't be any
|
||||
// device private struct page references left but there can be cases after
|
||||
// fork() where a child process still holds a reference. This function searches
|
||||
// for pages that still have a reference and migrates the page to the GPU in
|
||||
// order to release the reference in the CPU page table.
|
||||
static void uvm_pmm_gpu_free_orphan_pages(uvm_pmm_gpu_t *pmm)
|
||||
// Check there are no orphan pages. This should be only called as part of
|
||||
// removing a GPU: after all work is stopped and all va_blocks have been
|
||||
// destroyed. By now there should be no device-private page references left as
|
||||
// there are no va_space's left on this GPU and orphan pages should be removed
|
||||
// by va_space destruction or unregistration from the GPU.
|
||||
static bool uvm_pmm_gpu_check_orphan_pages(uvm_pmm_gpu_t *pmm)
|
||||
{
|
||||
size_t i;
|
||||
bool ret = true;
|
||||
unsigned long pfn;
|
||||
struct range range = pmm->devmem.pagemap.range;
|
||||
|
||||
if (!pmm->initialized)
|
||||
return;
|
||||
|
||||
// This is only safe to call during GPU teardown where chunks
|
||||
// cannot be re-allocated.
|
||||
UVM_ASSERT(uvm_gpu_retained_count(uvm_pmm_to_gpu(pmm)) == 0);
|
||||
if (!pmm->initialized || !uvm_hmm_is_enabled_system_wide())
|
||||
return ret;
|
||||
|
||||
// Scan all the root chunks looking for subchunks which are still
|
||||
// referenced. This is slow, but we only do this when unregistering a GPU
|
||||
// and is not critical for performance.
|
||||
// referenced.
|
||||
for (i = 0; i < pmm->root_chunks.count; i++) {
|
||||
uvm_gpu_root_chunk_t *root_chunk = &pmm->root_chunks.array[i];
|
||||
|
||||
root_chunk_lock(pmm, root_chunk);
|
||||
if (root_chunk->chunk.state == UVM_PMM_GPU_CHUNK_STATE_IS_SPLIT)
|
||||
evict_orphan_pages(pmm, &root_chunk->chunk);
|
||||
ret = false;
|
||||
root_chunk_unlock(pmm, root_chunk);
|
||||
}
|
||||
|
||||
for (pfn = __phys_to_pfn(range.start); pfn <= __phys_to_pfn(range.end); pfn++) {
|
||||
struct page *page = pfn_to_page(pfn);
|
||||
|
||||
if (!is_device_private_page(page)) {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (page_count(page)) {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void devmem_page_free(struct page *page)
|
||||
@ -3479,7 +3450,7 @@ static vm_fault_t devmem_fault(struct vm_fault *vmf)
|
||||
{
|
||||
uvm_va_space_t *va_space = vmf->page->zone_device_data;
|
||||
|
||||
if (!va_space || va_space->va_space_mm.mm != vmf->vma->vm_mm)
|
||||
if (!va_space)
|
||||
return VM_FAULT_SIGBUS;
|
||||
|
||||
return uvm_va_space_cpu_fault_hmm(va_space, vmf->vma, vmf);
|
||||
@ -3568,8 +3539,9 @@ static void devmem_deinit(uvm_pmm_gpu_t *pmm)
|
||||
{
|
||||
}
|
||||
|
||||
static void uvm_pmm_gpu_free_orphan_pages(uvm_pmm_gpu_t *pmm)
|
||||
static bool uvm_pmm_gpu_check_orphan_pages(uvm_pmm_gpu_t *pmm)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif // UVM_IS_CONFIG_HMM()
|
||||
|
||||
@ -3744,7 +3716,7 @@ void uvm_pmm_gpu_deinit(uvm_pmm_gpu_t *pmm)
|
||||
|
||||
gpu = uvm_pmm_to_gpu(pmm);
|
||||
|
||||
uvm_pmm_gpu_free_orphan_pages(pmm);
|
||||
UVM_ASSERT(uvm_pmm_gpu_check_orphan_pages(pmm));
|
||||
nv_kthread_q_flush(&gpu->parent->lazy_free_q);
|
||||
UVM_ASSERT(list_empty(&pmm->root_chunks.va_block_lazy_free));
|
||||
release_free_root_chunks(pmm);
|
||||
|
@ -749,6 +749,7 @@ NV_STATUS uvm_cpu_chunk_map_gpu(uvm_cpu_chunk_t *chunk, uvm_gpu_t *gpu)
|
||||
}
|
||||
|
||||
static struct page *uvm_cpu_chunk_alloc_page(uvm_chunk_size_t alloc_size,
|
||||
int nid,
|
||||
uvm_cpu_chunk_alloc_flags_t alloc_flags)
|
||||
{
|
||||
gfp_t kernel_alloc_flags;
|
||||
@ -764,18 +765,27 @@ static struct page *uvm_cpu_chunk_alloc_page(uvm_chunk_size_t alloc_size,
|
||||
|
||||
kernel_alloc_flags |= GFP_HIGHUSER;
|
||||
|
||||
// For allocation sizes higher than PAGE_SIZE, use __GFP_NORETRY in
|
||||
// order to avoid higher allocation latency from the kernel compacting
|
||||
// memory to satisfy the request.
|
||||
// For allocation sizes higher than PAGE_SIZE, use __GFP_NORETRY in order
|
||||
// to avoid higher allocation latency from the kernel compacting memory to
|
||||
// satisfy the request.
|
||||
// Use __GFP_NOWARN to avoid printing allocation failure to the kernel log.
|
||||
// High order allocation failures are handled gracefully by the caller.
|
||||
if (alloc_size > PAGE_SIZE)
|
||||
kernel_alloc_flags |= __GFP_COMP | __GFP_NORETRY;
|
||||
kernel_alloc_flags |= __GFP_COMP | __GFP_NORETRY | __GFP_NOWARN;
|
||||
|
||||
if (alloc_flags & UVM_CPU_CHUNK_ALLOC_FLAGS_ZERO)
|
||||
kernel_alloc_flags |= __GFP_ZERO;
|
||||
|
||||
page = alloc_pages(kernel_alloc_flags, get_order(alloc_size));
|
||||
if (page && (alloc_flags & UVM_CPU_CHUNK_ALLOC_FLAGS_ZERO))
|
||||
SetPageDirty(page);
|
||||
UVM_ASSERT(nid < num_online_nodes());
|
||||
if (nid == NUMA_NO_NODE)
|
||||
page = alloc_pages(kernel_alloc_flags, get_order(alloc_size));
|
||||
else
|
||||
page = alloc_pages_node(nid, kernel_alloc_flags, get_order(alloc_size));
|
||||
|
||||
if (page) {
|
||||
if (alloc_flags & UVM_CPU_CHUNK_ALLOC_FLAGS_ZERO)
|
||||
SetPageDirty(page);
|
||||
}
|
||||
|
||||
return page;
|
||||
}
|
||||
@ -805,6 +815,7 @@ static uvm_cpu_physical_chunk_t *uvm_cpu_chunk_create(uvm_chunk_size_t alloc_siz
|
||||
|
||||
NV_STATUS uvm_cpu_chunk_alloc(uvm_chunk_size_t alloc_size,
|
||||
uvm_cpu_chunk_alloc_flags_t alloc_flags,
|
||||
int nid,
|
||||
uvm_cpu_chunk_t **new_chunk)
|
||||
{
|
||||
uvm_cpu_physical_chunk_t *chunk;
|
||||
@ -812,7 +823,7 @@ NV_STATUS uvm_cpu_chunk_alloc(uvm_chunk_size_t alloc_size,
|
||||
|
||||
UVM_ASSERT(new_chunk);
|
||||
|
||||
page = uvm_cpu_chunk_alloc_page(alloc_size, alloc_flags);
|
||||
page = uvm_cpu_chunk_alloc_page(alloc_size, nid, alloc_flags);
|
||||
if (!page)
|
||||
return NV_ERR_NO_MEMORY;
|
||||
|
||||
@ -847,6 +858,13 @@ NV_STATUS uvm_cpu_chunk_alloc_hmm(struct page *page,
|
||||
return NV_OK;
|
||||
}
|
||||
|
||||
int uvm_cpu_chunk_get_numa_node(uvm_cpu_chunk_t *chunk)
|
||||
{
|
||||
UVM_ASSERT(chunk);
|
||||
UVM_ASSERT(chunk->page);
|
||||
return page_to_nid(chunk->page);
|
||||
}
|
||||
|
||||
NV_STATUS uvm_cpu_chunk_split(uvm_cpu_chunk_t *chunk, uvm_cpu_chunk_t **new_chunks)
|
||||
{
|
||||
NV_STATUS status = NV_OK;
|
||||
|
@ -304,11 +304,24 @@ uvm_chunk_sizes_mask_t uvm_cpu_chunk_get_allocation_sizes(void);
|
||||
|
||||
// Allocate a physical CPU chunk of the specified size.
|
||||
//
|
||||
// The nid argument is used to indicate a memory node preference. If the
|
||||
// value is a memory node ID, the chunk allocation will be attempted on
|
||||
// that memory node. If the chunk cannot be allocated on that memory node,
|
||||
// it will be allocated on any memory node allowed by the process's policy.
|
||||
//
|
||||
// If the value of nid is a memory node ID that is not in the set of
|
||||
// current process's allowed memory nodes, it will be allocated on one of the
|
||||
// nodes in the allowed set.
|
||||
//
|
||||
// If the value of nid is NUMA_NO_NODE, the chunk will be allocated from any
|
||||
// of the allowed memory nodes by the process policy.
|
||||
//
|
||||
// If a CPU chunk allocation succeeds, NV_OK is returned. new_chunk will be set
|
||||
// to point to the newly allocated chunk. On failure, NV_ERR_NO_MEMORY is
|
||||
// returned.
|
||||
NV_STATUS uvm_cpu_chunk_alloc(uvm_chunk_size_t alloc_size,
|
||||
uvm_cpu_chunk_alloc_flags_t flags,
|
||||
int nid,
|
||||
uvm_cpu_chunk_t **new_chunk);
|
||||
|
||||
// Allocate a HMM CPU chunk.
|
||||
@ -375,6 +388,9 @@ static uvm_cpu_logical_chunk_t *uvm_cpu_chunk_to_logical(uvm_cpu_chunk_t *chunk)
|
||||
return container_of((chunk), uvm_cpu_logical_chunk_t, common);
|
||||
}
|
||||
|
||||
// Return the NUMA node ID of the physical page backing the chunk.
|
||||
int uvm_cpu_chunk_get_numa_node(uvm_cpu_chunk_t *chunk);
|
||||
|
||||
// Free a CPU chunk.
|
||||
// This may not result in the immediate freeing of the physical pages of the
|
||||
// chunk if this is a logical chunk and there are other logical chunks holding
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2017-2019 NVIDIA Corporation
|
||||
Copyright (c) 2017-2023 NVIDIA Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
@ -664,6 +664,7 @@ done:
|
||||
|
||||
static NV_STATUS test_cpu_chunk_alloc(uvm_chunk_size_t size,
|
||||
uvm_cpu_chunk_alloc_flags_t flags,
|
||||
int nid,
|
||||
uvm_cpu_chunk_t **out_chunk)
|
||||
{
|
||||
uvm_cpu_chunk_t *chunk;
|
||||
@ -675,7 +676,7 @@ static NV_STATUS test_cpu_chunk_alloc(uvm_chunk_size_t size,
|
||||
// It is possible that the allocation fails due to lack of large pages
|
||||
// rather than an API issue, which will result in a false negative.
|
||||
// However, that should be very rare.
|
||||
TEST_NV_CHECK_RET(uvm_cpu_chunk_alloc(size, flags, &chunk));
|
||||
TEST_NV_CHECK_RET(uvm_cpu_chunk_alloc(size, flags, nid, &chunk));
|
||||
|
||||
// Check general state of the chunk:
|
||||
// - chunk should be a physical chunk,
|
||||
@ -685,6 +686,12 @@ static NV_STATUS test_cpu_chunk_alloc(uvm_chunk_size_t size,
|
||||
TEST_CHECK_GOTO(uvm_cpu_chunk_get_size(chunk) == size, done);
|
||||
TEST_CHECK_GOTO(uvm_cpu_chunk_num_pages(chunk) == size / PAGE_SIZE, done);
|
||||
|
||||
// It is possible for the kernel to allocate a chunk on a NUMA node other
|
||||
// than the one requested. However, that should not be an issue with
|
||||
// sufficient memory on each NUMA node.
|
||||
if (nid != NUMA_NO_NODE)
|
||||
TEST_CHECK_GOTO(uvm_cpu_chunk_get_numa_node(chunk) == nid, done);
|
||||
|
||||
if (flags & UVM_CPU_CHUNK_ALLOC_FLAGS_ZERO) {
|
||||
NvU64 *cpu_addr;
|
||||
|
||||
@ -719,7 +726,7 @@ static NV_STATUS test_cpu_chunk_mapping_basic_verify(uvm_gpu_t *gpu,
|
||||
NvU64 dma_addr;
|
||||
NV_STATUS status = NV_OK;
|
||||
|
||||
TEST_NV_CHECK_RET(test_cpu_chunk_alloc(size, flags, &chunk));
|
||||
TEST_NV_CHECK_RET(test_cpu_chunk_alloc(size, flags, NUMA_NO_NODE, &chunk));
|
||||
phys_chunk = uvm_cpu_chunk_to_physical(chunk);
|
||||
|
||||
// Check state of the physical chunk:
|
||||
@ -763,27 +770,27 @@ static NV_STATUS test_cpu_chunk_mapping_basic(uvm_gpu_t *gpu, uvm_cpu_chunk_allo
|
||||
return NV_OK;
|
||||
}
|
||||
|
||||
static NV_STATUS test_cpu_chunk_mapping_array(uvm_gpu_t *gpu1, uvm_gpu_t *gpu2, uvm_gpu_t *gpu3)
|
||||
static NV_STATUS test_cpu_chunk_mapping_array(uvm_gpu_t *gpu0, uvm_gpu_t *gpu1, uvm_gpu_t *gpu2)
|
||||
{
|
||||
NV_STATUS status = NV_OK;
|
||||
uvm_cpu_chunk_t *chunk;
|
||||
uvm_cpu_physical_chunk_t *phys_chunk;
|
||||
NvU64 dma_addr_gpu2;
|
||||
NvU64 dma_addr_gpu1;
|
||||
|
||||
TEST_NV_CHECK_RET(test_cpu_chunk_alloc(PAGE_SIZE, UVM_CPU_CHUNK_ALLOC_FLAGS_NONE, &chunk));
|
||||
TEST_NV_CHECK_RET(test_cpu_chunk_alloc(PAGE_SIZE, UVM_CPU_CHUNK_ALLOC_FLAGS_NONE, NUMA_NO_NODE, &chunk));
|
||||
phys_chunk = uvm_cpu_chunk_to_physical(chunk);
|
||||
|
||||
TEST_NV_CHECK_GOTO(uvm_cpu_chunk_map_gpu(chunk, gpu2), done);
|
||||
TEST_NV_CHECK_GOTO(test_cpu_chunk_mapping_access(chunk, gpu2), done);
|
||||
TEST_NV_CHECK_GOTO(uvm_cpu_chunk_map_gpu(chunk, gpu3), done);
|
||||
TEST_NV_CHECK_GOTO(test_cpu_chunk_mapping_access(chunk, gpu2), done);
|
||||
TEST_NV_CHECK_GOTO(test_cpu_chunk_mapping_access(chunk, gpu3), done);
|
||||
dma_addr_gpu2 = uvm_cpu_chunk_get_gpu_phys_addr(chunk, gpu2->parent);
|
||||
uvm_cpu_chunk_unmap_gpu_phys(chunk, gpu3->parent);
|
||||
TEST_NV_CHECK_GOTO(test_cpu_chunk_mapping_access(chunk, gpu2), done);
|
||||
TEST_NV_CHECK_GOTO(uvm_cpu_chunk_map_gpu(chunk, gpu1), done);
|
||||
TEST_NV_CHECK_GOTO(test_cpu_chunk_mapping_access(chunk, gpu1), done);
|
||||
TEST_NV_CHECK_GOTO(uvm_cpu_chunk_map_gpu(chunk, gpu2), done);
|
||||
TEST_NV_CHECK_GOTO(test_cpu_chunk_mapping_access(chunk, gpu1), done);
|
||||
TEST_NV_CHECK_GOTO(test_cpu_chunk_mapping_access(chunk, gpu2), done);
|
||||
dma_addr_gpu1 = uvm_cpu_chunk_get_gpu_phys_addr(chunk, gpu1->parent);
|
||||
uvm_cpu_chunk_unmap_gpu_phys(chunk, gpu2->parent);
|
||||
TEST_NV_CHECK_GOTO(test_cpu_chunk_mapping_access(chunk, gpu1), done);
|
||||
TEST_NV_CHECK_GOTO(uvm_cpu_chunk_map_gpu(chunk, gpu0), done);
|
||||
TEST_NV_CHECK_GOTO(test_cpu_chunk_mapping_access(chunk, gpu0), done);
|
||||
TEST_NV_CHECK_GOTO(test_cpu_chunk_mapping_access(chunk, gpu1), done);
|
||||
|
||||
// DMA mapping addresses for different GPUs live in different IOMMU spaces,
|
||||
// so it would be perfectly legal for them to have the same IOVA, and even
|
||||
@ -793,7 +800,7 @@ static NV_STATUS test_cpu_chunk_mapping_array(uvm_gpu_t *gpu1, uvm_gpu_t *gpu2,
|
||||
// GPU1. It's true that we may get a false negative if both addresses
|
||||
// happened to alias and we had a bug in how the addresses are shifted in
|
||||
// the dense array, but that's better than intermittent failure.
|
||||
TEST_CHECK_GOTO(uvm_cpu_chunk_get_gpu_phys_addr(chunk, gpu2->parent) == dma_addr_gpu2, done);
|
||||
TEST_CHECK_GOTO(uvm_cpu_chunk_get_gpu_phys_addr(chunk, gpu1->parent) == dma_addr_gpu1, done);
|
||||
|
||||
done:
|
||||
uvm_cpu_chunk_free(chunk);
|
||||
@ -911,7 +918,7 @@ static NV_STATUS test_cpu_chunk_split_and_merge(uvm_gpu_t *gpu)
|
||||
uvm_cpu_chunk_t *chunk;
|
||||
NV_STATUS status;
|
||||
|
||||
TEST_NV_CHECK_RET(test_cpu_chunk_alloc(size, UVM_CPU_CHUNK_ALLOC_FLAGS_NONE, &chunk));
|
||||
TEST_NV_CHECK_RET(test_cpu_chunk_alloc(size, UVM_CPU_CHUNK_ALLOC_FLAGS_NONE, NUMA_NO_NODE, &chunk));
|
||||
status = do_test_cpu_chunk_split_and_merge(chunk, gpu);
|
||||
uvm_cpu_chunk_free(chunk);
|
||||
|
||||
@ -993,7 +1000,7 @@ static NV_STATUS test_cpu_chunk_dirty(uvm_gpu_t *gpu)
|
||||
uvm_cpu_physical_chunk_t *phys_chunk;
|
||||
size_t num_pages;
|
||||
|
||||
TEST_NV_CHECK_RET(test_cpu_chunk_alloc(size, UVM_CPU_CHUNK_ALLOC_FLAGS_NONE, &chunk));
|
||||
TEST_NV_CHECK_RET(test_cpu_chunk_alloc(size, UVM_CPU_CHUNK_ALLOC_FLAGS_NONE, NUMA_NO_NODE, &chunk));
|
||||
phys_chunk = uvm_cpu_chunk_to_physical(chunk);
|
||||
num_pages = uvm_cpu_chunk_num_pages(chunk);
|
||||
|
||||
@ -1005,7 +1012,7 @@ static NV_STATUS test_cpu_chunk_dirty(uvm_gpu_t *gpu)
|
||||
|
||||
uvm_cpu_chunk_free(chunk);
|
||||
|
||||
TEST_NV_CHECK_RET(test_cpu_chunk_alloc(size, UVM_CPU_CHUNK_ALLOC_FLAGS_ZERO, &chunk));
|
||||
TEST_NV_CHECK_RET(test_cpu_chunk_alloc(size, UVM_CPU_CHUNK_ALLOC_FLAGS_ZERO, NUMA_NO_NODE, &chunk));
|
||||
phys_chunk = uvm_cpu_chunk_to_physical(chunk);
|
||||
num_pages = uvm_cpu_chunk_num_pages(chunk);
|
||||
|
||||
@ -1170,13 +1177,35 @@ NV_STATUS test_cpu_chunk_free(uvm_va_space_t *va_space, uvm_processor_mask_t *te
|
||||
size_t size = uvm_chunk_find_next_size(alloc_sizes, PAGE_SIZE);
|
||||
|
||||
for_each_chunk_size_from(size, alloc_sizes) {
|
||||
TEST_NV_CHECK_RET(test_cpu_chunk_alloc(size, UVM_CPU_CHUNK_ALLOC_FLAGS_NONE, &chunk));
|
||||
TEST_NV_CHECK_RET(test_cpu_chunk_alloc(size, UVM_CPU_CHUNK_ALLOC_FLAGS_NONE, NUMA_NO_NODE, &chunk));
|
||||
TEST_NV_CHECK_RET(do_test_cpu_chunk_free(chunk, va_space, test_gpus));
|
||||
}
|
||||
|
||||
return NV_OK;
|
||||
}
|
||||
|
||||
static NV_STATUS test_cpu_chunk_numa_alloc(uvm_va_space_t *va_space)
|
||||
{
|
||||
uvm_cpu_chunk_t *chunk;
|
||||
uvm_chunk_sizes_mask_t alloc_sizes = uvm_cpu_chunk_get_allocation_sizes();
|
||||
size_t size;
|
||||
|
||||
for_each_chunk_size(size, alloc_sizes) {
|
||||
int nid;
|
||||
|
||||
for_each_possible_uvm_node(nid) {
|
||||
// Do not test CPU allocation on nodes that have no memory or CPU
|
||||
if (!node_state(nid, N_MEMORY) || !node_state(nid, N_CPU))
|
||||
continue;
|
||||
|
||||
TEST_NV_CHECK_RET(test_cpu_chunk_alloc(size, UVM_CPU_CHUNK_ALLOC_FLAGS_NONE, nid, &chunk));
|
||||
uvm_cpu_chunk_free(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
return NV_OK;
|
||||
}
|
||||
|
||||
NV_STATUS uvm_test_cpu_chunk_api(UVM_TEST_CPU_CHUNK_API_PARAMS *params, struct file *filp)
|
||||
{
|
||||
uvm_va_space_t *va_space = uvm_va_space_get(filp);
|
||||
@ -1197,6 +1226,7 @@ NV_STATUS uvm_test_cpu_chunk_api(UVM_TEST_CPU_CHUNK_API_PARAMS *params, struct f
|
||||
}
|
||||
|
||||
TEST_NV_CHECK_GOTO(test_cpu_chunk_free(va_space, &test_gpus), done);
|
||||
TEST_NV_CHECK_GOTO(test_cpu_chunk_numa_alloc(va_space), done);
|
||||
|
||||
if (uvm_processor_mask_get_gpu_count(&test_gpus) >= 3) {
|
||||
uvm_gpu_t *gpu2, *gpu3;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2015-2023 NVIDIA Corporation
|
||||
Copyright (c) 2015-2022 NVIDIA Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
@ -324,7 +324,7 @@ static NV_STATUS gpu_mem_check(uvm_gpu_t *gpu,
|
||||
|
||||
// TODO: Bug 3839176: [UVM][HCC][uvm_test] Update tests that assume GPU
|
||||
// engines can directly access sysmem
|
||||
// Skip this test for now. To enable this test in Confidential Computing,
|
||||
// Skip this test for now. To enable this test under SEV,
|
||||
// The GPU->CPU CE copy needs to be updated so it uses encryption when
|
||||
// CC is enabled.
|
||||
if (uvm_conf_computing_mode_enabled(gpu))
|
||||
@ -1068,7 +1068,7 @@ static NV_STATUS test_pmm_reverse_map_single(uvm_gpu_t *gpu, uvm_va_space_t *va_
|
||||
uvm_mutex_lock(&va_block->lock);
|
||||
|
||||
is_resident = uvm_processor_mask_test(&va_block->resident, gpu->id) &&
|
||||
uvm_page_mask_full(uvm_va_block_resident_mask_get(va_block, gpu->id));
|
||||
uvm_page_mask_full(uvm_va_block_resident_mask_get(va_block, gpu->id, NUMA_NO_NODE));
|
||||
if (is_resident)
|
||||
phys_addr = uvm_va_block_gpu_phys_page_address(va_block, 0, gpu);
|
||||
|
||||
@ -1154,7 +1154,7 @@ static NV_STATUS test_pmm_reverse_map_many_blocks(uvm_gpu_t *gpu, uvm_va_space_t
|
||||
uvm_mutex_lock(&va_block->lock);
|
||||
|
||||
// Verify that all pages are populated on the GPU
|
||||
is_resident = uvm_page_mask_region_full(uvm_va_block_resident_mask_get(va_block, gpu->id),
|
||||
is_resident = uvm_page_mask_region_full(uvm_va_block_resident_mask_get(va_block, gpu->id, NUMA_NO_NODE),
|
||||
reverse_mapping->region);
|
||||
|
||||
uvm_mutex_unlock(&va_block->lock);
|
||||
@ -1223,6 +1223,8 @@ static NV_STATUS test_indirect_peers(uvm_gpu_t *owning_gpu, uvm_gpu_t *accessing
|
||||
if (!chunks)
|
||||
return NV_ERR_NO_MEMORY;
|
||||
|
||||
UVM_ASSERT(!g_uvm_global.sev_enabled);
|
||||
|
||||
TEST_NV_CHECK_GOTO(uvm_mem_alloc_sysmem_and_map_cpu_kernel(UVM_CHUNK_SIZE_MAX, current->mm, &verif_mem), out);
|
||||
TEST_NV_CHECK_GOTO(uvm_mem_map_gpu_kernel(verif_mem, owning_gpu), out);
|
||||
TEST_NV_CHECK_GOTO(uvm_mem_map_gpu_kernel(verif_mem, accessing_gpu), out);
|
||||
|
@ -176,7 +176,9 @@ static NV_STATUS preferred_location_unmap_remote_pages(uvm_va_block_t *va_block,
|
||||
mapped_mask = uvm_va_block_map_mask_get(va_block, preferred_location);
|
||||
|
||||
if (uvm_processor_mask_test(&va_block->resident, preferred_location)) {
|
||||
const uvm_page_mask_t *resident_mask = uvm_va_block_resident_mask_get(va_block, preferred_location);
|
||||
const uvm_page_mask_t *resident_mask = uvm_va_block_resident_mask_get(va_block,
|
||||
preferred_location,
|
||||
NUMA_NO_NODE);
|
||||
|
||||
if (!uvm_page_mask_andnot(&va_block_context->caller_page_mask, mapped_mask, resident_mask))
|
||||
goto done;
|
||||
@ -638,7 +640,7 @@ static NV_STATUS va_block_set_read_duplication_locked(uvm_va_block_t *va_block,
|
||||
|
||||
for_each_id_in_mask(src_id, &va_block->resident) {
|
||||
NV_STATUS status;
|
||||
uvm_page_mask_t *resident_mask = uvm_va_block_resident_mask_get(va_block, src_id);
|
||||
uvm_page_mask_t *resident_mask = uvm_va_block_resident_mask_get(va_block, src_id, NUMA_NO_NODE);
|
||||
|
||||
// Calling uvm_va_block_make_resident_read_duplicate will break all
|
||||
// SetAccessedBy and remote mappings
|
||||
@ -695,7 +697,7 @@ static NV_STATUS va_block_unset_read_duplication_locked(uvm_va_block_t *va_block
|
||||
// If preferred_location is set and has resident copies, give it preference
|
||||
if (UVM_ID_IS_VALID(preferred_location) &&
|
||||
uvm_processor_mask_test(&va_block->resident, preferred_location)) {
|
||||
uvm_page_mask_t *resident_mask = uvm_va_block_resident_mask_get(va_block, preferred_location);
|
||||
uvm_page_mask_t *resident_mask = uvm_va_block_resident_mask_get(va_block, preferred_location, NUMA_NO_NODE);
|
||||
bool is_mask_empty = !uvm_page_mask_and(break_read_duplication_pages,
|
||||
&va_block->read_duplicated_pages,
|
||||
resident_mask);
|
||||
@ -723,7 +725,7 @@ static NV_STATUS va_block_unset_read_duplication_locked(uvm_va_block_t *va_block
|
||||
if (uvm_id_equal(processor_id, preferred_location))
|
||||
continue;
|
||||
|
||||
resident_mask = uvm_va_block_resident_mask_get(va_block, processor_id);
|
||||
resident_mask = uvm_va_block_resident_mask_get(va_block, processor_id, NUMA_NO_NODE);
|
||||
is_mask_empty = !uvm_page_mask_and(break_read_duplication_pages,
|
||||
&va_block->read_duplicated_pages,
|
||||
resident_mask);
|
||||
|
40
kernel-open/nvidia-uvm/uvm_processors.c
Normal file
40
kernel-open/nvidia-uvm/uvm_processors.c
Normal file
@ -0,0 +1,40 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2023 NVIDIA Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include "uvm_processors.h"
|
||||
|
||||
int uvm_find_closest_node_mask(int src, const nodemask_t *mask)
|
||||
{
|
||||
int nid;
|
||||
int closest_nid = NUMA_NO_NODE;
|
||||
|
||||
if (node_isset(src, *mask))
|
||||
return src;
|
||||
|
||||
for_each_set_bit(nid, mask->bits, MAX_NUMNODES) {
|
||||
if (closest_nid == NUMA_NO_NODE || node_distance(src, nid) < node_distance(src, closest_nid))
|
||||
closest_nid = nid;
|
||||
}
|
||||
|
||||
return closest_nid;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
Copyright (c) 2016-2019 NVIDIA Corporation
|
||||
Copyright (c) 2016-2023 NVIDIA Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
@ -26,6 +26,7 @@
|
||||
|
||||
#include "uvm_linux.h"
|
||||
#include "uvm_common.h"
|
||||
#include <linux/numa.h>
|
||||
|
||||
#define UVM_MAX_UNIQUE_GPU_PAIRS SUM_FROM_0_TO_N(UVM_MAX_GPUS - 1)
|
||||
|
||||
@ -37,11 +38,11 @@
|
||||
// provide type safety, they are wrapped within the uvm_processor_id_t struct.
|
||||
// The range of valid identifiers needs to cover the maximum number of
|
||||
// supported GPUs on a system plus the CPU. CPU is assigned value 0, and GPUs
|
||||
// range: [1, UVM_ID_MAX_GPUS].
|
||||
// range: [1, UVM_PARENT_ID_MAX_GPUS].
|
||||
//
|
||||
// There are some functions that only expect GPU identifiers and, in order to
|
||||
// make it clearer, the uvm_gpu_id_t alias type is provided. However, as this
|
||||
// type is just a typedef of uvm_processor_id_t, there is no type checking
|
||||
// make it clearer, the uvm_parent_gpu_id_t alias type is provided. However, as
|
||||
// this type is just a typedef of uvm_processor_id_t, there is no type checking
|
||||
// performed by the compiler.
|
||||
//
|
||||
// Identifier value vs index
|
||||
@ -60,22 +61,25 @@
|
||||
// the GPU within the GPU id space (basically id - 1).
|
||||
//
|
||||
// In the diagram below, MAX_SUB is used to abbreviate
|
||||
// UVM_ID_MAX_SUB_PROCESSORS.
|
||||
// UVM_PARENT_ID_MAX_SUB_PROCESSORS.
|
||||
//
|
||||
// |-------------------------- uvm_processor_id_t ----------------------|
|
||||
// | |
|
||||
// | |----------------------- uvm_gpu_id_t ------------------------||
|
||||
// | | ||
|
||||
// Proc type | CPU | GPU ... GPU ... GPU ||
|
||||
// | | ||
|
||||
// ID values | 0 | 1 ... i+1 ... UVM_ID_MAX_PROCESSORS-1 ||
|
||||
// TODO: Bug 4195538: uvm_parent_processor_id_t is currently but temporarily the
|
||||
// same as uvm_processor_id_t.
|
||||
//
|
||||
// GPU index 0 ... i ... UVM_ID_MAX_GPUS-1
|
||||
// |-------------------------- uvm_parent_processor_id_t ----------------------|
|
||||
// | |
|
||||
// | |----------------------- uvm_parent_gpu_id_t ------------------------||
|
||||
// | | ||
|
||||
// Proc type | CPU | GPU ... GPU ... GPU ||
|
||||
// | | ||
|
||||
// ID values | 0 | 1 ... i+1 ... UVM_PARENT_ID_MAX_PROCESSORS-1 ||
|
||||
//
|
||||
// GPU index 0 ... i ... UVM_PARENT_ID_MAX_GPUS-1
|
||||
// | | | |
|
||||
// | | | |
|
||||
// | |-------------| | |-----------------------------|
|
||||
// | | | |
|
||||
// | | | |
|
||||
// | |-------------| | |------------------------------------|
|
||||
// | | | |
|
||||
// | | | |
|
||||
// GPU index 0 ... MAX_SUB-1 ... i*MAX_SUB ... (i+1)*MAX_SUB-1 ... UVM_GLOBAL_ID_MAX_GPUS-1
|
||||
//
|
||||
// ID values | 0 | 1 ... MAX_SUB ... (i*MAX_SUB)+1 ... (i+1)*MAX_SUB ... UVM_GLOBAL_ID_MAX_PROCESSORS-1 ||
|
||||
@ -210,7 +214,7 @@ static proc_id_t prefix_fn_mask##_find_first_id(const mask_t *mask)
|
||||
\
|
||||
static proc_id_t prefix_fn_mask##_find_first_gpu_id(const mask_t *mask) \
|
||||
{ \
|
||||
return proc_id_ctor(find_next_bit(mask->bitmap, (maxval), UVM_ID_GPU0_VALUE)); \
|
||||
return proc_id_ctor(find_next_bit(mask->bitmap, (maxval), UVM_PARENT_ID_GPU0_VALUE)); \
|
||||
} \
|
||||
\
|
||||
static proc_id_t prefix_fn_mask##_find_next_id(const mask_t *mask, proc_id_t min_id) \
|
||||
@ -252,7 +256,7 @@ static NvU32 prefix_fn_mask##_get_gpu_count(const mask_t *mask)
|
||||
{ \
|
||||
NvU32 gpu_count = prefix_fn_mask##_get_count(mask); \
|
||||
\
|
||||
if (prefix_fn_mask##_test(mask, proc_id_ctor(UVM_ID_CPU_VALUE))) \
|
||||
if (prefix_fn_mask##_test(mask, proc_id_ctor(UVM_PARENT_ID_CPU_VALUE))) \
|
||||
--gpu_count; \
|
||||
\
|
||||
return gpu_count; \
|
||||
@ -261,55 +265,55 @@ static NvU32 prefix_fn_mask##_get_gpu_count(const mask_t *mask)
|
||||
typedef struct
|
||||
{
|
||||
NvU32 val;
|
||||
} uvm_processor_id_t;
|
||||
} uvm_parent_processor_id_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
NvU32 val;
|
||||
} uvm_global_processor_id_t;
|
||||
|
||||
typedef uvm_processor_id_t uvm_gpu_id_t;
|
||||
typedef uvm_parent_processor_id_t uvm_parent_gpu_id_t;
|
||||
typedef uvm_global_processor_id_t uvm_global_gpu_id_t;
|
||||
|
||||
// Static value assigned to the CPU
|
||||
#define UVM_ID_CPU_VALUE 0
|
||||
#define UVM_ID_GPU0_VALUE (UVM_ID_CPU_VALUE + 1)
|
||||
#define UVM_PARENT_ID_CPU_VALUE 0
|
||||
#define UVM_PARENT_ID_GPU0_VALUE (UVM_PARENT_ID_CPU_VALUE + 1)
|
||||
|
||||
// ID values for the CPU and first GPU, respectively; the values for both types
|
||||
// of IDs must match to enable sharing of UVM_PROCESSOR_MASK().
|
||||
#define UVM_GLOBAL_ID_CPU_VALUE UVM_ID_CPU_VALUE
|
||||
#define UVM_GLOBAL_ID_GPU0_VALUE UVM_ID_GPU0_VALUE
|
||||
#define UVM_GLOBAL_ID_CPU_VALUE UVM_PARENT_ID_CPU_VALUE
|
||||
#define UVM_GLOBAL_ID_GPU0_VALUE UVM_PARENT_ID_GPU0_VALUE
|
||||
|
||||
// Maximum number of GPUs/processors that can be represented with the id types
|
||||
#define UVM_ID_MAX_GPUS UVM_MAX_GPUS
|
||||
#define UVM_ID_MAX_PROCESSORS UVM_MAX_PROCESSORS
|
||||
#define UVM_PARENT_ID_MAX_GPUS UVM_MAX_GPUS
|
||||
#define UVM_PARENT_ID_MAX_PROCESSORS UVM_MAX_PROCESSORS
|
||||
|
||||
#define UVM_ID_MAX_SUB_PROCESSORS 8
|
||||
#define UVM_PARENT_ID_MAX_SUB_PROCESSORS 8
|
||||
|
||||
#define UVM_GLOBAL_ID_MAX_GPUS (UVM_MAX_GPUS * UVM_ID_MAX_SUB_PROCESSORS)
|
||||
#define UVM_GLOBAL_ID_MAX_GPUS (UVM_PARENT_ID_MAX_GPUS * UVM_PARENT_ID_MAX_SUB_PROCESSORS)
|
||||
#define UVM_GLOBAL_ID_MAX_PROCESSORS (UVM_GLOBAL_ID_MAX_GPUS + 1)
|
||||
|
||||
#define UVM_ID_CPU ((uvm_processor_id_t) { .val = UVM_ID_CPU_VALUE })
|
||||
#define UVM_ID_INVALID ((uvm_processor_id_t) { .val = UVM_ID_MAX_PROCESSORS })
|
||||
#define UVM_PARENT_ID_CPU ((uvm_parent_processor_id_t) { .val = UVM_PARENT_ID_CPU_VALUE })
|
||||
#define UVM_PARENT_ID_INVALID ((uvm_parent_processor_id_t) { .val = UVM_PARENT_ID_MAX_PROCESSORS })
|
||||
#define UVM_GLOBAL_ID_CPU ((uvm_global_processor_id_t) { .val = UVM_GLOBAL_ID_CPU_VALUE })
|
||||
#define UVM_GLOBAL_ID_INVALID ((uvm_global_processor_id_t) { .val = UVM_GLOBAL_ID_MAX_PROCESSORS })
|
||||
|
||||
#define UVM_ID_CHECK_BOUNDS(id) UVM_ASSERT_MSG(id.val <= UVM_ID_MAX_PROCESSORS, "id %u\n", id.val)
|
||||
#define UVM_PARENT_ID_CHECK_BOUNDS(id) UVM_ASSERT_MSG(id.val <= UVM_PARENT_ID_MAX_PROCESSORS, "id %u\n", id.val)
|
||||
|
||||
#define UVM_GLOBAL_ID_CHECK_BOUNDS(id) UVM_ASSERT_MSG(id.val <= UVM_GLOBAL_ID_MAX_PROCESSORS, "id %u\n", id.val)
|
||||
|
||||
static int uvm_id_cmp(uvm_processor_id_t id1, uvm_processor_id_t id2)
|
||||
static int uvm_parent_id_cmp(uvm_parent_processor_id_t id1, uvm_parent_processor_id_t id2)
|
||||
{
|
||||
UVM_ID_CHECK_BOUNDS(id1);
|
||||
UVM_ID_CHECK_BOUNDS(id2);
|
||||
UVM_PARENT_ID_CHECK_BOUNDS(id1);
|
||||
UVM_PARENT_ID_CHECK_BOUNDS(id2);
|
||||
|
||||
return UVM_CMP_DEFAULT(id1.val, id2.val);
|
||||
}
|
||||
|
||||
static bool uvm_id_equal(uvm_processor_id_t id1, uvm_processor_id_t id2)
|
||||
static bool uvm_parent_id_equal(uvm_parent_processor_id_t id1, uvm_parent_processor_id_t id2)
|
||||
{
|
||||
UVM_ID_CHECK_BOUNDS(id1);
|
||||
UVM_ID_CHECK_BOUNDS(id2);
|
||||
UVM_PARENT_ID_CHECK_BOUNDS(id1);
|
||||
UVM_PARENT_ID_CHECK_BOUNDS(id2);
|
||||
|
||||
return id1.val == id2.val;
|
||||
}
|
||||
@ -330,30 +334,30 @@ static bool uvm_global_id_equal(uvm_global_processor_id_t id1, uvm_global_proces
|
||||
return id1.val == id2.val;
|
||||
}
|
||||
|
||||
#define UVM_ID_IS_CPU(id) uvm_id_equal(id, UVM_ID_CPU)
|
||||
#define UVM_ID_IS_INVALID(id) uvm_id_equal(id, UVM_ID_INVALID)
|
||||
#define UVM_ID_IS_VALID(id) (!UVM_ID_IS_INVALID(id))
|
||||
#define UVM_ID_IS_GPU(id) (!UVM_ID_IS_CPU(id) && !UVM_ID_IS_INVALID(id))
|
||||
#define UVM_PARENT_ID_IS_CPU(id) uvm_parent_id_equal(id, UVM_PARENT_ID_CPU)
|
||||
#define UVM_PARENT_ID_IS_INVALID(id) uvm_parent_id_equal(id, UVM_PARENT_ID_INVALID)
|
||||
#define UVM_PARENT_ID_IS_VALID(id) (!UVM_PARENT_ID_IS_INVALID(id))
|
||||
#define UVM_PARENT_ID_IS_GPU(id) (!UVM_PARENT_ID_IS_CPU(id) && !UVM_PARENT_ID_IS_INVALID(id))
|
||||
|
||||
#define UVM_GLOBAL_ID_IS_CPU(id) uvm_global_id_equal(id, UVM_GLOBAL_ID_CPU)
|
||||
#define UVM_GLOBAL_ID_IS_INVALID(id) uvm_global_id_equal(id, UVM_GLOBAL_ID_INVALID)
|
||||
#define UVM_GLOBAL_ID_IS_VALID(id) (!UVM_GLOBAL_ID_IS_INVALID(id))
|
||||
#define UVM_GLOBAL_ID_IS_GPU(id) (!UVM_GLOBAL_ID_IS_CPU(id) && !UVM_GLOBAL_ID_IS_INVALID(id))
|
||||
|
||||
static uvm_processor_id_t uvm_id_from_value(NvU32 val)
|
||||
static uvm_parent_processor_id_t uvm_parent_id_from_value(NvU32 val)
|
||||
{
|
||||
uvm_processor_id_t ret = { .val = val };
|
||||
uvm_parent_processor_id_t ret = { .val = val };
|
||||
|
||||
UVM_ID_CHECK_BOUNDS(ret);
|
||||
UVM_PARENT_ID_CHECK_BOUNDS(ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uvm_gpu_id_t uvm_gpu_id_from_value(NvU32 val)
|
||||
static uvm_parent_gpu_id_t uvm_parent_gpu_id_from_value(NvU32 val)
|
||||
{
|
||||
uvm_gpu_id_t ret = uvm_id_from_value(val);
|
||||
uvm_parent_gpu_id_t ret = uvm_parent_id_from_value(val);
|
||||
|
||||
UVM_ASSERT(!UVM_ID_IS_CPU(ret));
|
||||
UVM_ASSERT(!UVM_PARENT_ID_IS_CPU(ret));
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -376,34 +380,34 @@ static uvm_global_gpu_id_t uvm_global_gpu_id_from_value(NvU32 val)
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Create a GPU id from the given GPU id index (previously obtained via
|
||||
// uvm_id_gpu_index)
|
||||
static uvm_gpu_id_t uvm_gpu_id_from_index(NvU32 index)
|
||||
// Create a parent GPU id from the given parent GPU id index (previously
|
||||
// obtained via uvm_parent_id_gpu_index)
|
||||
static uvm_parent_gpu_id_t uvm_parent_gpu_id_from_index(NvU32 index)
|
||||
{
|
||||
return uvm_gpu_id_from_value(index + UVM_ID_GPU0_VALUE);
|
||||
return uvm_parent_gpu_id_from_value(index + UVM_PARENT_ID_GPU0_VALUE);
|
||||
}
|
||||
|
||||
static uvm_processor_id_t uvm_id_next(uvm_processor_id_t id)
|
||||
static uvm_parent_processor_id_t uvm_parent_id_next(uvm_parent_processor_id_t id)
|
||||
{
|
||||
++id.val;
|
||||
|
||||
UVM_ID_CHECK_BOUNDS(id);
|
||||
UVM_PARENT_ID_CHECK_BOUNDS(id);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
static uvm_gpu_id_t uvm_gpu_id_next(uvm_gpu_id_t id)
|
||||
static uvm_parent_gpu_id_t uvm_parent_gpu_id_next(uvm_parent_gpu_id_t id)
|
||||
{
|
||||
UVM_ASSERT(UVM_ID_IS_GPU(id));
|
||||
UVM_ASSERT(UVM_PARENT_ID_IS_GPU(id));
|
||||
|
||||
++id.val;
|
||||
|
||||
UVM_ID_CHECK_BOUNDS(id);
|
||||
UVM_PARENT_ID_CHECK_BOUNDS(id);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
// Same as uvm_gpu_id_from_index but for uvm_global_processor_id_t
|
||||
// Same as uvm_parent_gpu_id_from_index but for uvm_global_processor_id_t
|
||||
static uvm_global_gpu_id_t uvm_global_gpu_id_from_index(NvU32 index)
|
||||
{
|
||||
return uvm_global_gpu_id_from_value(index + UVM_GLOBAL_ID_GPU0_VALUE);
|
||||
@ -429,11 +433,11 @@ static uvm_global_gpu_id_t uvm_global_gpu_id_next(uvm_global_gpu_id_t id)
|
||||
return id;
|
||||
}
|
||||
|
||||
// This function returns the numerical value within [0, UVM_ID_MAX_PROCESSORS)
|
||||
// of the given processor id
|
||||
static NvU32 uvm_id_value(uvm_processor_id_t id)
|
||||
// This function returns the numerical value within
|
||||
// [0, UVM_PARENT_ID_MAX_PROCESSORS) of the given parent processor id.
|
||||
static NvU32 uvm_parent_id_value(uvm_parent_processor_id_t id)
|
||||
{
|
||||
UVM_ASSERT(UVM_ID_IS_VALID(id));
|
||||
UVM_ASSERT(UVM_PARENT_ID_IS_VALID(id));
|
||||
|
||||
return id.val;
|
||||
}
|
||||
@ -448,12 +452,12 @@ static NvU32 uvm_global_id_value(uvm_global_processor_id_t id)
|
||||
}
|
||||
|
||||
// This function returns the index of the given GPU id within the GPU id space
|
||||
// [0, UVM_ID_MAX_GPUS)
|
||||
static NvU32 uvm_id_gpu_index(uvm_gpu_id_t id)
|
||||
// [0, UVM_PARENT_ID_MAX_GPUS)
|
||||
static NvU32 uvm_parent_id_gpu_index(uvm_parent_gpu_id_t id)
|
||||
{
|
||||
UVM_ASSERT(UVM_ID_IS_GPU(id));
|
||||
UVM_ASSERT(UVM_PARENT_ID_IS_GPU(id));
|
||||
|
||||
return id.val - UVM_ID_GPU0_VALUE;
|
||||
return id.val - UVM_PARENT_ID_GPU0_VALUE;
|
||||
}
|
||||
|
||||
// This function returns the index of the given GPU id within the GPU id space
|
||||
@ -465,61 +469,61 @@ static NvU32 uvm_global_id_gpu_index(const uvm_global_gpu_id_t id)
|
||||
return id.val - UVM_GLOBAL_ID_GPU0_VALUE;
|
||||
}
|
||||
|
||||
static NvU32 uvm_global_id_gpu_index_from_gpu_id(const uvm_gpu_id_t id)
|
||||
static NvU32 uvm_global_id_gpu_index_from_parent_gpu_id(const uvm_parent_gpu_id_t id)
|
||||
{
|
||||
UVM_ASSERT(UVM_ID_IS_GPU(id));
|
||||
UVM_ASSERT(UVM_PARENT_ID_IS_GPU(id));
|
||||
|
||||
return uvm_id_gpu_index(id) * UVM_ID_MAX_SUB_PROCESSORS;
|
||||
return uvm_parent_id_gpu_index(id) * UVM_PARENT_ID_MAX_SUB_PROCESSORS;
|
||||
}
|
||||
|
||||
static NvU32 uvm_id_gpu_index_from_global_gpu_id(const uvm_global_gpu_id_t id)
|
||||
static NvU32 uvm_parent_id_gpu_index_from_global_gpu_id(const uvm_global_gpu_id_t id)
|
||||
{
|
||||
UVM_ASSERT(UVM_GLOBAL_ID_IS_GPU(id));
|
||||
|
||||
return uvm_global_id_gpu_index(id) / UVM_ID_MAX_SUB_PROCESSORS;
|
||||
return uvm_global_id_gpu_index(id) / UVM_PARENT_ID_MAX_SUB_PROCESSORS;
|
||||
}
|
||||
|
||||
static uvm_global_gpu_id_t uvm_global_gpu_id_from_gpu_id(const uvm_gpu_id_t id)
|
||||
static uvm_global_gpu_id_t uvm_global_gpu_id_from_parent_gpu_id(const uvm_parent_gpu_id_t id)
|
||||
{
|
||||
UVM_ASSERT(UVM_ID_IS_GPU(id));
|
||||
UVM_ASSERT(UVM_PARENT_ID_IS_GPU(id));
|
||||
|
||||
return uvm_global_gpu_id_from_index(uvm_global_id_gpu_index_from_gpu_id(id));
|
||||
return uvm_global_gpu_id_from_index(uvm_global_id_gpu_index_from_parent_gpu_id(id));
|
||||
}
|
||||
|
||||
static uvm_global_gpu_id_t uvm_global_gpu_id_from_parent_index(NvU32 index)
|
||||
{
|
||||
UVM_ASSERT(index < UVM_MAX_GPUS);
|
||||
UVM_ASSERT(index < UVM_PARENT_ID_MAX_GPUS);
|
||||
|
||||
return uvm_global_gpu_id_from_gpu_id(uvm_gpu_id_from_value(index + UVM_GLOBAL_ID_GPU0_VALUE));
|
||||
return uvm_global_gpu_id_from_parent_gpu_id(uvm_parent_gpu_id_from_value(index + UVM_GLOBAL_ID_GPU0_VALUE));
|
||||
}
|
||||
|
||||
static uvm_global_gpu_id_t uvm_global_gpu_id_from_sub_processor_index(const uvm_gpu_id_t id, NvU32 sub_index)
|
||||
static uvm_global_gpu_id_t uvm_global_gpu_id_from_sub_processor_index(const uvm_parent_gpu_id_t id, NvU32 sub_index)
|
||||
{
|
||||
NvU32 index;
|
||||
|
||||
UVM_ASSERT(sub_index < UVM_ID_MAX_SUB_PROCESSORS);
|
||||
UVM_ASSERT(sub_index < UVM_PARENT_ID_MAX_SUB_PROCESSORS);
|
||||
|
||||
index = uvm_global_id_gpu_index_from_gpu_id(id) + sub_index;
|
||||
index = uvm_global_id_gpu_index_from_parent_gpu_id(id) + sub_index;
|
||||
return uvm_global_gpu_id_from_index(index);
|
||||
}
|
||||
|
||||
static uvm_gpu_id_t uvm_gpu_id_from_global_gpu_id(const uvm_global_gpu_id_t id)
|
||||
static uvm_parent_gpu_id_t uvm_parent_gpu_id_from_global_gpu_id(const uvm_global_gpu_id_t id)
|
||||
{
|
||||
UVM_ASSERT(UVM_GLOBAL_ID_IS_GPU(id));
|
||||
|
||||
return uvm_gpu_id_from_index(uvm_id_gpu_index_from_global_gpu_id(id));
|
||||
return uvm_parent_gpu_id_from_index(uvm_parent_id_gpu_index_from_global_gpu_id(id));
|
||||
}
|
||||
|
||||
static NvU32 uvm_global_id_sub_processor_index(const uvm_global_gpu_id_t id)
|
||||
{
|
||||
return uvm_global_id_gpu_index(id) % UVM_ID_MAX_SUB_PROCESSORS;
|
||||
return uvm_global_id_gpu_index(id) % UVM_PARENT_ID_MAX_SUB_PROCESSORS;
|
||||
}
|
||||
|
||||
UVM_PROCESSOR_MASK(uvm_processor_mask_t, \
|
||||
uvm_processor_mask, \
|
||||
UVM_ID_MAX_PROCESSORS, \
|
||||
uvm_processor_id_t, \
|
||||
uvm_id_from_value)
|
||||
UVM_PARENT_ID_MAX_PROCESSORS, \
|
||||
uvm_parent_processor_id_t, \
|
||||
uvm_parent_id_from_value)
|
||||
|
||||
UVM_PROCESSOR_MASK(uvm_global_processor_mask_t, \
|
||||
uvm_global_processor_mask, \
|
||||
@ -533,19 +537,19 @@ static bool uvm_processor_mask_gpu_subset(const uvm_processor_mask_t *subset, co
|
||||
{
|
||||
uvm_processor_mask_t subset_gpus;
|
||||
uvm_processor_mask_copy(&subset_gpus, subset);
|
||||
uvm_processor_mask_clear(&subset_gpus, UVM_ID_CPU);
|
||||
uvm_processor_mask_clear(&subset_gpus, UVM_PARENT_ID_CPU);
|
||||
return uvm_processor_mask_subset(&subset_gpus, mask);
|
||||
}
|
||||
|
||||
#define for_each_id_in_mask(id, mask) \
|
||||
for ((id) = uvm_processor_mask_find_first_id(mask); \
|
||||
UVM_ID_IS_VALID(id); \
|
||||
(id) = uvm_processor_mask_find_next_id((mask), uvm_id_next(id)))
|
||||
UVM_PARENT_ID_IS_VALID(id); \
|
||||
(id) = uvm_processor_mask_find_next_id((mask), uvm_parent_id_next(id)))
|
||||
|
||||
#define for_each_gpu_id_in_mask(gpu_id, mask) \
|
||||
for ((gpu_id) = uvm_processor_mask_find_first_gpu_id((mask)); \
|
||||
UVM_ID_IS_VALID(gpu_id); \
|
||||
(gpu_id) = uvm_processor_mask_find_next_id((mask), uvm_gpu_id_next(gpu_id)))
|
||||
UVM_PARENT_ID_IS_VALID(gpu_id); \
|
||||
(gpu_id) = uvm_processor_mask_find_next_id((mask), uvm_parent_gpu_id_next(gpu_id)))
|
||||
|
||||
#define for_each_global_id_in_mask(id, mask) \
|
||||
for ((id) = uvm_global_processor_mask_find_first_id(mask); \
|
||||
@ -559,21 +563,36 @@ static bool uvm_processor_mask_gpu_subset(const uvm_processor_mask_t *subset, co
|
||||
|
||||
// Helper to iterate over all valid gpu ids
|
||||
#define for_each_gpu_id(i) \
|
||||
for (i = uvm_gpu_id_from_value(UVM_ID_GPU0_VALUE); UVM_ID_IS_VALID(i); i = uvm_gpu_id_next(i))
|
||||
for (i = uvm_parent_gpu_id_from_value(UVM_PARENT_ID_GPU0_VALUE); UVM_PARENT_ID_IS_VALID(i); i = uvm_parent_gpu_id_next(i))
|
||||
#define for_each_global_gpu_id(i) \
|
||||
for (i = uvm_global_gpu_id_from_value(UVM_GLOBAL_ID_GPU0_VALUE); UVM_GLOBAL_ID_IS_VALID(i); i = uvm_global_gpu_id_next(i))
|
||||
|
||||
#define for_each_global_sub_processor_id_in_gpu(id, i) \
|
||||
for (i = uvm_global_gpu_id_from_gpu_id(id); \
|
||||
for (i = uvm_global_gpu_id_from_parent_gpu_id(id); \
|
||||
UVM_GLOBAL_ID_IS_VALID(i) && \
|
||||
(uvm_global_id_value(i) < uvm_global_id_value(uvm_global_gpu_id_from_gpu_id(id)) + UVM_ID_MAX_SUB_PROCESSORS); \
|
||||
(uvm_global_id_value(i) < uvm_global_id_value(uvm_global_gpu_id_from_parent_gpu_id(id)) + UVM_PARENT_ID_MAX_SUB_PROCESSORS); \
|
||||
i = uvm_global_gpu_id_next(i))
|
||||
|
||||
// Helper to iterate over all valid gpu ids
|
||||
#define for_each_processor_id(i) for (i = UVM_ID_CPU; UVM_ID_IS_VALID(i); i = uvm_id_next(i))
|
||||
#define for_each_processor_id(i) for (i = UVM_PARENT_ID_CPU; UVM_PARENT_ID_IS_VALID(i); i = uvm_parent_id_next(i))
|
||||
|
||||
#define for_each_global_id(i) for (i = UVM_GLOBAL_ID_CPU; UVM_GLOBAL_ID_IS_VALID(i); i = uvm_global_id_next(i))
|
||||
|
||||
// Find the node in mask with the shorted distance (as returned by
|
||||
// node_distance) for src.
|
||||
// Note that the search is inclusive of src.
|
||||
// If mask has no bits set, NUMA_NO_NODE is returned.
|
||||
int uvm_find_closest_node_mask(int src, const nodemask_t *mask);
|
||||
|
||||
// Iterate over all nodes in mask with increasing distance from src.
|
||||
// Note that this iterator is destructive of the mask.
|
||||
#define for_each_closest_uvm_node(nid, src, mask) \
|
||||
for ((nid) = uvm_find_closest_node_mask((src), &(mask)); \
|
||||
(nid) != NUMA_NO_NODE; \
|
||||
node_clear((nid), (mask)), (nid) = uvm_find_closest_node_mask((src), &(mask)))
|
||||
|
||||
#define for_each_possible_uvm_node(nid) for_each_node_mask((nid), node_possible_map)
|
||||
|
||||
static bool uvm_processor_uuid_eq(const NvProcessorUuid *uuid1, const NvProcessorUuid *uuid2)
|
||||
{
|
||||
return memcmp(uuid1, uuid2, sizeof(*uuid1)) == 0;
|
||||
@ -585,4 +604,78 @@ static void uvm_processor_uuid_copy(NvProcessorUuid *dst, const NvProcessorUuid
|
||||
memcpy(dst, src, sizeof(*dst));
|
||||
}
|
||||
|
||||
// TODO: Bug 4195538: [uvm][multi-SMC] Get UVM internal data structures ready to
|
||||
// meet multi-SMC requirements. Temporary aliases, they must be removed once
|
||||
// the data structures are converted.
|
||||
typedef uvm_parent_processor_id_t uvm_processor_id_t;
|
||||
typedef uvm_parent_gpu_id_t uvm_gpu_id_t;
|
||||
|
||||
#define UVM_ID_CPU_VALUE UVM_PARENT_ID_CPU_VALUE
|
||||
#define UVM_ID_GPU0_VALUE UVM_PARENT_ID_GPU0_VALUE
|
||||
#define UVM_ID_MAX_GPUS UVM_PARENT_ID_MAX_GPUS
|
||||
#define UVM_ID_MAX_PROCESSORS UVM_PARENT_ID_MAX_PROCESSORS
|
||||
#define UVM_ID_MAX_SUB_PROCESSORS UVM_PARENT_ID_MAX_SUB_PROCESSORS
|
||||
#define UVM_ID_CPU UVM_PARENT_ID_CPU
|
||||
#define UVM_ID_INVALID UVM_PARENT_ID_INVALID
|
||||
|
||||
static int uvm_id_cmp(uvm_parent_processor_id_t id1, uvm_parent_processor_id_t id2)
|
||||
{
|
||||
return UVM_CMP_DEFAULT(id1.val, id2.val);
|
||||
}
|
||||
|
||||
static bool uvm_id_equal(uvm_parent_processor_id_t id1, uvm_parent_processor_id_t id2)
|
||||
{
|
||||
return uvm_parent_id_equal(id1, id2);
|
||||
}
|
||||
|
||||
#define UVM_ID_IS_CPU(id) uvm_id_equal(id, UVM_ID_CPU)
|
||||
#define UVM_ID_IS_INVALID(id) uvm_id_equal(id, UVM_ID_INVALID)
|
||||
#define UVM_ID_IS_VALID(id) (!UVM_ID_IS_INVALID(id))
|
||||
#define UVM_ID_IS_GPU(id) (!UVM_ID_IS_CPU(id) && !UVM_ID_IS_INVALID(id))
|
||||
|
||||
static uvm_parent_gpu_id_t uvm_gpu_id_from_value(NvU32 val)
|
||||
{
|
||||
return uvm_parent_gpu_id_from_value(val);
|
||||
}
|
||||
|
||||
static NvU32 uvm_id_value(uvm_parent_processor_id_t id)
|
||||
{
|
||||
return uvm_parent_id_value(id);
|
||||
}
|
||||
|
||||
static NvU32 uvm_id_gpu_index(uvm_parent_gpu_id_t id)
|
||||
{
|
||||
return uvm_parent_id_gpu_index(id);
|
||||
}
|
||||
|
||||
static NvU32 uvm_id_gpu_index_from_global_gpu_id(const uvm_global_gpu_id_t id)
|
||||
{
|
||||
return uvm_parent_id_gpu_index_from_global_gpu_id(id);
|
||||
}
|
||||
|
||||
static uvm_parent_gpu_id_t uvm_gpu_id_from_index(NvU32 index)
|
||||
{
|
||||
return uvm_parent_gpu_id_from_index(index);
|
||||
}
|
||||
|
||||
static uvm_parent_gpu_id_t uvm_gpu_id_next(uvm_parent_gpu_id_t id)
|
||||
{
|
||||
return uvm_parent_gpu_id_next(id);
|
||||
}
|
||||
|
||||
static uvm_parent_gpu_id_t uvm_gpu_id_from_global_gpu_id(const uvm_global_gpu_id_t id)
|
||||
{
|
||||
return uvm_parent_gpu_id_from_global_gpu_id(id);
|
||||
}
|
||||
|
||||
static NvU32 uvm_global_id_gpu_index_from_gpu_id(const uvm_parent_gpu_id_t id)
|
||||
{
|
||||
return uvm_global_id_gpu_index_from_parent_gpu_id(id);
|
||||
}
|
||||
|
||||
static uvm_global_gpu_id_t uvm_global_gpu_id_from_gpu_id(const uvm_parent_gpu_id_t id)
|
||||
{
|
||||
return uvm_global_gpu_id_from_parent_gpu_id(id);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -106,26 +106,6 @@ static NV_STATUS uvm_test_nv_kthread_q(UVM_TEST_NV_KTHREAD_Q_PARAMS *params, str
|
||||
return NV_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
static NV_STATUS uvm_test_numa_get_closest_cpu_node_to_gpu(UVM_TEST_NUMA_GET_CLOSEST_CPU_NODE_TO_GPU_PARAMS *params,
|
||||
struct file *filp)
|
||||
{
|
||||
uvm_gpu_t *gpu;
|
||||
NV_STATUS status;
|
||||
uvm_rm_user_object_t user_rm_va_space = {
|
||||
.rm_control_fd = -1,
|
||||
.user_client = params->client,
|
||||
.user_object = params->smc_part_ref
|
||||
};
|
||||
|
||||
status = uvm_gpu_retain_by_uuid(¶ms->gpu_uuid, &user_rm_va_space, &gpu);
|
||||
if (status != NV_OK)
|
||||
return status;
|
||||
|
||||
params->node_id = gpu->parent->closest_cpu_numa_node;
|
||||
uvm_gpu_release(gpu);
|
||||
return NV_OK;
|
||||
}
|
||||
|
||||
// Callers of this function should ensure that node is not NUMA_NO_NODE in order
|
||||
// to avoid overrunning the kernel's node to cpumask map.
|
||||
static NV_STATUS uvm_test_verify_bh_affinity(uvm_intr_handler_t *isr, int node)
|
||||
@ -307,8 +287,6 @@ long uvm_test_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
UVM_ROUTE_CMD_STACK_INIT_CHECK(UVM_TEST_DRAIN_REPLAYABLE_FAULTS, uvm_test_drain_replayable_faults);
|
||||
UVM_ROUTE_CMD_STACK_INIT_CHECK(UVM_TEST_PMA_GET_BATCH_SIZE, uvm_test_pma_get_batch_size);
|
||||
UVM_ROUTE_CMD_STACK_INIT_CHECK(UVM_TEST_PMM_QUERY_PMA_STATS, uvm_test_pmm_query_pma_stats);
|
||||
UVM_ROUTE_CMD_STACK_INIT_CHECK(UVM_TEST_NUMA_GET_CLOSEST_CPU_NODE_TO_GPU,
|
||||
uvm_test_numa_get_closest_cpu_node_to_gpu);
|
||||
UVM_ROUTE_CMD_STACK_INIT_CHECK(UVM_TEST_NUMA_CHECK_AFFINITY, uvm_test_numa_check_affinity);
|
||||
UVM_ROUTE_CMD_STACK_INIT_CHECK(UVM_TEST_VA_SPACE_ADD_DUMMY_THREAD_CONTEXTS,
|
||||
uvm_test_va_space_add_dummy_thread_contexts);
|
||||
|
@ -561,6 +561,22 @@ typedef struct
|
||||
// user_pages_allocation_retry_force_count, but the injection point simulates
|
||||
// driver metadata allocation failure.
|
||||
//
|
||||
// cpu_chunk_allocation_target_id and cpu_chunk_allocation_actual_id are used
|
||||
// to control the NUMA node IDs for CPU chunk allocations, specifically for
|
||||
// testing overlapping CPU chunk allocations.
|
||||
//
|
||||
// Currently, uvm_api_migrate() does not pass the preferred CPU NUMA node to for
|
||||
// managed memory so it is not possible to request a specific node.
|
||||
// cpu_chunk_allocation_target_id is used to request the allocation be made on
|
||||
// specific node. On the other hand, cpu_chunk_allocation_actual_id is the node
|
||||
// on which the allocation will actually be made.
|
||||
//
|
||||
// The two parameters can be used to force a CPU chunk allocation to overlap a
|
||||
// previously allocated chunk.
|
||||
//
|
||||
// Please note that even when specifying cpu_cpu_allocation_actual_id, the
|
||||
// kernel may end up allocating on a different node.
|
||||
//
|
||||
// Error returns:
|
||||
// NV_ERR_INVALID_ADDRESS
|
||||
// - lookup_address doesn't match a UVM range
|
||||
@ -571,6 +587,8 @@ typedef struct
|
||||
NvU32 page_table_allocation_retry_force_count; // In
|
||||
NvU32 user_pages_allocation_retry_force_count; // In
|
||||
NvU32 cpu_chunk_allocation_size_mask; // In
|
||||
NvS32 cpu_chunk_allocation_target_id; // In
|
||||
NvS32 cpu_chunk_allocation_actual_id; // In
|
||||
NvU32 cpu_pages_allocation_error_count; // In
|
||||
NvBool eviction_error; // In
|
||||
NvBool populate_error; // In
|
||||
@ -604,6 +622,10 @@ typedef struct
|
||||
NvProcessorUuid resident_on[UVM_MAX_PROCESSORS]; // Out
|
||||
NvU32 resident_on_count; // Out
|
||||
|
||||
// If the memory is resident on the CPU, the NUMA node on which the page
|
||||
// is resident. Otherwise, -1.
|
||||
NvS32 resident_nid; // Out
|
||||
|
||||
// The size of the physical allocation backing lookup_address. Only the
|
||||
// system-page-sized portion of this allocation which contains
|
||||
// lookup_address is guaranteed to be resident on the corresponding
|
||||
@ -1168,19 +1190,6 @@ typedef struct
|
||||
NV_STATUS rmStatus; // Out
|
||||
} UVM_TEST_PMM_QUERY_PMA_STATS_PARAMS;
|
||||
|
||||
#define UVM_TEST_NUMA_GET_CLOSEST_CPU_NODE_TO_GPU UVM_TEST_IOCTL_BASE(77)
|
||||
typedef struct
|
||||
{
|
||||
NvProcessorUuid gpu_uuid; // In
|
||||
NvHandle client; // In
|
||||
NvHandle smc_part_ref; // In
|
||||
|
||||
// On kernels with NUMA support, this entry contains the closest CPU NUMA
|
||||
// node to this GPU. Otherwise, the value will be -1.
|
||||
NvS32 node_id; // Out
|
||||
NV_STATUS rmStatus; // Out
|
||||
} UVM_TEST_NUMA_GET_CLOSEST_CPU_NODE_TO_GPU_PARAMS;
|
||||
|
||||
// Test whether the bottom halves have run on the correct CPUs based on the
|
||||
// NUMA node locality of the GPU.
|
||||
//
|
||||
|
@ -54,6 +54,7 @@ static struct kmem_cache *g_uvm_va_block_cache __read_mostly;
|
||||
static struct kmem_cache *g_uvm_va_block_gpu_state_cache __read_mostly;
|
||||
static struct kmem_cache *g_uvm_page_mask_cache __read_mostly;
|
||||
static struct kmem_cache *g_uvm_va_block_context_cache __read_mostly;
|
||||
static struct kmem_cache *g_uvm_va_block_cpu_node_state_cache __read_mostly;
|
||||
|
||||
static int uvm_fault_force_sysmem __read_mostly = 0;
|
||||
module_param(uvm_fault_force_sysmem, int, S_IRUGO|S_IWUSR);
|
||||
@ -179,6 +180,20 @@ void uvm_va_block_retry_init(uvm_va_block_retry_t *retry)
|
||||
INIT_LIST_HEAD(&retry->free_chunks);
|
||||
}
|
||||
|
||||
static size_t node_to_index(int nid)
|
||||
{
|
||||
UVM_ASSERT(nid != NUMA_NO_NODE);
|
||||
UVM_ASSERT(nid < MAX_NUMNODES);
|
||||
return __nodes_weight(&node_possible_map, nid);
|
||||
}
|
||||
|
||||
static uvm_va_block_cpu_node_state_t *block_node_state_get(uvm_va_block_t *block, int nid)
|
||||
{
|
||||
size_t index = node_to_index(nid);
|
||||
UVM_ASSERT(block->cpu.node_state[index]);
|
||||
return block->cpu.node_state[index];
|
||||
}
|
||||
|
||||
// The bottom bit of uvm_va_block_t::chunks is used to indicate how CPU chunks
|
||||
// are stored.
|
||||
//
|
||||
@ -227,14 +242,26 @@ static uvm_va_block_region_t uvm_cpu_chunk_block_region(uvm_va_block_t *va_block
|
||||
return uvm_va_block_chunk_region(va_block, uvm_cpu_chunk_get_size(chunk), page_index);
|
||||
}
|
||||
|
||||
static void *uvm_cpu_storage_get_ptr(uvm_va_block_t *block)
|
||||
static void *uvm_cpu_storage_get_ptr(uvm_va_block_cpu_node_state_t *node_state)
|
||||
{
|
||||
return (void *)(block->cpu.chunks & ~UVM_CPU_CHUNK_STORAGE_MASK);
|
||||
return (void *)(node_state->chunks & ~UVM_CPU_CHUNK_STORAGE_MASK);
|
||||
}
|
||||
|
||||
static uvm_cpu_chunk_storage_type_t uvm_cpu_storage_get_type(uvm_va_block_t *block)
|
||||
static uvm_cpu_chunk_storage_type_t uvm_cpu_storage_get_type(uvm_va_block_cpu_node_state_t *node_state)
|
||||
{
|
||||
return block->cpu.chunks & UVM_CPU_CHUNK_STORAGE_MASK;
|
||||
return node_state->chunks & UVM_CPU_CHUNK_STORAGE_MASK;
|
||||
}
|
||||
|
||||
static int block_get_page_node_residency(uvm_va_block_t *block, uvm_page_index_t page_index)
|
||||
{
|
||||
int nid;
|
||||
|
||||
for_each_possible_uvm_node(nid) {
|
||||
if (uvm_va_block_cpu_is_page_resident_on(block, nid, page_index))
|
||||
return nid;
|
||||
}
|
||||
|
||||
return NUMA_NO_NODE;
|
||||
}
|
||||
|
||||
static uvm_page_index_t compute_page_prefix(uvm_va_block_t *va_block, uvm_chunk_size_t size)
|
||||
@ -270,12 +297,12 @@ static size_t compute_small_index(uvm_va_block_t *va_block, uvm_page_index_t pag
|
||||
return (page_index - prefix) % MAX_SMALL_CHUNKS_PER_BIG_SLOT;
|
||||
}
|
||||
|
||||
NV_STATUS uvm_cpu_chunk_insert_in_block(uvm_va_block_t *va_block,
|
||||
uvm_cpu_chunk_t *chunk,
|
||||
uvm_page_index_t page_index)
|
||||
NV_STATUS uvm_cpu_chunk_insert_in_block(uvm_va_block_t *va_block, uvm_cpu_chunk_t *chunk, uvm_page_index_t page_index)
|
||||
{
|
||||
uvm_chunk_size_t chunk_size = uvm_cpu_chunk_get_size(chunk);
|
||||
uvm_va_block_region_t chunk_region = uvm_va_block_region(page_index, page_index + uvm_cpu_chunk_num_pages(chunk));
|
||||
int nid = uvm_cpu_chunk_get_numa_node(chunk);
|
||||
uvm_va_block_cpu_node_state_t *node_state = block_node_state_get(va_block, nid);
|
||||
size_t slot_index;
|
||||
uvm_cpu_chunk_storage_mixed_t *mixed;
|
||||
uvm_cpu_chunk_t **chunks = NULL;
|
||||
@ -291,20 +318,20 @@ NV_STATUS uvm_cpu_chunk_insert_in_block(uvm_va_block_t *va_block,
|
||||
|
||||
if (chunk_size == UVM_CHUNK_SIZE_2M) {
|
||||
UVM_ASSERT(uvm_va_block_size(va_block) == UVM_PAGE_SIZE_2M);
|
||||
UVM_ASSERT(!va_block->cpu.chunks);
|
||||
va_block->cpu.chunks = (unsigned long)chunk | UVM_CPU_CHUNK_STORAGE_CHUNK;
|
||||
UVM_ASSERT(!node_state->chunks);
|
||||
node_state->chunks = (unsigned long)chunk | UVM_CPU_CHUNK_STORAGE_CHUNK;
|
||||
}
|
||||
else {
|
||||
if (!va_block->cpu.chunks) {
|
||||
if (!node_state->chunks) {
|
||||
mixed = uvm_kvmalloc_zero(sizeof(*mixed));
|
||||
if (!mixed)
|
||||
return NV_ERR_NO_MEMORY;
|
||||
|
||||
va_block->cpu.chunks = (unsigned long)mixed | UVM_CPU_CHUNK_STORAGE_MIXED;
|
||||
node_state->chunks = (unsigned long)mixed | UVM_CPU_CHUNK_STORAGE_MIXED;
|
||||
}
|
||||
|
||||
UVM_ASSERT(uvm_cpu_storage_get_type(va_block) == UVM_CPU_CHUNK_STORAGE_MIXED);
|
||||
mixed = uvm_cpu_storage_get_ptr(va_block);
|
||||
UVM_ASSERT(uvm_cpu_storage_get_type(node_state) == UVM_CPU_CHUNK_STORAGE_MIXED);
|
||||
mixed = uvm_cpu_storage_get_ptr(node_state);
|
||||
slot_index = compute_slot_index(va_block, page_index);
|
||||
UVM_ASSERT(compute_slot_index(va_block, page_index + uvm_cpu_chunk_num_pages(chunk) - 1) == slot_index);
|
||||
UVM_ASSERT(!test_bit(slot_index, mixed->big_chunks));
|
||||
@ -331,28 +358,32 @@ NV_STATUS uvm_cpu_chunk_insert_in_block(uvm_va_block_t *va_block,
|
||||
}
|
||||
}
|
||||
|
||||
uvm_page_mask_region_fill(&node_state->allocated, chunk_region);
|
||||
uvm_page_mask_region_fill(&va_block->cpu.allocated, chunk_region);
|
||||
return NV_OK;
|
||||
}
|
||||
|
||||
uvm_cpu_chunk_t *uvm_cpu_chunk_get_chunk_for_page(uvm_va_block_t *va_block, uvm_page_index_t page_index)
|
||||
uvm_cpu_chunk_t *uvm_cpu_chunk_get_chunk_for_page(uvm_va_block_t *va_block, int nid, uvm_page_index_t page_index)
|
||||
{
|
||||
uvm_va_block_cpu_node_state_t *node_state;
|
||||
uvm_cpu_chunk_storage_mixed_t *mixed;
|
||||
uvm_cpu_chunk_t *chunk;
|
||||
uvm_cpu_chunk_t **chunks;
|
||||
size_t slot_index;
|
||||
|
||||
UVM_ASSERT(page_index < uvm_va_block_num_cpu_pages(va_block));
|
||||
if (!uvm_page_mask_test(&va_block->cpu.allocated, page_index))
|
||||
UVM_ASSERT(nid != NUMA_NO_NODE);
|
||||
node_state = block_node_state_get(va_block, nid);
|
||||
if (!uvm_page_mask_test(&node_state->allocated, page_index))
|
||||
return NULL;
|
||||
|
||||
UVM_ASSERT(va_block->cpu.chunks);
|
||||
UVM_ASSERT(node_state->chunks);
|
||||
|
||||
if (uvm_cpu_storage_get_type(va_block) == UVM_CPU_CHUNK_STORAGE_CHUNK) {
|
||||
return uvm_cpu_storage_get_ptr(va_block);
|
||||
if (uvm_cpu_storage_get_type(node_state) == UVM_CPU_CHUNK_STORAGE_CHUNK) {
|
||||
return uvm_cpu_storage_get_ptr(node_state);
|
||||
}
|
||||
else {
|
||||
mixed = uvm_cpu_storage_get_ptr(va_block);
|
||||
mixed = uvm_cpu_storage_get_ptr(node_state);
|
||||
slot_index = compute_slot_index(va_block, page_index);
|
||||
UVM_ASSERT(mixed->slots[slot_index] != NULL);
|
||||
if (test_bit(slot_index, mixed->big_chunks))
|
||||
@ -366,31 +397,43 @@ uvm_cpu_chunk_t *uvm_cpu_chunk_get_chunk_for_page(uvm_va_block_t *va_block, uvm_
|
||||
return chunk;
|
||||
}
|
||||
|
||||
void uvm_cpu_chunk_remove_from_block(uvm_va_block_t *va_block,
|
||||
uvm_page_index_t page_index)
|
||||
static uvm_cpu_chunk_t *uvm_cpu_chunk_get_chunk_for_page_resident(uvm_va_block_t *va_block, uvm_page_index_t page_index)
|
||||
{
|
||||
uvm_cpu_chunk_t *chunk = NULL;
|
||||
int nid = block_get_page_node_residency(va_block, page_index);
|
||||
|
||||
if (nid != NUMA_NO_NODE)
|
||||
chunk = uvm_cpu_chunk_get_chunk_for_page(va_block, nid, page_index);
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
void uvm_cpu_chunk_remove_from_block(uvm_va_block_t *va_block, int nid, uvm_page_index_t page_index)
|
||||
{
|
||||
uvm_va_block_cpu_node_state_t *node_state = block_node_state_get(va_block, nid);
|
||||
uvm_cpu_chunk_storage_mixed_t *mixed;
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(va_block, page_index);
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(va_block, nid, page_index);
|
||||
uvm_va_block_region_t chunk_region = uvm_cpu_chunk_block_region(va_block, chunk, page_index);
|
||||
size_t slot_index;
|
||||
uvm_cpu_chunk_t **chunks;
|
||||
int nid_iter;
|
||||
|
||||
// We want to protect against two threads manipulating the VA block's CPU
|
||||
// chunks at the same time. However, when a block is split, the new block's
|
||||
// lock is locked without tracking. So, we can't use
|
||||
// uvm_assert_mutex_locked().
|
||||
UVM_ASSERT(mutex_is_locked(&va_block->lock.m));
|
||||
UVM_ASSERT(va_block->cpu.chunks);
|
||||
UVM_ASSERT(node_state->chunks);
|
||||
UVM_ASSERT(uvm_va_block_region_num_pages(chunk_region) == uvm_cpu_chunk_num_pages(chunk));
|
||||
|
||||
if (uvm_cpu_storage_get_type(va_block) == UVM_CPU_CHUNK_STORAGE_CHUNK) {
|
||||
if (uvm_cpu_storage_get_type(node_state) == UVM_CPU_CHUNK_STORAGE_CHUNK) {
|
||||
UVM_ASSERT(uvm_cpu_chunk_get_size(chunk) == UVM_CHUNK_SIZE_2M);
|
||||
UVM_ASSERT(uvm_cpu_storage_get_ptr(va_block) == chunk);
|
||||
va_block->cpu.chunks = 0;
|
||||
UVM_ASSERT(uvm_cpu_storage_get_ptr(node_state) == chunk);
|
||||
node_state->chunks = 0;
|
||||
}
|
||||
else {
|
||||
UVM_ASSERT(uvm_cpu_chunk_get_size(chunk) != UVM_CHUNK_SIZE_2M);
|
||||
mixed = uvm_cpu_storage_get_ptr(va_block);
|
||||
mixed = uvm_cpu_storage_get_ptr(node_state);
|
||||
slot_index = compute_slot_index(va_block, page_index);
|
||||
UVM_ASSERT(mixed->slots[slot_index] != NULL);
|
||||
|
||||
@ -421,18 +464,22 @@ void uvm_cpu_chunk_remove_from_block(uvm_va_block_t *va_block,
|
||||
}
|
||||
}
|
||||
|
||||
uvm_page_mask_region_clear(&va_block->cpu.allocated, chunk_region);
|
||||
uvm_page_mask_region_clear(&node_state->allocated, chunk_region);
|
||||
uvm_page_mask_zero(&va_block->cpu.allocated);
|
||||
for_each_possible_uvm_node(nid_iter) {
|
||||
uvm_va_block_cpu_node_state_t *iter_node_state = block_node_state_get(va_block, nid_iter);
|
||||
uvm_page_mask_or(&va_block->cpu.allocated, &va_block->cpu.allocated, &iter_node_state->allocated);
|
||||
}
|
||||
|
||||
if (uvm_page_mask_empty(&va_block->cpu.allocated) && va_block->cpu.chunks) {
|
||||
uvm_kvfree(uvm_cpu_storage_get_ptr(va_block));
|
||||
va_block->cpu.chunks = 0;
|
||||
if (uvm_page_mask_empty(&node_state->allocated) && node_state->chunks) {
|
||||
uvm_kvfree(uvm_cpu_storage_get_ptr(node_state));
|
||||
node_state->chunks = 0;
|
||||
}
|
||||
}
|
||||
|
||||
struct page *uvm_cpu_chunk_get_cpu_page(uvm_va_block_t *va_block, uvm_page_index_t page_index)
|
||||
struct page *uvm_cpu_chunk_get_cpu_page(uvm_va_block_t *va_block, uvm_cpu_chunk_t *chunk, uvm_page_index_t page_index)
|
||||
{
|
||||
uvm_va_block_region_t chunk_region;
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(va_block, page_index);
|
||||
|
||||
UVM_ASSERT(chunk);
|
||||
UVM_ASSERT(chunk->page);
|
||||
@ -440,16 +487,28 @@ struct page *uvm_cpu_chunk_get_cpu_page(uvm_va_block_t *va_block, uvm_page_index
|
||||
return chunk->page + (page_index - chunk_region.first);
|
||||
}
|
||||
|
||||
struct page *uvm_va_block_get_cpu_page(uvm_va_block_t *va_block, uvm_page_index_t page_index)
|
||||
{
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page_resident(va_block, page_index);
|
||||
|
||||
return uvm_cpu_chunk_get_cpu_page(va_block, chunk, page_index);
|
||||
}
|
||||
|
||||
static uvm_cpu_chunk_t *uvm_cpu_chunk_first_in_region(uvm_va_block_t *va_block,
|
||||
uvm_va_block_region_t region,
|
||||
int nid,
|
||||
uvm_page_index_t *first_chunk_page)
|
||||
{
|
||||
uvm_cpu_chunk_t *chunk = NULL;
|
||||
uvm_page_index_t page_index;
|
||||
uvm_va_block_cpu_node_state_t *node_state = block_node_state_get(va_block, nid);
|
||||
|
||||
page_index = uvm_va_block_first_page_in_mask(region, &va_block->cpu.allocated);
|
||||
if (!node_state)
|
||||
return NULL;
|
||||
|
||||
page_index = uvm_va_block_first_page_in_mask(region, &node_state->allocated);
|
||||
if (page_index < region.outer)
|
||||
chunk = uvm_cpu_chunk_get_chunk_for_page(va_block, page_index);
|
||||
chunk = uvm_cpu_chunk_get_chunk_for_page(va_block, nid, page_index);
|
||||
|
||||
if (first_chunk_page && chunk) {
|
||||
uvm_va_block_region_t chunk_region = uvm_cpu_chunk_block_region(va_block, chunk, page_index);
|
||||
@ -459,33 +518,156 @@ static uvm_cpu_chunk_t *uvm_cpu_chunk_first_in_region(uvm_va_block_t *va_block,
|
||||
return chunk;
|
||||
}
|
||||
|
||||
#define for_each_cpu_chunk_in_block_region(chunk, page_index, va_block, region) \
|
||||
for ((chunk) = uvm_cpu_chunk_first_in_region((va_block), (region), &(page_index)); \
|
||||
(chunk) != NULL; \
|
||||
(chunk) = uvm_cpu_chunk_first_in_region((va_block), \
|
||||
uvm_va_block_region((page_index) + uvm_cpu_chunk_num_pages((chunk)), \
|
||||
(region).outer), \
|
||||
&(page_index)))
|
||||
static uvm_cpu_chunk_t *uvm_cpu_chunk_next_in_region(uvm_va_block_t *va_block,
|
||||
uvm_va_block_region_t region,
|
||||
int nid,
|
||||
uvm_page_index_t prev_page_index,
|
||||
uvm_page_index_t *next_chunk_page)
|
||||
{
|
||||
if (prev_page_index >= region.outer)
|
||||
return NULL;
|
||||
|
||||
#define for_each_cpu_chunk_in_block_region_safe(chunk, page_index, next_page_index, va_block, region) \
|
||||
for ((chunk) = uvm_cpu_chunk_first_in_region((va_block), (region), &(page_index)), \
|
||||
(next_page_index) = (page_index) + (chunk ? uvm_cpu_chunk_num_pages(chunk) : 0); \
|
||||
(chunk) != NULL; \
|
||||
(chunk) = uvm_cpu_chunk_first_in_region((va_block), \
|
||||
uvm_va_block_region((next_page_index), (region).outer), \
|
||||
&(page_index)), \
|
||||
(next_page_index) = (page_index) + ((chunk) ? uvm_cpu_chunk_num_pages((chunk)) : 0))
|
||||
return uvm_cpu_chunk_first_in_region(va_block,
|
||||
uvm_va_block_region(prev_page_index, region.outer),
|
||||
nid, next_chunk_page);
|
||||
}
|
||||
|
||||
#define for_each_cpu_chunk_in_block(chunk, page_index, va_block) \
|
||||
for_each_cpu_chunk_in_block_region((chunk), (page_index), (va_block), uvm_va_block_region_from_block((va_block)))
|
||||
#define for_each_cpu_chunk_in_block_region(chunk, chunk_start, va_block, nid, region) \
|
||||
for ((chunk) = uvm_cpu_chunk_first_in_region((va_block), (region), (nid), &(chunk_start)); \
|
||||
(chunk) != NULL; \
|
||||
(chunk) = uvm_cpu_chunk_next_in_region((va_block), \
|
||||
(region), \
|
||||
(nid), \
|
||||
(chunk_start) + uvm_cpu_chunk_num_pages((chunk)), \
|
||||
&(chunk_start)))
|
||||
|
||||
#define for_each_cpu_chunk_in_block_safe(chunk, page_index, next_page_index, va_block) \
|
||||
for_each_cpu_chunk_in_block_region_safe((chunk), \
|
||||
(page_index), \
|
||||
(next_page_index), \
|
||||
(va_block), \
|
||||
#define for_each_cpu_chunk_in_block_region_safe(chunk, chunk_start, next_chunk_start, va_block, nid, region) \
|
||||
for ((chunk) = uvm_cpu_chunk_first_in_region((va_block), (region), (nid), &(chunk_start)), \
|
||||
(next_chunk_start) = (chunk_start) + (chunk ? uvm_cpu_chunk_num_pages(chunk) : 0); \
|
||||
(chunk) != NULL; \
|
||||
(chunk) = uvm_cpu_chunk_next_in_region((va_block), (region), (nid), (next_chunk_start), &(chunk_start)), \
|
||||
(next_chunk_start) = (chunk_start) + ((chunk) ? uvm_cpu_chunk_num_pages((chunk)) : 0))
|
||||
|
||||
#define for_each_cpu_chunk_in_block(chunk, chunk_start, va_block, nid) \
|
||||
for_each_cpu_chunk_in_block_region((chunk), \
|
||||
(chunk_start), \
|
||||
(va_block), \
|
||||
(nid), \
|
||||
uvm_va_block_region_from_block((va_block)))
|
||||
|
||||
#define for_each_cpu_chunk_in_block_safe(chunk, chunk_start, next_chunk_start, va_block, nid) \
|
||||
for_each_cpu_chunk_in_block_region_safe((chunk), \
|
||||
(chunk_start), \
|
||||
(next_chunk_start), \
|
||||
(va_block), \
|
||||
(nid), \
|
||||
uvm_va_block_region_from_block((va_block)))
|
||||
|
||||
static void block_update_cpu_resident_mask(uvm_va_block_t *va_block)
|
||||
{
|
||||
int nid;
|
||||
|
||||
uvm_page_mask_zero(&va_block->cpu.resident);
|
||||
for_each_possible_uvm_node(nid) {
|
||||
uvm_va_block_cpu_node_state_t *node_state = block_node_state_get(va_block, nid);
|
||||
uvm_page_mask_or(&va_block->cpu.resident, &va_block->cpu.resident, &node_state->resident);
|
||||
}
|
||||
}
|
||||
|
||||
void uvm_va_block_cpu_set_resident_page(uvm_va_block_t *va_block, int nid, uvm_page_index_t page_index)
|
||||
{
|
||||
uvm_va_block_cpu_node_state_t *node_state;
|
||||
|
||||
node_state = block_node_state_get(va_block, nid);
|
||||
UVM_ASSERT(node_state);
|
||||
UVM_ASSERT(uvm_page_mask_test(&node_state->allocated, page_index));
|
||||
uvm_page_mask_set(&node_state->resident, page_index);
|
||||
uvm_page_mask_set(&va_block->cpu.resident, page_index);
|
||||
uvm_processor_mask_set(&va_block->resident, UVM_ID_CPU);
|
||||
}
|
||||
|
||||
// Set all CPU pages in the mask as resident on NUMA node nid.
|
||||
// nid cannot be NUMA_NO_NODE.
|
||||
static void uvm_va_block_cpu_set_resident_mask(uvm_va_block_t *va_block, int nid, const uvm_page_mask_t *mask)
|
||||
{
|
||||
uvm_va_block_cpu_node_state_t *node_state;
|
||||
|
||||
node_state = block_node_state_get(va_block, nid);
|
||||
UVM_ASSERT(node_state);
|
||||
UVM_ASSERT(uvm_page_mask_subset(mask, &node_state->allocated));
|
||||
uvm_page_mask_or(&node_state->resident, &node_state->resident, mask);
|
||||
uvm_page_mask_or(&va_block->cpu.resident, &va_block->cpu.resident, mask);
|
||||
}
|
||||
|
||||
static void uvm_va_block_cpu_set_resident_all_chunks(uvm_va_block_t *va_block,
|
||||
uvm_va_block_context_t *va_block_context,
|
||||
const uvm_page_mask_t *page_mask)
|
||||
{
|
||||
uvm_make_resident_page_tracking_t *tracking = &va_block_context->make_resident.cpu_pages_used;
|
||||
uvm_page_mask_t *node_pages_mask = &va_block_context->make_resident.node_pages_mask;
|
||||
uvm_page_mask_t *page_mask_copy = &va_block_context->scratch_page_mask;
|
||||
int nid;
|
||||
|
||||
if (uvm_page_mask_empty(page_mask))
|
||||
return;
|
||||
|
||||
uvm_page_mask_copy(page_mask_copy, page_mask);
|
||||
for_each_node_mask(nid, tracking->nodes) {
|
||||
size_t index = node_to_index(nid);
|
||||
|
||||
if (uvm_page_mask_and(node_pages_mask, page_mask_copy, tracking->node_masks[index])) {
|
||||
uvm_va_block_cpu_set_resident_mask(va_block, nid, node_pages_mask);
|
||||
uvm_page_mask_andnot(page_mask_copy, page_mask_copy, node_pages_mask);
|
||||
}
|
||||
}
|
||||
|
||||
UVM_ASSERT(uvm_page_mask_empty(page_mask_copy));
|
||||
}
|
||||
|
||||
// Clear residency for all CPU pages in the mask.
|
||||
// nid cannot be NUMA_NO_NODE.
|
||||
static void uvm_va_block_cpu_clear_resident_mask(uvm_va_block_t *va_block, int nid, const uvm_page_mask_t *mask)
|
||||
{
|
||||
uvm_va_block_cpu_node_state_t *node_state;
|
||||
|
||||
node_state = block_node_state_get(va_block, nid);
|
||||
UVM_ASSERT(node_state);
|
||||
uvm_page_mask_andnot(&node_state->resident, &node_state->resident, mask);
|
||||
block_update_cpu_resident_mask(va_block);
|
||||
}
|
||||
|
||||
static void uvm_va_block_cpu_clear_resident_region(uvm_va_block_t *va_block, int nid, uvm_va_block_region_t region)
|
||||
{
|
||||
uvm_va_block_cpu_node_state_t *node_state;
|
||||
|
||||
node_state = block_node_state_get(va_block, nid);
|
||||
UVM_ASSERT(node_state);
|
||||
uvm_page_mask_region_clear(&node_state->resident, region);
|
||||
block_update_cpu_resident_mask(va_block);
|
||||
}
|
||||
|
||||
bool uvm_va_block_cpu_is_page_resident_on(uvm_va_block_t *va_block, int nid, uvm_page_index_t page_index)
|
||||
{
|
||||
uvm_page_mask_t *resident_mask = uvm_va_block_resident_mask_get(va_block, UVM_ID_CPU, nid);
|
||||
|
||||
return uvm_page_mask_test(resident_mask, page_index);
|
||||
}
|
||||
|
||||
bool uvm_va_block_cpu_is_region_resident_on(uvm_va_block_t *va_block, int nid, uvm_va_block_region_t region)
|
||||
{
|
||||
uvm_page_mask_t *resident_mask = uvm_va_block_resident_mask_get(va_block, UVM_ID_CPU, nid);
|
||||
|
||||
return uvm_page_mask_region_full(resident_mask, region);
|
||||
}
|
||||
|
||||
int uvm_va_block_context_get_node(uvm_va_block_context_t *va_block_context)
|
||||
{
|
||||
if (va_block_context->make_resident.dest_nid != NUMA_NO_NODE)
|
||||
return va_block_context->make_resident.dest_nid;
|
||||
|
||||
return numa_mem_id();
|
||||
}
|
||||
|
||||
struct vm_area_struct *uvm_va_block_find_vma_region(uvm_va_block_t *va_block,
|
||||
struct mm_struct *mm,
|
||||
NvU64 start,
|
||||
@ -515,29 +697,48 @@ struct vm_area_struct *uvm_va_block_find_vma_region(uvm_va_block_t *va_block,
|
||||
|
||||
static bool block_check_cpu_chunks(uvm_va_block_t *block)
|
||||
{
|
||||
uvm_cpu_chunk_t *chunk;
|
||||
size_t alloced_pages = 0;
|
||||
uvm_va_block_region_t prev_region = { 0 };
|
||||
uvm_page_index_t page_index;
|
||||
int nid;
|
||||
uvm_page_mask_t *temp_resident_mask;
|
||||
|
||||
for_each_cpu_chunk_in_block(chunk, page_index, block) {
|
||||
uvm_va_block_region_t chunk_region = uvm_cpu_chunk_block_region(block, chunk, page_index);
|
||||
size_t num_chunk_pages = uvm_cpu_chunk_num_pages(chunk);
|
||||
uvm_page_index_t chunk_page;
|
||||
temp_resident_mask = kmem_cache_alloc(g_uvm_page_mask_cache, NV_UVM_GFP_FLAGS | __GFP_ZERO);
|
||||
|
||||
UVM_ASSERT(prev_region.outer <= chunk_region.first);
|
||||
UVM_ASSERT(IS_ALIGNED(uvm_va_block_region_start(block, chunk_region), uvm_cpu_chunk_get_size(chunk)));
|
||||
UVM_ASSERT(chunk_region.outer <= uvm_va_block_num_cpu_pages(block));
|
||||
for_each_possible_uvm_node(nid) {
|
||||
uvm_cpu_chunk_t *chunk;
|
||||
uvm_page_index_t page_index;
|
||||
uvm_va_block_region_t prev_region = {0};
|
||||
uvm_va_block_cpu_node_state_t *node_state = block_node_state_get(block, nid);
|
||||
size_t alloced_pages = 0;
|
||||
|
||||
alloced_pages += uvm_cpu_chunk_num_pages(chunk);
|
||||
UVM_ASSERT(uvm_page_mask_region_full(&block->cpu.allocated, chunk_region));
|
||||
prev_region = chunk_region;
|
||||
for_each_cpu_chunk_in_block(chunk, page_index, block, nid) {
|
||||
uvm_va_block_region_t chunk_region = uvm_cpu_chunk_block_region(block, chunk, page_index);
|
||||
size_t num_chunk_pages = uvm_cpu_chunk_num_pages(chunk);
|
||||
uvm_page_index_t chunk_page;
|
||||
|
||||
for (chunk_page = page_index; chunk_page < page_index + num_chunk_pages; chunk_page++)
|
||||
UVM_ASSERT(uvm_cpu_chunk_get_chunk_for_page(block, chunk_page) == chunk);
|
||||
UVM_ASSERT(prev_region.outer <= chunk_region.first);
|
||||
UVM_ASSERT(IS_ALIGNED(uvm_va_block_region_start(block, chunk_region), uvm_cpu_chunk_get_size(chunk)));
|
||||
UVM_ASSERT(chunk_region.outer <= uvm_va_block_num_cpu_pages(block));
|
||||
|
||||
alloced_pages += uvm_cpu_chunk_num_pages(chunk);
|
||||
UVM_ASSERT(uvm_page_mask_region_full(&node_state->allocated, chunk_region));
|
||||
prev_region = chunk_region;
|
||||
|
||||
for (chunk_page = page_index; chunk_page < page_index + num_chunk_pages; chunk_page++)
|
||||
UVM_ASSERT(uvm_cpu_chunk_get_chunk_for_page(block, nid, chunk_page) == chunk);
|
||||
}
|
||||
|
||||
UVM_ASSERT(alloced_pages == uvm_page_mask_weight(&node_state->allocated));
|
||||
UVM_ASSERT(uvm_page_mask_subset(&node_state->resident, &node_state->allocated));
|
||||
UVM_ASSERT(uvm_page_mask_subset(&node_state->resident, &block->cpu.resident));
|
||||
if (temp_resident_mask && !uvm_page_mask_empty(&node_state->resident)) {
|
||||
UVM_ASSERT(!uvm_page_mask_intersects(&node_state->resident, temp_resident_mask));
|
||||
uvm_page_mask_or(temp_resident_mask, temp_resident_mask, &node_state->resident);
|
||||
}
|
||||
}
|
||||
|
||||
UVM_ASSERT(alloced_pages == uvm_page_mask_weight(&block->cpu.allocated));
|
||||
if (temp_resident_mask) {
|
||||
UVM_ASSERT(uvm_page_mask_equal(temp_resident_mask, &block->cpu.resident));
|
||||
kmem_cache_free(g_uvm_page_mask_cache, temp_resident_mask);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -607,11 +808,17 @@ typedef struct
|
||||
|
||||
// The page index
|
||||
uvm_page_index_t page_index;
|
||||
|
||||
// If processor is the CPU, the NUMA node of the page.
|
||||
int nid;
|
||||
} block_phys_page_t;
|
||||
|
||||
static block_phys_page_t block_phys_page(uvm_processor_id_t processor, uvm_page_index_t page_index)
|
||||
static block_phys_page_t block_phys_page(uvm_processor_id_t processor, int nid, uvm_page_index_t page_index)
|
||||
{
|
||||
return (block_phys_page_t){ processor, page_index };
|
||||
if (UVM_ID_IS_CPU(processor))
|
||||
UVM_ASSERT(nid != NUMA_NO_NODE);
|
||||
|
||||
return (block_phys_page_t){ processor, page_index, nid };
|
||||
}
|
||||
|
||||
NV_STATUS uvm_va_block_init(void)
|
||||
@ -636,30 +843,102 @@ NV_STATUS uvm_va_block_init(void)
|
||||
if (!g_uvm_va_block_context_cache)
|
||||
return NV_ERR_NO_MEMORY;
|
||||
|
||||
g_uvm_va_block_cpu_node_state_cache = NV_KMEM_CACHE_CREATE("uvm_va_block_cpu_node_state_t",
|
||||
uvm_va_block_cpu_node_state_t);
|
||||
if (!g_uvm_va_block_cpu_node_state_cache)
|
||||
return NV_ERR_NO_MEMORY;
|
||||
|
||||
return NV_OK;
|
||||
}
|
||||
|
||||
void uvm_va_block_exit(void)
|
||||
{
|
||||
kmem_cache_destroy_safe(&g_uvm_va_block_cpu_node_state_cache);
|
||||
kmem_cache_destroy_safe(&g_uvm_va_block_context_cache);
|
||||
kmem_cache_destroy_safe(&g_uvm_page_mask_cache);
|
||||
kmem_cache_destroy_safe(&g_uvm_va_block_gpu_state_cache);
|
||||
kmem_cache_destroy_safe(&g_uvm_va_block_cache);
|
||||
}
|
||||
|
||||
static void block_context_free_tracking(uvm_make_resident_page_tracking_t *tracking)
|
||||
{
|
||||
size_t index;
|
||||
|
||||
for (index = 0; index < num_possible_nodes(); index++) {
|
||||
if (tracking->node_masks[index])
|
||||
kmem_cache_free(g_uvm_page_mask_cache, tracking->node_masks[index]);
|
||||
}
|
||||
|
||||
uvm_kvfree(tracking->node_masks);
|
||||
}
|
||||
|
||||
static NV_STATUS block_context_alloc_tracking(uvm_make_resident_page_tracking_t *tracking)
|
||||
{
|
||||
size_t index;
|
||||
|
||||
tracking->node_masks = uvm_kvmalloc_zero(num_possible_nodes() * sizeof(*tracking->node_masks));
|
||||
if (!tracking->node_masks)
|
||||
return NV_ERR_NO_MEMORY;
|
||||
|
||||
for (index = 0; index < num_possible_nodes(); index++) {
|
||||
tracking->node_masks[index] = kmem_cache_alloc(g_uvm_page_mask_cache, NV_UVM_GFP_FLAGS);
|
||||
if (!tracking->node_masks[index])
|
||||
goto error;
|
||||
}
|
||||
|
||||
return NV_OK;
|
||||
|
||||
error:
|
||||
block_context_free_tracking(tracking);
|
||||
return NV_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
uvm_va_block_context_t *uvm_va_block_context_alloc(struct mm_struct *mm)
|
||||
{
|
||||
uvm_va_block_context_t *block_context = kmem_cache_alloc(g_uvm_va_block_context_cache, NV_UVM_GFP_FLAGS);
|
||||
if (block_context)
|
||||
uvm_va_block_context_init(block_context, mm);
|
||||
NV_STATUS status;
|
||||
|
||||
if (!block_context)
|
||||
return NULL;
|
||||
|
||||
status = block_context_alloc_tracking(&block_context->make_resident.cpu_pages_used);
|
||||
if (status != NV_OK) {
|
||||
kmem_cache_free(g_uvm_va_block_context_cache, block_context);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uvm_va_block_context_init(block_context, mm);
|
||||
return block_context;
|
||||
}
|
||||
|
||||
void uvm_va_block_context_init(uvm_va_block_context_t *va_block_context, struct mm_struct *mm)
|
||||
{
|
||||
UVM_ASSERT(va_block_context);
|
||||
|
||||
// Write garbage into the VA Block context to ensure that the UVM code
|
||||
// clears masks appropriately
|
||||
if (UVM_IS_DEBUG()) {
|
||||
uvm_page_mask_t **mask_array = va_block_context->make_resident.cpu_pages_used.node_masks;
|
||||
int nid;
|
||||
|
||||
memset(va_block_context, 0xff, sizeof(*va_block_context));
|
||||
|
||||
for_each_possible_uvm_node(nid)
|
||||
uvm_page_mask_fill(mask_array[node_to_index(nid)]);
|
||||
|
||||
va_block_context->make_resident.cpu_pages_used.node_masks = mask_array;
|
||||
}
|
||||
|
||||
va_block_context->mm = mm;
|
||||
va_block_context->make_resident.dest_nid = NUMA_NO_NODE;
|
||||
}
|
||||
|
||||
void uvm_va_block_context_free(uvm_va_block_context_t *va_block_context)
|
||||
{
|
||||
if (va_block_context)
|
||||
if (va_block_context) {
|
||||
block_context_free_tracking(&va_block_context->make_resident.cpu_pages_used);
|
||||
kmem_cache_free(g_uvm_va_block_context_cache, va_block_context);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert from page_index to chunk_index. The goal is for each system page in
|
||||
@ -884,6 +1163,18 @@ uvm_gpu_chunk_t *uvm_va_block_lookup_gpu_chunk(uvm_va_block_t *va_block, uvm_gpu
|
||||
return gpu_state->chunks[chunk_index];
|
||||
}
|
||||
|
||||
static void uvm_va_block_free(uvm_va_block_t *block)
|
||||
{
|
||||
if (uvm_enable_builtin_tests) {
|
||||
uvm_va_block_wrapper_t *block_wrapper = container_of(block, uvm_va_block_wrapper_t, block);
|
||||
|
||||
kmem_cache_free(g_uvm_va_block_cache, block_wrapper);
|
||||
}
|
||||
else {
|
||||
kmem_cache_free(g_uvm_va_block_cache, block);
|
||||
}
|
||||
}
|
||||
|
||||
NV_STATUS uvm_va_block_create(uvm_va_range_t *va_range,
|
||||
NvU64 start,
|
||||
NvU64 end,
|
||||
@ -891,6 +1182,7 @@ NV_STATUS uvm_va_block_create(uvm_va_range_t *va_range,
|
||||
{
|
||||
uvm_va_block_t *block = NULL;
|
||||
NvU64 size = end - start + 1;
|
||||
int nid;
|
||||
|
||||
UVM_ASSERT(PAGE_ALIGNED(start));
|
||||
UVM_ASSERT(PAGE_ALIGNED(end + 1));
|
||||
@ -911,8 +1203,11 @@ NV_STATUS uvm_va_block_create(uvm_va_range_t *va_range,
|
||||
if (uvm_enable_builtin_tests) {
|
||||
uvm_va_block_wrapper_t *block_wrapper = nv_kmem_cache_zalloc(g_uvm_va_block_cache, NV_UVM_GFP_FLAGS);
|
||||
|
||||
if (block_wrapper)
|
||||
if (block_wrapper) {
|
||||
block = &block_wrapper->block;
|
||||
block_wrapper->test.cpu_chunk_allocation_target_id = NUMA_NO_NODE;
|
||||
block_wrapper->test.cpu_chunk_allocation_actual_id = NUMA_NO_NODE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
block = nv_kmem_cache_zalloc(g_uvm_va_block_cache, NV_UVM_GFP_FLAGS);
|
||||
@ -921,6 +1216,18 @@ NV_STATUS uvm_va_block_create(uvm_va_range_t *va_range,
|
||||
if (!block)
|
||||
return NV_ERR_NO_MEMORY;
|
||||
|
||||
block->cpu.node_state = uvm_kvmalloc_zero(sizeof(*block->cpu.node_state) * num_possible_nodes());
|
||||
if (!block->cpu.node_state)
|
||||
goto error_block_free;
|
||||
|
||||
for_each_possible_uvm_node(nid) {
|
||||
size_t index = node_to_index(nid);
|
||||
|
||||
block->cpu.node_state[index] = nv_kmem_cache_zalloc(g_uvm_va_block_cpu_node_state_cache, NV_UVM_GFP_FLAGS);
|
||||
if (!block->cpu.node_state[index])
|
||||
goto error;
|
||||
}
|
||||
|
||||
nv_kref_init(&block->kref);
|
||||
uvm_mutex_init(&block->lock, UVM_LOCK_ORDER_VA_BLOCK);
|
||||
block->start = start;
|
||||
@ -933,6 +1240,20 @@ NV_STATUS uvm_va_block_create(uvm_va_range_t *va_range,
|
||||
|
||||
*out_block = block;
|
||||
return NV_OK;
|
||||
|
||||
error:
|
||||
for_each_possible_uvm_node(nid) {
|
||||
size_t index = node_to_index(nid);
|
||||
|
||||
if (block->cpu.node_state[index])
|
||||
kmem_cache_free(g_uvm_va_block_cpu_node_state_cache, block->cpu.node_state[index]);
|
||||
}
|
||||
|
||||
uvm_kvfree(block->cpu.node_state);
|
||||
|
||||
error_block_free:
|
||||
uvm_va_block_free(block);
|
||||
return NV_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
static void cpu_chunk_remove_sysmem_gpu_mapping(uvm_cpu_chunk_t *chunk, uvm_gpu_t *gpu)
|
||||
@ -984,9 +1305,12 @@ static void block_gpu_unmap_phys_all_cpu_pages(uvm_va_block_t *block, uvm_gpu_t
|
||||
{
|
||||
uvm_cpu_chunk_t *chunk;
|
||||
uvm_page_index_t page_index;
|
||||
int nid;
|
||||
|
||||
for_each_cpu_chunk_in_block(chunk, page_index, block)
|
||||
cpu_chunk_remove_sysmem_gpu_mapping(chunk, gpu);
|
||||
for_each_possible_uvm_node(nid) {
|
||||
for_each_cpu_chunk_in_block(chunk, page_index, block, nid)
|
||||
cpu_chunk_remove_sysmem_gpu_mapping(chunk, gpu);
|
||||
}
|
||||
}
|
||||
|
||||
static NV_STATUS block_gpu_map_phys_all_cpu_pages(uvm_va_block_t *block, uvm_gpu_t *gpu)
|
||||
@ -995,18 +1319,21 @@ static NV_STATUS block_gpu_map_phys_all_cpu_pages(uvm_va_block_t *block, uvm_gpu
|
||||
uvm_cpu_chunk_t *chunk;
|
||||
NvU64 block_mapping_size = uvm_va_block_size(block);
|
||||
uvm_page_index_t page_index;
|
||||
int nid;
|
||||
|
||||
UVM_ASSERT(IS_ALIGNED(block_mapping_size, UVM_PAGE_SIZE_4K));
|
||||
|
||||
for_each_cpu_chunk_in_block(chunk, page_index, block) {
|
||||
UVM_ASSERT_MSG(uvm_cpu_chunk_get_gpu_phys_addr(chunk, gpu->parent) == 0,
|
||||
"GPU%u DMA address 0x%llx\n",
|
||||
uvm_id_value(gpu->id),
|
||||
uvm_cpu_chunk_get_gpu_phys_addr(chunk, gpu->parent));
|
||||
for_each_possible_uvm_node(nid) {
|
||||
for_each_cpu_chunk_in_block(chunk, page_index, block, nid) {
|
||||
UVM_ASSERT_MSG(uvm_cpu_chunk_get_gpu_phys_addr(chunk, gpu->parent) == 0,
|
||||
"GPU%u DMA address 0x%llx\n",
|
||||
uvm_id_value(gpu->id),
|
||||
uvm_cpu_chunk_get_gpu_phys_addr(chunk, gpu->parent));
|
||||
|
||||
status = cpu_chunk_add_sysmem_gpu_mapping(chunk, block, page_index, gpu);
|
||||
if (status != NV_OK)
|
||||
goto error;
|
||||
status = cpu_chunk_add_sysmem_gpu_mapping(chunk, block, page_index, gpu);
|
||||
if (status != NV_OK)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return NV_OK;
|
||||
@ -1176,11 +1503,11 @@ void uvm_va_block_unmap_cpu_chunk_on_gpus(uvm_va_block_t *block,
|
||||
}
|
||||
|
||||
NV_STATUS uvm_va_block_map_cpu_chunk_on_gpus(uvm_va_block_t *block,
|
||||
uvm_cpu_chunk_t *chunk,
|
||||
uvm_page_index_t page_index)
|
||||
{
|
||||
NV_STATUS status;
|
||||
uvm_gpu_id_t id;
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(block, page_index);
|
||||
uvm_chunk_size_t chunk_size = uvm_cpu_chunk_get_size(chunk);
|
||||
uvm_va_block_region_t chunk_region = uvm_va_block_chunk_region(block, chunk_size, page_index);
|
||||
|
||||
@ -1213,21 +1540,25 @@ void uvm_va_block_remove_cpu_chunks(uvm_va_block_t *va_block, uvm_va_block_regio
|
||||
uvm_cpu_chunk_t *chunk;
|
||||
uvm_page_index_t page_index, next_page_index;
|
||||
uvm_va_block_region_t chunk_region;
|
||||
int nid;
|
||||
|
||||
for_each_cpu_chunk_in_block_region_safe(chunk, page_index, next_page_index, va_block, region) {
|
||||
chunk_region = uvm_va_block_region(page_index, page_index + uvm_cpu_chunk_num_pages(chunk));
|
||||
for_each_possible_uvm_node(nid) {
|
||||
for_each_cpu_chunk_in_block_region_safe(chunk, page_index, next_page_index, va_block, nid, region) {
|
||||
chunk_region = uvm_va_block_region(page_index, page_index + uvm_cpu_chunk_num_pages(chunk));
|
||||
|
||||
uvm_page_mask_region_clear(&va_block->cpu.pte_bits[UVM_PTE_BITS_CPU_READ], chunk_region);
|
||||
uvm_page_mask_region_clear(&va_block->cpu.pte_bits[UVM_PTE_BITS_CPU_WRITE], chunk_region);
|
||||
uvm_page_mask_region_clear(&va_block->cpu.resident, chunk_region);
|
||||
uvm_cpu_chunk_remove_from_block(va_block, page_index);
|
||||
uvm_va_block_unmap_cpu_chunk_on_gpus(va_block, chunk, page_index);
|
||||
uvm_cpu_chunk_free(chunk);
|
||||
uvm_page_mask_region_clear(&va_block->cpu.pte_bits[UVM_PTE_BITS_CPU_READ], chunk_region);
|
||||
uvm_page_mask_region_clear(&va_block->cpu.pte_bits[UVM_PTE_BITS_CPU_WRITE], chunk_region);
|
||||
uvm_va_block_cpu_clear_resident_region(va_block, nid, chunk_region);
|
||||
uvm_cpu_chunk_remove_from_block(va_block, nid, page_index);
|
||||
uvm_va_block_unmap_cpu_chunk_on_gpus(va_block, chunk, page_index);
|
||||
uvm_cpu_chunk_free(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
if (uvm_page_mask_empty(&va_block->cpu.pte_bits[UVM_PTE_BITS_CPU_READ]))
|
||||
uvm_processor_mask_clear(&va_block->mapped, UVM_ID_CPU);
|
||||
if (uvm_page_mask_empty(&va_block->cpu.resident))
|
||||
|
||||
if (uvm_page_mask_empty(uvm_va_block_resident_mask_get(va_block, UVM_ID_CPU, NUMA_NO_NODE)))
|
||||
uvm_processor_mask_clear(&va_block->resident, UVM_ID_CPU);
|
||||
}
|
||||
|
||||
@ -1291,25 +1622,25 @@ static void block_unmap_indirect_peers_from_gpu_chunk(uvm_va_block_t *block, uvm
|
||||
}
|
||||
|
||||
// Mark a CPU page as dirty.
|
||||
static void block_mark_cpu_page_dirty(uvm_va_block_t *block, uvm_page_index_t page_index)
|
||||
static void block_mark_cpu_page_dirty(uvm_va_block_t *block, uvm_page_index_t page_index, int nid)
|
||||
{
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(block, page_index);
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(block, nid, page_index);
|
||||
uvm_va_block_region_t chunk_region = uvm_va_block_chunk_region(block, uvm_cpu_chunk_get_size(chunk), page_index);
|
||||
uvm_cpu_chunk_mark_dirty(chunk, page_index - chunk_region.first);
|
||||
}
|
||||
|
||||
// Mark a CPU page as clean.
|
||||
static void block_mark_cpu_page_clean(uvm_va_block_t *block, uvm_page_index_t page_index)
|
||||
static void block_mark_cpu_page_clean(uvm_va_block_t *block, uvm_page_index_t page_index, int nid)
|
||||
{
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(block, page_index);
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(block, nid, page_index);
|
||||
uvm_va_block_region_t chunk_region = uvm_va_block_chunk_region(block, uvm_cpu_chunk_get_size(chunk), page_index);
|
||||
uvm_cpu_chunk_mark_clean(chunk, page_index - chunk_region.first);
|
||||
}
|
||||
|
||||
// Check if a CPU page is dirty.
|
||||
static bool block_cpu_page_is_dirty(uvm_va_block_t *block, uvm_page_index_t page_index)
|
||||
static bool block_cpu_page_is_dirty(uvm_va_block_t *block, uvm_page_index_t page_index, int nid)
|
||||
{
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(block, page_index);
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(block, nid, page_index);
|
||||
uvm_va_block_region_t chunk_region = uvm_va_block_chunk_region(block, uvm_cpu_chunk_get_size(chunk), page_index);
|
||||
return uvm_cpu_chunk_is_dirty(chunk, page_index - chunk_region.first);
|
||||
}
|
||||
@ -1317,21 +1648,177 @@ static bool block_cpu_page_is_dirty(uvm_va_block_t *block, uvm_page_index_t page
|
||||
static NV_STATUS block_alloc_cpu_chunk(uvm_va_block_t *block,
|
||||
uvm_chunk_size_t alloc_size,
|
||||
uvm_cpu_chunk_alloc_flags_t flags,
|
||||
int nid,
|
||||
uvm_cpu_chunk_t **chunk)
|
||||
{
|
||||
uvm_va_block_test_t *block_test = uvm_va_block_get_test(block);
|
||||
|
||||
// Return out of memory error if the tests have requested it. As opposed to
|
||||
// other error injection settings, this one fails N times and then succeeds.
|
||||
// TODO: Bug 3701182: This will print a warning in Linux kernels newer than
|
||||
// 5.16.0-rc1+.
|
||||
if (block_test && block_test->inject_cpu_pages_allocation_error_count) {
|
||||
if (block_test->inject_cpu_pages_allocation_error_count != ~(NvU32)0)
|
||||
block_test->inject_cpu_pages_allocation_error_count--;
|
||||
if (block_test) {
|
||||
// Return out of memory error if the tests have requested it. As opposed
|
||||
// to other error injection settings, this one fails N times and then
|
||||
// succeeds.
|
||||
// TODO: Bug 3701182: This will print a warning in Linux kernels newer
|
||||
// than 5.16.0-rc1+.
|
||||
if (block_test->inject_cpu_pages_allocation_error_count) {
|
||||
if (block_test->inject_cpu_pages_allocation_error_count != ~(NvU32)0)
|
||||
block_test->inject_cpu_pages_allocation_error_count--;
|
||||
return NV_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (block_test->cpu_chunk_allocation_actual_id != NUMA_NO_NODE)
|
||||
nid = block_test->cpu_chunk_allocation_actual_id;
|
||||
}
|
||||
|
||||
return uvm_cpu_chunk_alloc(alloc_size, flags, nid, chunk);
|
||||
}
|
||||
|
||||
// Handle insertion of overlapping CPU chunks.
|
||||
// In cases where the kernel allocates CPU chunks on NUMA nodes that already
|
||||
// have existing chunks, it's possible that the newly allocated chunk overlaps
|
||||
// existing chunks.
|
||||
// In such cases, the newly allocated chunk has to be appropriately split and
|
||||
// only the non-overlapping subchunks inserted into the block.
|
||||
// The subchunks, which are not inserted are freed.
|
||||
// If there is an error during split, insertion, or mapping, any sub-chunks that
|
||||
// have already been successfully inserted will remain in the block. The rest of
|
||||
// the sub-chunks will be freed in order to maintain proper refcounts on the
|
||||
// parent chunk.
|
||||
static NV_STATUS block_populate_overlapping_cpu_chunks(uvm_va_block_t *block,
|
||||
uvm_va_block_context_t *block_context,
|
||||
uvm_cpu_chunk_t *chunk,
|
||||
uvm_page_index_t page_index)
|
||||
{
|
||||
int nid = uvm_cpu_chunk_get_numa_node(chunk);
|
||||
uvm_va_block_cpu_node_state_t *node_state = block_node_state_get(block, nid);
|
||||
uvm_chunk_size_t chunk_size = uvm_cpu_chunk_get_size(chunk);
|
||||
uvm_va_block_region_t chunk_region = uvm_va_block_chunk_region(block, chunk_size, page_index);
|
||||
uvm_page_index_t running_page_index;
|
||||
uvm_cpu_chunk_t **split_chunks;
|
||||
uvm_cpu_chunk_t **small_chunks = NULL;
|
||||
uvm_cpu_chunk_t *chunk_ptr;
|
||||
uvm_page_mask_t *node_pages_mask = &block_context->make_resident.node_pages_mask;
|
||||
uvm_chunk_size_t split_size;
|
||||
size_t i;
|
||||
NV_STATUS status;
|
||||
|
||||
UVM_ASSERT(IS_ALIGNED(uvm_va_block_cpu_page_address(block, page_index), chunk_size));
|
||||
|
||||
// Get a mask of all the chunk pages that are not overlapping existing
|
||||
// chunks.
|
||||
uvm_page_mask_init_from_region(node_pages_mask, chunk_region, NULL);
|
||||
uvm_page_mask_andnot(node_pages_mask, node_pages_mask, &node_state->allocated);
|
||||
|
||||
split_size = uvm_chunk_find_prev_size(uvm_cpu_chunk_get_allocation_sizes(), chunk_size);
|
||||
split_chunks = uvm_kvmalloc_zero((chunk_size / split_size) * sizeof(*split_chunks));
|
||||
if (!split_chunks) {
|
||||
uvm_cpu_chunk_free(chunk);
|
||||
return NV_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
return uvm_cpu_chunk_alloc(alloc_size, flags, chunk);
|
||||
if (split_size > UVM_PAGE_SIZE_4K) {
|
||||
small_chunks = uvm_kvmalloc_zero(MAX_SMALL_CHUNKS_PER_BIG_SLOT * sizeof(*small_chunks));
|
||||
if (!small_chunks) {
|
||||
uvm_kvfree(split_chunks);
|
||||
uvm_cpu_chunk_free(chunk);
|
||||
return NV_ERR_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
// If we are here, we have to do at least one split.
|
||||
// We can't call any of the block_split_cpu_chunk_to_* functions since they
|
||||
// insert all of the split chunks into the block.
|
||||
// We only want to insert the sub-chunks that don't overlap. So, we have to
|
||||
// handle that by calling uvm_cpu_chunk_split() directly.
|
||||
status = uvm_cpu_chunk_split(chunk, split_chunks);
|
||||
if (status != NV_OK)
|
||||
goto done;
|
||||
|
||||
// Insert all split chunks that don't overlap existing allocations.
|
||||
// Note that this handles both splitting to 64K and 4K.
|
||||
running_page_index = page_index;
|
||||
for (i = 0; i < chunk_size / split_size; i++) {
|
||||
uvm_va_block_region_t subchunk_region = uvm_va_block_chunk_region(block, split_size, running_page_index);
|
||||
|
||||
// - If all the pages covered by the split chunk are missing, insert the
|
||||
// chunk into the block.
|
||||
// - If none of the pages are missing, free the chunk.
|
||||
// - Otherwise, some of the pages covered by the chunk are missing and a
|
||||
// second split will be needed.
|
||||
if (uvm_page_mask_region_full(node_pages_mask, subchunk_region)) {
|
||||
status = uvm_cpu_chunk_insert_in_block(block, split_chunks[i], running_page_index);
|
||||
if (status != NV_OK)
|
||||
goto done;
|
||||
|
||||
// To prevent double chunk freeing on error, clear the array pointer
|
||||
// before mapping.
|
||||
chunk_ptr = split_chunks[i];
|
||||
split_chunks[i] = NULL;
|
||||
status = uvm_va_block_map_cpu_chunk_on_gpus(block, chunk_ptr, running_page_index);
|
||||
if (status != NV_OK)
|
||||
goto done;
|
||||
}
|
||||
else if (uvm_page_mask_region_empty(node_pages_mask, subchunk_region)) {
|
||||
uvm_cpu_chunk_free(split_chunks[i]);
|
||||
split_chunks[i] = NULL;
|
||||
}
|
||||
|
||||
running_page_index = subchunk_region.outer;
|
||||
}
|
||||
|
||||
if (split_size > UVM_PAGE_SIZE_4K) {
|
||||
// Split any 64K chunks that overlap 4K chunks.
|
||||
for (i = 0; i < chunk_size / split_size; i++) {
|
||||
size_t j;
|
||||
|
||||
if (!split_chunks[i])
|
||||
continue;
|
||||
|
||||
running_page_index = page_index + ((split_size * i) / PAGE_SIZE);
|
||||
status = uvm_cpu_chunk_split(split_chunks[i], small_chunks);
|
||||
if (status != NV_OK)
|
||||
goto done;
|
||||
|
||||
for (j = 0; j < MAX_SMALL_CHUNKS_PER_BIG_SLOT; j++) {
|
||||
size_t chunk_num_pages = uvm_cpu_chunk_num_pages(small_chunks[j]);
|
||||
|
||||
if (uvm_page_mask_test(node_pages_mask, running_page_index)) {
|
||||
status = uvm_cpu_chunk_insert_in_block(block, small_chunks[j], running_page_index);
|
||||
if (status != NV_OK)
|
||||
goto done;
|
||||
|
||||
// To prevent double chunk freeing on error, clear the array pointer
|
||||
// before mapping.
|
||||
chunk_ptr = small_chunks[j];
|
||||
small_chunks[j] = NULL;
|
||||
status = uvm_va_block_map_cpu_chunk_on_gpus(block, chunk_ptr, running_page_index);
|
||||
if (status != NV_OK)
|
||||
goto done;
|
||||
}
|
||||
else {
|
||||
uvm_cpu_chunk_free(small_chunks[j]);
|
||||
}
|
||||
|
||||
running_page_index += chunk_num_pages;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
if (status != NV_OK) {
|
||||
// First, free any small chunks that have not been inserted.
|
||||
if (small_chunks) {
|
||||
for (i = 0; i < MAX_SMALL_CHUNKS_PER_BIG_SLOT; i++)
|
||||
uvm_cpu_chunk_free(small_chunks[i]);
|
||||
}
|
||||
|
||||
// Next, free any large chunks that have not been inserted.
|
||||
for (i = 0; i < chunk_size / split_size; i++)
|
||||
uvm_cpu_chunk_free(split_chunks[i]);
|
||||
}
|
||||
|
||||
uvm_kvfree(small_chunks);
|
||||
uvm_kvfree(split_chunks);
|
||||
return status;
|
||||
}
|
||||
|
||||
// Allocates the input page in the block, if it doesn't already exist
|
||||
@ -1350,17 +1837,31 @@ static NV_STATUS block_populate_pages_cpu(uvm_va_block_t *block,
|
||||
uvm_chunk_sizes_mask_t cpu_allocation_sizes = uvm_cpu_chunk_get_allocation_sizes();
|
||||
uvm_chunk_size_t alloc_size;
|
||||
uvm_page_mask_t *resident_mask = &block_context->scratch_page_mask;
|
||||
uvm_page_mask_t *allocated_mask;
|
||||
uvm_cpu_chunk_alloc_flags_t alloc_flags = UVM_CPU_CHUNK_ALLOC_FLAGS_NONE;
|
||||
uvm_va_space_t *va_space = uvm_va_block_get_va_space(block);
|
||||
uvm_processor_mask_t uvm_lite_gpus;
|
||||
uvm_page_index_t page_index;
|
||||
uvm_gpu_id_t id;
|
||||
int preferred_nid = block_context->make_resident.dest_nid;
|
||||
|
||||
if (block_test && block_test->cpu_chunk_allocation_target_id != NUMA_NO_NODE)
|
||||
preferred_nid = block_test->cpu_chunk_allocation_target_id;
|
||||
|
||||
// TODO: Bug 4158598: Using NUMA_NO_NODE for staging allocations is sub-optimal.
|
||||
if (preferred_nid != NUMA_NO_NODE) {
|
||||
uvm_va_block_cpu_node_state_t *node_state = block_node_state_get(block, preferred_nid);
|
||||
allocated_mask = &node_state->allocated;
|
||||
}
|
||||
else {
|
||||
allocated_mask = &block->cpu.allocated;
|
||||
}
|
||||
|
||||
// Check whether all requested pages have already been allocated.
|
||||
uvm_page_mask_init_from_region(&block_context->scratch_page_mask, populate_region, populate_page_mask);
|
||||
if (!uvm_page_mask_andnot(&block_context->scratch_page_mask,
|
||||
&block_context->scratch_page_mask,
|
||||
&block->cpu.allocated))
|
||||
allocated_mask))
|
||||
return NV_OK;
|
||||
|
||||
if (block_test) {
|
||||
@ -1369,8 +1870,8 @@ static NV_STATUS block_populate_pages_cpu(uvm_va_block_t *block,
|
||||
}
|
||||
|
||||
uvm_page_mask_zero(resident_mask);
|
||||
for_each_id_in_mask (id, &block->resident)
|
||||
uvm_page_mask_or(resident_mask, resident_mask, uvm_va_block_resident_mask_get(block, id));
|
||||
for_each_id_in_mask(id, &block->resident)
|
||||
uvm_page_mask_or(resident_mask, resident_mask, uvm_va_block_resident_mask_get(block, id, NUMA_NO_NODE));
|
||||
|
||||
// If the VA space has a UVM-Lite GPU registered, only PAGE_SIZE allocations
|
||||
// should be used in order to avoid extra copies due to dirty compound
|
||||
@ -1390,13 +1891,15 @@ static NV_STATUS block_populate_pages_cpu(uvm_va_block_t *block,
|
||||
for_each_va_block_page_in_region_mask(page_index, populate_page_mask, populate_region) {
|
||||
uvm_cpu_chunk_alloc_flags_t chunk_alloc_flags;
|
||||
uvm_va_block_region_t region = populate_region;
|
||||
uvm_va_block_cpu_node_state_t *node_state;
|
||||
int alloced_nid;
|
||||
|
||||
if (uvm_page_mask_test(&block->cpu.allocated, page_index)) {
|
||||
page_index = uvm_va_block_next_unset_page_in_mask(populate_region, &block->cpu.allocated, page_index) - 1;
|
||||
if (uvm_page_mask_test(allocated_mask, page_index)) {
|
||||
page_index = uvm_va_block_next_unset_page_in_mask(populate_region, allocated_mask, page_index) - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
UVM_ASSERT(!uvm_page_mask_test(&block->cpu.resident, page_index));
|
||||
UVM_ASSERT(!uvm_va_block_cpu_is_page_resident_on(block, preferred_nid, page_index));
|
||||
|
||||
chunk_alloc_flags = alloc_flags;
|
||||
|
||||
@ -1419,7 +1922,7 @@ static NV_STATUS block_populate_pages_cpu(uvm_va_block_t *block,
|
||||
|
||||
region = uvm_va_block_region_from_start_end(block, alloc_virt_addr, alloc_virt_addr + alloc_size - 1);
|
||||
|
||||
if (!uvm_page_mask_region_empty(&block->cpu.allocated, region))
|
||||
if (!uvm_page_mask_region_empty(allocated_mask, region))
|
||||
continue;
|
||||
|
||||
// If not all pages in the allocation region are resident somewhere,
|
||||
@ -1430,7 +1933,7 @@ static NV_STATUS block_populate_pages_cpu(uvm_va_block_t *block,
|
||||
if (!uvm_page_mask_region_full(resident_mask, region))
|
||||
chunk_alloc_flags |= UVM_CPU_CHUNK_ALLOC_FLAGS_ZERO;
|
||||
|
||||
status = block_alloc_cpu_chunk(block, alloc_size, chunk_alloc_flags, &chunk);
|
||||
status = block_alloc_cpu_chunk(block, alloc_size, chunk_alloc_flags, preferred_nid, &chunk);
|
||||
if (status == NV_OK) {
|
||||
page_index = region.first;
|
||||
break;
|
||||
@ -1442,22 +1945,39 @@ static NV_STATUS block_populate_pages_cpu(uvm_va_block_t *block,
|
||||
if (status != NV_OK)
|
||||
break;
|
||||
|
||||
status = uvm_cpu_chunk_insert_in_block(block, chunk, page_index);
|
||||
if (status != NV_OK) {
|
||||
uvm_cpu_chunk_free(chunk);
|
||||
return status;
|
||||
alloced_nid = uvm_cpu_chunk_get_numa_node(chunk);
|
||||
node_state = block_node_state_get(block, alloced_nid);
|
||||
if (!uvm_page_mask_region_empty(&node_state->allocated, region)) {
|
||||
UVM_ASSERT(preferred_nid != NUMA_NO_NODE);
|
||||
|
||||
if (uvm_page_mask_region_full(&node_state->allocated, region)) {
|
||||
uvm_cpu_chunk_free(chunk);
|
||||
goto skip;
|
||||
}
|
||||
|
||||
status = block_populate_overlapping_cpu_chunks(block, block_context, chunk, page_index);
|
||||
if (status != NV_OK)
|
||||
return status;
|
||||
}
|
||||
else {
|
||||
status = uvm_cpu_chunk_insert_in_block(block, chunk, page_index);
|
||||
if (status != NV_OK) {
|
||||
uvm_cpu_chunk_free(chunk);
|
||||
return status;
|
||||
}
|
||||
|
||||
status = uvm_va_block_map_cpu_chunk_on_gpus(block, chunk, page_index);
|
||||
if (status != NV_OK)
|
||||
break;
|
||||
}
|
||||
|
||||
status = uvm_va_block_map_cpu_chunk_on_gpus(block, page_index);
|
||||
if (status != NV_OK)
|
||||
break;
|
||||
|
||||
skip:
|
||||
// Skip iterating over all pages covered by the allocated chunk.
|
||||
page_index = region.outer - 1;
|
||||
}
|
||||
|
||||
if (status != NV_OK && chunk) {
|
||||
uvm_cpu_chunk_remove_from_block(block, page_index);
|
||||
uvm_cpu_chunk_remove_from_block(block, uvm_cpu_chunk_get_numa_node(chunk), page_index);
|
||||
uvm_cpu_chunk_free(chunk);
|
||||
}
|
||||
|
||||
@ -1742,9 +2262,10 @@ static NvU32 block_phys_page_size(uvm_va_block_t *block, block_phys_page_t page)
|
||||
uvm_chunk_size_t chunk_size;
|
||||
|
||||
if (UVM_ID_IS_CPU(page.processor)) {
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(block, page.page_index);
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(block, page.nid, page.page_index);
|
||||
uvm_page_mask_t *resident_mask = uvm_va_block_resident_mask_get(block, page.processor, NUMA_NO_NODE);
|
||||
|
||||
if (!uvm_page_mask_test(&block->cpu.resident, page.page_index))
|
||||
if (!uvm_page_mask_test(resident_mask, page.page_index))
|
||||
return 0;
|
||||
|
||||
UVM_ASSERT(uvm_processor_mask_test(&block->resident, UVM_ID_CPU));
|
||||
@ -1791,29 +2312,41 @@ static uvm_pte_bits_gpu_t get_gpu_pte_bit_index(uvm_prot_t prot)
|
||||
return pte_bit_index;
|
||||
}
|
||||
|
||||
uvm_page_mask_t *uvm_va_block_resident_mask_get(uvm_va_block_t *block, uvm_processor_id_t processor)
|
||||
uvm_page_mask_t *uvm_va_block_resident_mask_get(uvm_va_block_t *block, uvm_processor_id_t processor, int nid)
|
||||
{
|
||||
uvm_va_block_gpu_state_t *gpu_state;
|
||||
uvm_page_mask_t *resident_mask;
|
||||
|
||||
if (UVM_ID_IS_CPU(processor))
|
||||
return &block->cpu.resident;
|
||||
if (UVM_ID_IS_CPU(processor)) {
|
||||
uvm_va_block_cpu_node_state_t *node_state;
|
||||
|
||||
gpu_state = uvm_va_block_gpu_state_get(block, processor);
|
||||
if (nid == NUMA_NO_NODE) {
|
||||
resident_mask = &block->cpu.resident;
|
||||
}
|
||||
else {
|
||||
node_state = block_node_state_get(block, nid);
|
||||
resident_mask = &node_state->resident;
|
||||
}
|
||||
}
|
||||
else {
|
||||
gpu_state = uvm_va_block_gpu_state_get(block, processor);
|
||||
UVM_ASSERT(gpu_state);
|
||||
resident_mask = &gpu_state->resident;
|
||||
}
|
||||
|
||||
UVM_ASSERT(gpu_state);
|
||||
return &gpu_state->resident;
|
||||
return resident_mask;
|
||||
}
|
||||
|
||||
// Get the page residency mask for a processor
|
||||
//
|
||||
// Notably this will allocate GPU state if not yet present and if that fails
|
||||
// NULL is returned.
|
||||
static uvm_page_mask_t *block_resident_mask_get_alloc(uvm_va_block_t *block, uvm_processor_id_t processor)
|
||||
static uvm_page_mask_t *block_resident_mask_get_alloc(uvm_va_block_t *block, uvm_processor_id_t processor, int nid)
|
||||
{
|
||||
uvm_va_block_gpu_state_t *gpu_state;
|
||||
|
||||
if (UVM_ID_IS_CPU(processor))
|
||||
return &block->cpu.resident;
|
||||
return uvm_va_block_resident_mask_get(block, processor, nid);
|
||||
|
||||
gpu_state = block_gpu_state_get_alloc(block, block_get_gpu(block, processor));
|
||||
if (!gpu_state)
|
||||
@ -1842,6 +2375,28 @@ const uvm_page_mask_t *uvm_va_block_map_mask_get(uvm_va_block_t *block, uvm_proc
|
||||
return block_map_with_prot_mask_get(block, processor, UVM_PROT_READ_ONLY);
|
||||
}
|
||||
|
||||
void uvm_va_block_unmapped_pages_get(uvm_va_block_t *va_block,
|
||||
uvm_va_block_region_t region,
|
||||
uvm_page_mask_t *out_mask)
|
||||
{
|
||||
uvm_processor_mask_t non_uvm_lite_gpus;
|
||||
uvm_processor_id_t id;
|
||||
|
||||
uvm_assert_mutex_locked(&va_block->lock);
|
||||
|
||||
if (!uvm_va_block_is_hmm(va_block)) {
|
||||
uvm_page_mask_complement(out_mask, &va_block->maybe_mapped_pages);
|
||||
return;
|
||||
}
|
||||
|
||||
uvm_page_mask_region_fill(out_mask, region);
|
||||
uvm_processor_mask_andnot(&non_uvm_lite_gpus, &va_block->mapped, block_get_uvm_lite_gpus(va_block));
|
||||
|
||||
for_each_id_in_mask(id, &non_uvm_lite_gpus) {
|
||||
uvm_page_mask_andnot(out_mask, out_mask, uvm_va_block_map_mask_get(va_block, id));
|
||||
}
|
||||
}
|
||||
|
||||
static const uvm_page_mask_t *block_evicted_mask_get(uvm_va_block_t *block, uvm_gpu_id_t gpu_id)
|
||||
{
|
||||
uvm_va_block_gpu_state_t *gpu_state = uvm_va_block_gpu_state_get(block, gpu_id);
|
||||
@ -1854,7 +2409,7 @@ static bool block_is_page_resident_anywhere(uvm_va_block_t *block, uvm_page_inde
|
||||
{
|
||||
uvm_processor_id_t id;
|
||||
for_each_id_in_mask(id, &block->resident) {
|
||||
if (uvm_page_mask_test(uvm_va_block_resident_mask_get(block, id), page_index))
|
||||
if (uvm_page_mask_test(uvm_va_block_resident_mask_get(block, id, NUMA_NO_NODE), page_index))
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1877,24 +2432,6 @@ static bool block_processor_page_is_populated(uvm_va_block_t *block, uvm_process
|
||||
return gpu_state->chunks[chunk_index] != NULL;
|
||||
}
|
||||
|
||||
static bool block_processor_page_is_resident_on(uvm_va_block_t *block, uvm_processor_id_t proc, uvm_page_index_t page_index)
|
||||
{
|
||||
const uvm_page_mask_t *resident_mask;
|
||||
|
||||
if (UVM_ID_IS_CPU(proc)) {
|
||||
resident_mask = &block->cpu.resident;
|
||||
}
|
||||
else {
|
||||
uvm_va_block_gpu_state_t *gpu_state = uvm_va_block_gpu_state_get(block, proc);
|
||||
if (!gpu_state)
|
||||
return false;
|
||||
|
||||
resident_mask = &gpu_state->resident;
|
||||
}
|
||||
|
||||
return uvm_page_mask_test(resident_mask, page_index);
|
||||
}
|
||||
|
||||
// Compute the gpus that have at least the given access permissions for the
|
||||
// range described by region and page_mask. The function sets the bit if any
|
||||
// page in the region has the permissions.
|
||||
@ -2007,7 +2544,7 @@ static void block_page_resident_gpus(uvm_va_block_t *va_block,
|
||||
uvm_processor_mask_zero(resident_gpus);
|
||||
|
||||
for_each_gpu_id_in_mask(id, &va_block->resident) {
|
||||
if (uvm_page_mask_test(uvm_va_block_resident_mask_get(va_block, id), page_index)) {
|
||||
if (uvm_page_mask_test(uvm_va_block_resident_mask_get(va_block, id, NUMA_NO_NODE), page_index)) {
|
||||
UVM_ASSERT(block_processor_page_is_populated(va_block, id, page_index));
|
||||
uvm_processor_mask_set(resident_gpus, id);
|
||||
}
|
||||
@ -2020,7 +2557,7 @@ void uvm_va_block_page_resident_processors(uvm_va_block_t *va_block,
|
||||
{
|
||||
block_page_resident_gpus(va_block, page_index, resident_processors);
|
||||
|
||||
if (uvm_page_mask_test(uvm_va_block_resident_mask_get(va_block, UVM_ID_CPU), page_index)) {
|
||||
if (uvm_page_mask_test(uvm_va_block_resident_mask_get(va_block, UVM_ID_CPU, NUMA_NO_NODE), page_index)) {
|
||||
UVM_ASSERT(block_processor_page_is_populated(va_block, UVM_ID_CPU, page_index));
|
||||
uvm_processor_mask_set(resident_processors, UVM_ID_CPU);
|
||||
}
|
||||
@ -2049,7 +2586,7 @@ static uvm_processor_id_t block_page_get_closest_resident_in_mask(uvm_va_block_t
|
||||
uvm_processor_mask_copy(&search_mask, &va_block->resident);
|
||||
|
||||
for_each_closest_id(id, &search_mask, processor, va_space) {
|
||||
if (uvm_page_mask_test(uvm_va_block_resident_mask_get(va_block, id), page_index))
|
||||
if (uvm_page_mask_test(uvm_va_block_resident_mask_get(va_block, id, NUMA_NO_NODE), page_index))
|
||||
return id;
|
||||
}
|
||||
|
||||
@ -2245,7 +2782,7 @@ static NV_STATUS block_zero_new_gpu_chunk(uvm_va_block_t *block,
|
||||
// Roll up all pages in chunk_region which are resident somewhere
|
||||
uvm_page_mask_zero(zero_mask);
|
||||
for_each_id_in_mask(id, &block->resident)
|
||||
uvm_page_mask_or(zero_mask, zero_mask, uvm_va_block_resident_mask_get(block, id));
|
||||
uvm_page_mask_or(zero_mask, zero_mask, uvm_va_block_resident_mask_get(block, id, NUMA_NO_NODE));
|
||||
|
||||
// If all pages in the chunk are resident somewhere, we don't need to clear
|
||||
// anything. Just make sure the chunk is tracked properly.
|
||||
@ -2434,6 +2971,13 @@ static NV_STATUS block_populate_pages_gpu(uvm_va_block_t *block,
|
||||
return NV_OK;
|
||||
}
|
||||
|
||||
static const uvm_processor_mask_t *block_get_can_copy_from_mask(uvm_va_block_t *block, uvm_processor_id_t from)
|
||||
{
|
||||
uvm_va_space_t *va_space = uvm_va_block_get_va_space(block);
|
||||
|
||||
return &va_space->can_copy_from[uvm_id_value(from)];
|
||||
}
|
||||
|
||||
static NV_STATUS block_populate_pages(uvm_va_block_t *block,
|
||||
uvm_va_block_retry_t *retry,
|
||||
uvm_va_block_context_t *block_context,
|
||||
@ -2442,8 +2986,10 @@ static NV_STATUS block_populate_pages(uvm_va_block_t *block,
|
||||
const uvm_page_mask_t *page_mask)
|
||||
{
|
||||
NV_STATUS status;
|
||||
const uvm_page_mask_t *resident_mask = block_resident_mask_get_alloc(block, dest_id);
|
||||
const uvm_page_mask_t *resident_mask = block_resident_mask_get_alloc(block, dest_id, NUMA_NO_NODE);
|
||||
uvm_page_mask_t *populate_page_mask = &block_context->make_resident.page_mask;
|
||||
uvm_page_mask_t *pages_staged = &block_context->make_resident.pages_staged;
|
||||
uvm_page_mask_t *cpu_populate_mask;
|
||||
uvm_memcg_context_t memcg_context;
|
||||
|
||||
if (!resident_mask)
|
||||
@ -2454,22 +3000,58 @@ static NV_STATUS block_populate_pages(uvm_va_block_t *block,
|
||||
else
|
||||
uvm_page_mask_complement(populate_page_mask, resident_mask);
|
||||
|
||||
if (UVM_ID_IS_GPU(dest_id))
|
||||
return block_populate_pages_gpu(block, retry, block_get_gpu(block, dest_id), region, populate_page_mask);
|
||||
if (UVM_ID_IS_GPU(dest_id)) {
|
||||
uvm_processor_mask_t staged_processors;
|
||||
uvm_processor_mask_t accessible_resident_processors;
|
||||
const uvm_processor_mask_t *can_copy_from_processors;
|
||||
uvm_page_mask_t *scratch_page_mask = &block_context->scratch_page_mask;
|
||||
uvm_page_mask_t *id_resident_mask;
|
||||
uvm_processor_id_t id;
|
||||
|
||||
status = block_populate_pages_gpu(block, retry, block_get_gpu(block, dest_id), region, populate_page_mask);
|
||||
if (status != NV_OK)
|
||||
return status;
|
||||
|
||||
uvm_page_mask_zero(pages_staged);
|
||||
|
||||
// Get the mask of all processors that have resident pages from which
|
||||
// the destination cannot copy directly.
|
||||
can_copy_from_processors = block_get_can_copy_from_mask(block, dest_id);
|
||||
if (!uvm_processor_mask_andnot(&staged_processors, &block->resident, can_copy_from_processors))
|
||||
return status;
|
||||
|
||||
// Compute the pages that will be staged through the CPU by:
|
||||
// 1. Computing all of the pages resident on the processors from which
|
||||
// dest_id cannot directly copy.
|
||||
for_each_id_in_mask(id, &staged_processors) {
|
||||
id_resident_mask = uvm_va_block_resident_mask_get(block, id, NUMA_NO_NODE);
|
||||
uvm_page_mask_and(scratch_page_mask, populate_page_mask, id_resident_mask);
|
||||
uvm_page_mask_or(pages_staged, pages_staged, scratch_page_mask);
|
||||
}
|
||||
|
||||
// 2. Remove any pages in pages_staged that are on any resident processor
|
||||
// dest_id can copy from.
|
||||
if (uvm_processor_mask_and(&accessible_resident_processors, can_copy_from_processors, &block->resident)) {
|
||||
for_each_id_in_mask(id, &accessible_resident_processors) {
|
||||
id_resident_mask = uvm_va_block_resident_mask_get(block, id, NUMA_NO_NODE);
|
||||
uvm_page_mask_andnot(pages_staged, pages_staged, id_resident_mask);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Removing any pages not in the populate mask.
|
||||
uvm_page_mask_region_clear_outside(pages_staged, region);
|
||||
cpu_populate_mask = pages_staged;
|
||||
}
|
||||
else {
|
||||
cpu_populate_mask = populate_page_mask;
|
||||
}
|
||||
|
||||
uvm_memcg_context_start(&memcg_context, block_context->mm);
|
||||
status = block_populate_pages_cpu(block, populate_page_mask, region, block_context);
|
||||
status = block_populate_pages_cpu(block, cpu_populate_mask, region, block_context);
|
||||
uvm_memcg_context_end(&memcg_context);
|
||||
return status;
|
||||
}
|
||||
|
||||
static const uvm_processor_mask_t *block_get_can_copy_from_mask(uvm_va_block_t *block, uvm_processor_id_t from)
|
||||
{
|
||||
uvm_va_space_t *va_space = uvm_va_block_get_va_space(block);
|
||||
|
||||
return &va_space->can_copy_from[uvm_id_value(from)];
|
||||
}
|
||||
|
||||
static bool block_can_copy_from(uvm_va_block_t *va_block, uvm_processor_id_t from, uvm_processor_id_t to)
|
||||
{
|
||||
return uvm_processor_mask_test(block_get_can_copy_from_mask(va_block, to), from);
|
||||
@ -2513,7 +3095,7 @@ static uvm_gpu_phys_address_t block_phys_page_address(uvm_va_block_t *block,
|
||||
UVM_ASSERT(accessing_gpu_state);
|
||||
|
||||
if (UVM_ID_IS_CPU(block_page.processor)) {
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(block, block_page.page_index);
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(block, block_page.nid, block_page.page_index);
|
||||
NvU64 dma_addr = uvm_cpu_chunk_get_gpu_phys_addr(chunk, gpu->parent);
|
||||
uvm_va_block_region_t chunk_region = uvm_va_block_chunk_region(block,
|
||||
uvm_cpu_chunk_get_size(chunk),
|
||||
@ -2588,9 +3170,15 @@ uvm_gpu_phys_address_t uvm_va_block_res_phys_page_address(uvm_va_block_t *va_blo
|
||||
uvm_processor_id_t residency,
|
||||
uvm_gpu_t *gpu)
|
||||
{
|
||||
uvm_assert_mutex_locked(&va_block->lock);
|
||||
int nid = NUMA_NO_NODE;
|
||||
|
||||
return block_phys_page_address(va_block, block_phys_page(residency, page_index), gpu);
|
||||
uvm_assert_mutex_locked(&va_block->lock);
|
||||
if (UVM_ID_IS_CPU(residency)) {
|
||||
nid = block_get_page_node_residency(va_block, page_index);
|
||||
UVM_ASSERT(nid != NUMA_NO_NODE);
|
||||
}
|
||||
|
||||
return block_phys_page_address(va_block, block_phys_page(residency, nid, page_index), gpu);
|
||||
}
|
||||
|
||||
uvm_gpu_phys_address_t uvm_va_block_gpu_phys_page_address(uvm_va_block_t *va_block,
|
||||
@ -2605,6 +3193,9 @@ typedef struct
|
||||
// Location of the memory
|
||||
uvm_processor_id_t id;
|
||||
|
||||
// NUMA node ID if the processor is the CPU. Ignored otherwise.
|
||||
int nid;
|
||||
|
||||
// Whether the whole block has a single physically-contiguous chunk of
|
||||
// storage on the processor.
|
||||
bool is_block_contig;
|
||||
@ -2734,13 +3325,14 @@ error:
|
||||
static bool block_page_is_clean(uvm_va_block_t *block,
|
||||
uvm_processor_id_t dst_id,
|
||||
uvm_processor_id_t src_id,
|
||||
uvm_page_index_t page_index)
|
||||
uvm_page_index_t page_index,
|
||||
int nid)
|
||||
{
|
||||
return !uvm_va_block_is_hmm(block) &&
|
||||
uvm_id_equal(dst_id, uvm_va_range_get_policy(block->va_range)->preferred_location) &&
|
||||
UVM_ID_IS_CPU(src_id) &&
|
||||
!block_get_gpu(block, dst_id)->parent->isr.replayable_faults.handling &&
|
||||
!block_cpu_page_is_dirty(block, page_index);
|
||||
!block_cpu_page_is_dirty(block, page_index, nid);
|
||||
}
|
||||
|
||||
// When the destination is the CPU...
|
||||
@ -2749,15 +3341,16 @@ static bool block_page_is_clean(uvm_va_block_t *block,
|
||||
static void block_update_page_dirty_state(uvm_va_block_t *block,
|
||||
uvm_processor_id_t dst_id,
|
||||
uvm_processor_id_t src_id,
|
||||
int nid,
|
||||
uvm_page_index_t page_index)
|
||||
{
|
||||
if (UVM_ID_IS_GPU(dst_id))
|
||||
return;
|
||||
|
||||
if (uvm_id_equal(src_id, uvm_va_range_get_policy(block->va_range)->preferred_location))
|
||||
block_mark_cpu_page_clean(block, page_index);
|
||||
block_mark_cpu_page_clean(block, page_index, nid);
|
||||
else
|
||||
block_mark_cpu_page_dirty(block, page_index);
|
||||
block_mark_cpu_page_dirty(block, page_index, nid);
|
||||
}
|
||||
|
||||
static void block_mark_memory_used(uvm_va_block_t *block, uvm_processor_id_t id)
|
||||
@ -2783,7 +3376,7 @@ static void block_mark_memory_used(uvm_va_block_t *block, uvm_processor_id_t id)
|
||||
|
||||
static void block_set_resident_processor(uvm_va_block_t *block, uvm_processor_id_t id)
|
||||
{
|
||||
UVM_ASSERT(!uvm_page_mask_empty(uvm_va_block_resident_mask_get(block, id)));
|
||||
UVM_ASSERT(!uvm_page_mask_empty(uvm_va_block_resident_mask_get(block, id, NUMA_NO_NODE)));
|
||||
|
||||
if (uvm_processor_mask_test_and_set(&block->resident, id))
|
||||
return;
|
||||
@ -2795,7 +3388,7 @@ static void block_clear_resident_processor(uvm_va_block_t *block, uvm_processor_
|
||||
{
|
||||
uvm_gpu_t *gpu;
|
||||
|
||||
UVM_ASSERT(uvm_page_mask_empty(uvm_va_block_resident_mask_get(block, id)));
|
||||
UVM_ASSERT(uvm_page_mask_empty(uvm_va_block_resident_mask_get(block, id, NUMA_NO_NODE)));
|
||||
|
||||
if (!uvm_processor_mask_test_and_clear(&block->resident, id))
|
||||
return;
|
||||
@ -2821,37 +3414,41 @@ static bool block_phys_copy_contig_check(uvm_va_block_t *block,
|
||||
uvm_page_index_t page_index,
|
||||
const uvm_gpu_address_t *base_address,
|
||||
uvm_processor_id_t proc_id,
|
||||
int nid,
|
||||
uvm_gpu_t *copying_gpu)
|
||||
{
|
||||
uvm_gpu_address_t page_address;
|
||||
uvm_gpu_address_t contig_address = *base_address;
|
||||
|
||||
contig_address.address += page_index * PAGE_SIZE;
|
||||
|
||||
page_address = block_phys_page_copy_address(block, block_phys_page(proc_id, page_index), copying_gpu);
|
||||
page_address = block_phys_page_copy_address(block, block_phys_page(proc_id, nid, page_index), copying_gpu);
|
||||
|
||||
return uvm_gpu_addr_cmp(page_address, contig_address) == 0;
|
||||
}
|
||||
|
||||
// Check if the VA block has a single physically-contiguous chunk of storage
|
||||
// on the processor.
|
||||
static bool is_block_phys_contig(uvm_va_block_t *block, uvm_processor_id_t id)
|
||||
static bool is_block_phys_contig(uvm_va_block_t *block, uvm_processor_id_t id, int nid)
|
||||
{
|
||||
uvm_cpu_chunk_t *chunk;
|
||||
|
||||
if (UVM_ID_IS_GPU(id))
|
||||
return uvm_va_block_size(block) == block_gpu_chunk_size(block, block_get_gpu(block, id), 0);
|
||||
|
||||
chunk = uvm_cpu_chunk_first_in_region(block, uvm_va_block_region_from_block(block), NULL);
|
||||
UVM_ASSERT(nid != NUMA_NO_NODE);
|
||||
chunk = uvm_cpu_chunk_first_in_region(block, uvm_va_block_region_from_block(block), nid, NULL);
|
||||
return chunk && (uvm_va_block_size(block) == uvm_cpu_chunk_get_size(chunk));
|
||||
}
|
||||
|
||||
static uvm_va_block_region_t block_phys_contig_region(uvm_va_block_t *block,
|
||||
uvm_page_index_t page_index,
|
||||
uvm_processor_id_t resident_id)
|
||||
uvm_processor_id_t resident_id,
|
||||
int nid)
|
||||
{
|
||||
if (UVM_ID_IS_CPU(resident_id)) {
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(block, page_index);
|
||||
uvm_cpu_chunk_t *chunk;
|
||||
UVM_ASSERT(nid != NUMA_NO_NODE);
|
||||
chunk = uvm_cpu_chunk_get_chunk_for_page(block, nid, page_index);
|
||||
return uvm_cpu_chunk_block_region(block, chunk, page_index);
|
||||
}
|
||||
else {
|
||||
@ -2871,11 +3468,11 @@ static uvm_gpu_address_t block_copy_get_address(uvm_va_block_t *block,
|
||||
if (bca->is_block_contig) {
|
||||
uvm_gpu_address_t addr = bca->gpu_address;
|
||||
addr.address += page_index * PAGE_SIZE;
|
||||
UVM_ASSERT(block_phys_copy_contig_check(block, page_index, &bca->gpu_address, bca->id, copying_gpu));
|
||||
UVM_ASSERT(block_phys_copy_contig_check(block, page_index, &bca->gpu_address, bca->id, bca->nid, copying_gpu));
|
||||
return addr;
|
||||
}
|
||||
|
||||
return block_phys_page_copy_address(block, block_phys_page(bca->id, page_index), copying_gpu);
|
||||
return block_phys_page_copy_address(block, block_phys_page(bca->id, bca->nid, page_index), copying_gpu);
|
||||
}
|
||||
|
||||
// When the Confidential Computing feature is enabled, the function performs
|
||||
@ -2886,17 +3483,19 @@ static void conf_computing_block_copy_push_cpu_to_gpu(uvm_va_block_t *block,
|
||||
uvm_va_block_region_t region,
|
||||
uvm_push_t *push)
|
||||
{
|
||||
uvm_push_flag_t membar_flag = 0;
|
||||
uvm_push_flag_t push_membar_flag = UVM_PUSH_FLAG_COUNT;
|
||||
uvm_gpu_t *gpu = uvm_push_get_gpu(push);
|
||||
uvm_page_index_t page_index = region.first;
|
||||
uvm_conf_computing_dma_buffer_t *dma_buffer = copy_state->dma_buffer;
|
||||
struct page *src_page = uvm_cpu_chunk_get_cpu_page(block, page_index);
|
||||
struct page *src_page;
|
||||
uvm_gpu_address_t staging_buffer = uvm_mem_gpu_address_virtual_kernel(dma_buffer->alloc, gpu);
|
||||
uvm_gpu_address_t auth_tag_buffer = uvm_mem_gpu_address_virtual_kernel(dma_buffer->auth_tag, gpu);
|
||||
char *cpu_auth_tag_buffer = (char *)uvm_mem_get_cpu_addr_kernel(dma_buffer->auth_tag) +
|
||||
(page_index * UVM_CONF_COMPUTING_AUTH_TAG_SIZE);
|
||||
uvm_gpu_address_t dst_address = block_copy_get_address(block, ©_state->dst, page_index, gpu);
|
||||
char *cpu_va_staging_buffer = (char *)uvm_mem_get_cpu_addr_kernel(dma_buffer->alloc) + (page_index * PAGE_SIZE);
|
||||
uvm_cpu_chunk_t *chunk;
|
||||
uvm_va_block_region_t chunk_region;
|
||||
|
||||
UVM_ASSERT(UVM_ID_IS_CPU(copy_state->src.id));
|
||||
UVM_ASSERT(UVM_ID_IS_GPU(copy_state->dst.id));
|
||||
@ -2906,23 +3505,27 @@ static void conf_computing_block_copy_push_cpu_to_gpu(uvm_va_block_t *block,
|
||||
// See comment in block_copy_begin_push.
|
||||
UVM_ASSERT(uvm_tracker_is_completed(&block->tracker));
|
||||
|
||||
chunk = uvm_cpu_chunk_get_chunk_for_page(block, copy_state->src.nid, page_index);
|
||||
UVM_ASSERT(chunk);
|
||||
// The caller guarantees that all pages in region are contiguous,
|
||||
// meaning they're guaranteed to be part of the same compound page.
|
||||
chunk_region = uvm_va_block_chunk_region(block, uvm_cpu_chunk_get_size(chunk), page_index);
|
||||
UVM_ASSERT(uvm_va_block_region_contains_region(region, chunk_region));
|
||||
|
||||
src_page = uvm_cpu_chunk_get_cpu_page(block, chunk, page_index);
|
||||
staging_buffer.address += page_index * PAGE_SIZE;
|
||||
auth_tag_buffer.address += page_index * UVM_CONF_COMPUTING_AUTH_TAG_SIZE;
|
||||
|
||||
if (uvm_push_get_and_reset_flag(push, UVM_PUSH_FLAG_NEXT_MEMBAR_NONE))
|
||||
membar_flag = UVM_PUSH_FLAG_NEXT_MEMBAR_NONE;
|
||||
push_membar_flag = UVM_PUSH_FLAG_NEXT_MEMBAR_NONE;
|
||||
else if (uvm_push_get_and_reset_flag(push, UVM_PUSH_FLAG_NEXT_MEMBAR_GPU))
|
||||
membar_flag = UVM_PUSH_FLAG_NEXT_MEMBAR_GPU;
|
||||
push_membar_flag = UVM_PUSH_FLAG_NEXT_MEMBAR_GPU;
|
||||
|
||||
// kmap() only guarantees PAGE_SIZE contiguity, all encryption and
|
||||
// decryption must happen on a PAGE_SIZE basis.
|
||||
for_each_va_block_page_in_region(page_index, region) {
|
||||
void *src_cpu_virt_addr;
|
||||
|
||||
// The caller guarantees that all pages in region are contiguous,
|
||||
// meaning they're guaranteed to be part of the same compound page.
|
||||
UVM_ASSERT(src_page == uvm_cpu_chunk_get_cpu_page(block, page_index));
|
||||
|
||||
src_cpu_virt_addr = kmap(src_page);
|
||||
uvm_conf_computing_cpu_encrypt(push->channel,
|
||||
cpu_va_staging_buffer,
|
||||
@ -2942,8 +3545,8 @@ static void conf_computing_block_copy_push_cpu_to_gpu(uvm_va_block_t *block,
|
||||
|
||||
if (page_index < (region.outer - 1))
|
||||
uvm_push_set_flag(push, UVM_PUSH_FLAG_NEXT_MEMBAR_NONE);
|
||||
else if (membar_flag)
|
||||
uvm_push_set_flag(push, membar_flag);
|
||||
else if (push_membar_flag != UVM_PUSH_FLAG_COUNT)
|
||||
uvm_push_set_flag(push, push_membar_flag);
|
||||
|
||||
gpu->parent->ce_hal->decrypt(push, dst_address, staging_buffer, PAGE_SIZE, auth_tag_buffer);
|
||||
|
||||
@ -2964,7 +3567,7 @@ static void conf_computing_block_copy_push_gpu_to_cpu(uvm_va_block_t *block,
|
||||
uvm_va_block_region_t region,
|
||||
uvm_push_t *push)
|
||||
{
|
||||
uvm_push_flag_t membar_flag = 0;
|
||||
uvm_push_flag_t push_membar_flag = UVM_PUSH_FLAG_COUNT;
|
||||
uvm_gpu_t *gpu = uvm_push_get_gpu(push);
|
||||
uvm_page_index_t page_index = region.first;
|
||||
uvm_conf_computing_dma_buffer_t *dma_buffer = copy_state->dma_buffer;
|
||||
@ -2981,9 +3584,9 @@ static void conf_computing_block_copy_push_gpu_to_cpu(uvm_va_block_t *block,
|
||||
auth_tag_buffer.address += page_index * UVM_CONF_COMPUTING_AUTH_TAG_SIZE;
|
||||
|
||||
if (uvm_push_get_and_reset_flag(push, UVM_PUSH_FLAG_NEXT_MEMBAR_NONE))
|
||||
membar_flag = UVM_PUSH_FLAG_NEXT_MEMBAR_NONE;
|
||||
push_membar_flag = UVM_PUSH_FLAG_NEXT_MEMBAR_NONE;
|
||||
else if (uvm_push_get_and_reset_flag(push, UVM_PUSH_FLAG_NEXT_MEMBAR_GPU))
|
||||
membar_flag = UVM_PUSH_FLAG_NEXT_MEMBAR_GPU;
|
||||
push_membar_flag = UVM_PUSH_FLAG_NEXT_MEMBAR_GPU;
|
||||
|
||||
// Because we use kmap() for mapping pages for CPU side
|
||||
// crypto-operations and it only guarantees PAGE_SIZE contiguity, all
|
||||
@ -3001,8 +3604,8 @@ static void conf_computing_block_copy_push_gpu_to_cpu(uvm_va_block_t *block,
|
||||
|
||||
if (page_index < (region.outer - 1))
|
||||
uvm_push_set_flag(push, UVM_PUSH_FLAG_NEXT_MEMBAR_NONE);
|
||||
else if (membar_flag)
|
||||
uvm_push_set_flag(push, membar_flag);
|
||||
else if (push_membar_flag != UVM_PUSH_FLAG_COUNT)
|
||||
uvm_push_set_flag(push, push_membar_flag);
|
||||
|
||||
gpu->parent->ce_hal->encrypt(push, staging_buffer, src_address, PAGE_SIZE, auth_tag_buffer);
|
||||
|
||||
@ -3039,7 +3642,7 @@ static NV_STATUS conf_computing_copy_pages_finish(uvm_va_block_t *block,
|
||||
// kmap() only guarantees PAGE_SIZE contiguity, all encryption and
|
||||
// decryption must happen on a PAGE_SIZE basis.
|
||||
for_each_va_block_page_in_mask(page_index, encrypted_page_mask, block) {
|
||||
struct page *dst_page = uvm_cpu_chunk_get_cpu_page(block, page_index);
|
||||
struct page *dst_page = uvm_va_block_get_cpu_page(block, page_index);
|
||||
void *staging_buffer = (char *)staging_buffer_base + (page_index * PAGE_SIZE);
|
||||
void *auth_tag_buffer = (char *)auth_tag_buffer_base + (page_index * UVM_CONF_COMPUTING_AUTH_TAG_SIZE);
|
||||
void *cpu_page_address = kmap(dst_page);
|
||||
@ -3135,7 +3738,9 @@ static NV_STATUS block_copy_end_push(uvm_va_block_t *block,
|
||||
static NV_STATUS block_copy_resident_pages_between(uvm_va_block_t *block,
|
||||
uvm_va_block_context_t *block_context,
|
||||
uvm_processor_id_t dst_id,
|
||||
int dst_nid,
|
||||
uvm_processor_id_t src_id,
|
||||
int src_nid,
|
||||
uvm_va_block_region_t region,
|
||||
uvm_page_mask_t *copy_mask,
|
||||
const uvm_page_mask_t *prefetch_page_mask,
|
||||
@ -3145,7 +3750,7 @@ static NV_STATUS block_copy_resident_pages_between(uvm_va_block_t *block,
|
||||
uvm_tracker_t *copy_tracker)
|
||||
{
|
||||
NV_STATUS status = NV_OK;
|
||||
uvm_page_mask_t *dst_resident_mask = uvm_va_block_resident_mask_get(block, dst_id);
|
||||
uvm_page_mask_t *dst_resident_mask = uvm_va_block_resident_mask_get(block, dst_id, dst_nid);
|
||||
uvm_gpu_t *copying_gpu = NULL;
|
||||
uvm_push_t push;
|
||||
uvm_page_index_t page_index;
|
||||
@ -3162,18 +3767,31 @@ static NV_STATUS block_copy_resident_pages_between(uvm_va_block_t *block,
|
||||
uvm_va_range_t *va_range = block->va_range;
|
||||
uvm_va_space_t *va_space = uvm_va_block_get_va_space(block);
|
||||
|
||||
copy_state.src.id = src_id;
|
||||
copy_state.dst.id = dst_id;
|
||||
copy_state.src.is_block_contig = is_block_phys_contig(block, src_id);
|
||||
copy_state.dst.is_block_contig = is_block_phys_contig(block, dst_id);
|
||||
|
||||
*copied_pages = 0;
|
||||
|
||||
UVM_ASSERT(UVM_ID_IS_GPU(src_id) || UVM_ID_IS_GPU(dst_id));
|
||||
|
||||
if (UVM_ID_IS_CPU(src_id))
|
||||
UVM_ASSERT(src_nid != NUMA_NO_NODE);
|
||||
|
||||
if (UVM_ID_IS_CPU(dst_id))
|
||||
UVM_ASSERT(dst_nid != NUMA_NO_NODE);
|
||||
|
||||
// If there are no pages to be copied, exit early
|
||||
if (!uvm_page_mask_andnot(copy_mask, copy_mask, dst_resident_mask) ||
|
||||
!uvm_page_mask_andnot(copy_mask, copy_mask, migrated_pages))
|
||||
if (!uvm_page_mask_andnot(copy_mask, copy_mask, dst_resident_mask))
|
||||
return NV_OK;
|
||||
|
||||
if (migrated_pages && !uvm_page_mask_andnot(copy_mask, copy_mask, migrated_pages))
|
||||
return NV_OK;
|
||||
|
||||
copy_state.src.id = src_id;
|
||||
copy_state.dst.id = dst_id;
|
||||
copy_state.src.nid = src_nid;
|
||||
copy_state.dst.nid = dst_nid;
|
||||
|
||||
copy_state.src.is_block_contig = is_block_phys_contig(block, src_id, copy_state.src.nid);
|
||||
copy_state.dst.is_block_contig = is_block_phys_contig(block, dst_id, copy_state.dst.nid);
|
||||
|
||||
// uvm_range_group_range_iter_first should only be called when the va_space
|
||||
// lock is held, which is always the case unless an eviction is taking
|
||||
// place.
|
||||
@ -3184,19 +3802,6 @@ static NV_STATUS block_copy_resident_pages_between(uvm_va_block_t *block,
|
||||
rgr_has_changed = true;
|
||||
}
|
||||
|
||||
if (UVM_ID_IS_CPU(dst_id)) {
|
||||
uvm_memcg_context_t memcg_context;
|
||||
|
||||
// To support staging through CPU, populate CPU pages on demand.
|
||||
// GPU destinations should have their pages populated already, but
|
||||
// that might change if we add staging through GPUs.
|
||||
uvm_memcg_context_start(&memcg_context, block_context->mm);
|
||||
status = block_populate_pages_cpu(block, copy_mask, region, block_context);
|
||||
uvm_memcg_context_end(&memcg_context);
|
||||
if (status != NV_OK)
|
||||
return status;
|
||||
}
|
||||
|
||||
// TODO: Bug 3745051: This function is complicated and needs refactoring
|
||||
for_each_va_block_page_in_region_mask(page_index, copy_mask, region) {
|
||||
NvU64 page_start = uvm_va_block_cpu_page_address(block, page_index);
|
||||
@ -3244,7 +3849,7 @@ static NV_STATUS block_copy_resident_pages_between(uvm_va_block_t *block,
|
||||
|
||||
// No need to copy pages that haven't changed. Just clear residency
|
||||
// information
|
||||
if (block_page_is_clean(block, dst_id, src_id, page_index))
|
||||
if (block_page_is_clean(block, dst_id, src_id, page_index, copy_state.src.nid))
|
||||
continue;
|
||||
|
||||
if (!copying_gpu) {
|
||||
@ -3270,7 +3875,7 @@ static NV_STATUS block_copy_resident_pages_between(uvm_va_block_t *block,
|
||||
}
|
||||
|
||||
if (!uvm_va_block_is_hmm(block))
|
||||
block_update_page_dirty_state(block, dst_id, src_id, page_index);
|
||||
block_update_page_dirty_state(block, dst_id, src_id, copy_state.dst.nid, page_index);
|
||||
|
||||
if (last_index == region.outer) {
|
||||
bool can_cache_src_phys_addr = copy_state.src.is_block_contig;
|
||||
@ -3291,12 +3896,16 @@ static NV_STATUS block_copy_resident_pages_between(uvm_va_block_t *block,
|
||||
// using the page index.
|
||||
if (can_cache_src_phys_addr) {
|
||||
copy_state.src.gpu_address = block_phys_page_copy_address(block,
|
||||
block_phys_page(src_id, 0),
|
||||
block_phys_page(src_id,
|
||||
copy_state.src.nid,
|
||||
0),
|
||||
copying_gpu);
|
||||
}
|
||||
if (can_cache_dst_phys_addr) {
|
||||
copy_state.dst.gpu_address = block_phys_page_copy_address(block,
|
||||
block_phys_page(dst_id, 0),
|
||||
block_phys_page(dst_id,
|
||||
copy_state.dst.nid,
|
||||
0),
|
||||
copying_gpu);
|
||||
}
|
||||
}
|
||||
@ -3359,12 +3968,87 @@ static NV_STATUS block_copy_resident_pages_between(uvm_va_block_t *block,
|
||||
uvm_page_mask_region_clear(copy_mask, uvm_va_block_region(page_index, PAGES_PER_UVM_VA_BLOCK));
|
||||
|
||||
*copied_pages = uvm_page_mask_weight(copy_mask);
|
||||
if (*copied_pages)
|
||||
if (*copied_pages && migrated_pages)
|
||||
uvm_page_mask_or(migrated_pages, migrated_pages, copy_mask);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static NV_STATUS block_copy_resident_pages_from(uvm_va_block_t *block,
|
||||
uvm_va_block_context_t *block_context,
|
||||
uvm_processor_id_t dst_id,
|
||||
uvm_processor_id_t src_id,
|
||||
int src_nid,
|
||||
uvm_va_block_region_t region,
|
||||
const uvm_page_mask_t *page_mask,
|
||||
const uvm_page_mask_t *prefetch_page_mask,
|
||||
uvm_va_block_transfer_mode_t transfer_mode,
|
||||
uvm_page_mask_t *migrated_pages,
|
||||
NvU32 *copied_pages_out,
|
||||
uvm_tracker_t *copy_tracker)
|
||||
{
|
||||
uvm_page_mask_t *copy_mask = &block_context->make_resident.copy_resident_pages_mask;
|
||||
uvm_page_mask_t *src_resident_mask;
|
||||
uvm_page_mask_t *node_pages_mask = &block_context->make_resident.node_pages_mask;
|
||||
uvm_make_resident_page_tracking_t *page_tracking = &block_context->make_resident.cpu_pages_used;
|
||||
NvU32 copied_pages_from_src;
|
||||
NV_STATUS status = NV_OK;
|
||||
int dst_nid;
|
||||
|
||||
src_resident_mask = uvm_va_block_resident_mask_get(block, src_id, src_nid);
|
||||
uvm_page_mask_init_from_region(copy_mask, region, src_resident_mask);
|
||||
|
||||
if (page_mask)
|
||||
uvm_page_mask_and(copy_mask, copy_mask, page_mask);
|
||||
|
||||
if (UVM_ID_IS_CPU(dst_id)) {
|
||||
for_each_node_mask(dst_nid, page_tracking->nodes) {
|
||||
if (!uvm_page_mask_and(node_pages_mask, copy_mask, page_tracking->node_masks[node_to_index(dst_nid)]))
|
||||
continue;
|
||||
|
||||
status = block_copy_resident_pages_between(block,
|
||||
block_context,
|
||||
dst_id,
|
||||
dst_nid,
|
||||
src_id,
|
||||
src_nid,
|
||||
region,
|
||||
node_pages_mask,
|
||||
prefetch_page_mask,
|
||||
transfer_mode,
|
||||
migrated_pages,
|
||||
&copied_pages_from_src,
|
||||
copy_tracker);
|
||||
|
||||
*copied_pages_out += copied_pages_from_src;
|
||||
|
||||
if (status != NV_OK)
|
||||
break;
|
||||
|
||||
if (!uvm_page_mask_andnot(copy_mask, copy_mask, node_pages_mask))
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
status = block_copy_resident_pages_between(block,
|
||||
block_context,
|
||||
dst_id,
|
||||
NUMA_NO_NODE,
|
||||
src_id,
|
||||
src_nid,
|
||||
region,
|
||||
copy_mask,
|
||||
prefetch_page_mask,
|
||||
transfer_mode,
|
||||
migrated_pages,
|
||||
&copied_pages_from_src,
|
||||
copy_tracker);
|
||||
*copied_pages_out += copied_pages_from_src;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
// Copy resident pages to the destination from all source processors in the
|
||||
// src_processor_mask
|
||||
//
|
||||
@ -3387,36 +4071,53 @@ static NV_STATUS block_copy_resident_pages_mask(uvm_va_block_t *block,
|
||||
uvm_va_space_t *va_space = uvm_va_block_get_va_space(block);
|
||||
uvm_processor_id_t src_id;
|
||||
uvm_processor_mask_t search_mask;
|
||||
uvm_page_mask_t *copy_mask = &block_context->make_resident.copy_resident_pages_mask;
|
||||
|
||||
uvm_processor_mask_copy(&search_mask, src_processor_mask);
|
||||
|
||||
*copied_pages_out = 0;
|
||||
|
||||
for_each_closest_id(src_id, &search_mask, dst_id, va_space) {
|
||||
uvm_page_mask_t *src_resident_mask = uvm_va_block_resident_mask_get(block, src_id);
|
||||
NV_STATUS status;
|
||||
NvU32 copied_pages_from_src;
|
||||
|
||||
UVM_ASSERT(!uvm_id_equal(src_id, dst_id));
|
||||
|
||||
uvm_page_mask_init_from_region(copy_mask, region, src_resident_mask);
|
||||
if (UVM_ID_IS_CPU(src_id)) {
|
||||
int nid;
|
||||
|
||||
if (page_mask)
|
||||
uvm_page_mask_and(copy_mask, copy_mask, page_mask);
|
||||
for_each_possible_uvm_node(nid) {
|
||||
status = block_copy_resident_pages_from(block,
|
||||
block_context,
|
||||
dst_id,
|
||||
src_id,
|
||||
nid,
|
||||
region,
|
||||
page_mask,
|
||||
prefetch_page_mask,
|
||||
transfer_mode,
|
||||
migrated_pages,
|
||||
copied_pages_out,
|
||||
tracker_out);
|
||||
|
||||
if (status != NV_OK)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
status = block_copy_resident_pages_from(block,
|
||||
block_context,
|
||||
dst_id,
|
||||
src_id,
|
||||
NUMA_NO_NODE,
|
||||
region,
|
||||
page_mask,
|
||||
prefetch_page_mask,
|
||||
transfer_mode,
|
||||
migrated_pages,
|
||||
copied_pages_out,
|
||||
tracker_out);
|
||||
|
||||
}
|
||||
|
||||
status = block_copy_resident_pages_between(block,
|
||||
block_context,
|
||||
dst_id,
|
||||
src_id,
|
||||
region,
|
||||
copy_mask,
|
||||
prefetch_page_mask,
|
||||
transfer_mode,
|
||||
migrated_pages,
|
||||
&copied_pages_from_src,
|
||||
tracker_out);
|
||||
*copied_pages_out += copied_pages_from_src;
|
||||
UVM_ASSERT(*copied_pages_out <= max_pages_to_copy);
|
||||
|
||||
if (status != NV_OK)
|
||||
@ -3441,7 +4142,8 @@ static void break_read_duplication_in_region(uvm_va_block_t *block,
|
||||
|
||||
uvm_page_mask_init_from_region(break_pages_in_region, region, page_mask);
|
||||
|
||||
UVM_ASSERT(uvm_page_mask_subset(break_pages_in_region, uvm_va_block_resident_mask_get(block, dst_id)));
|
||||
UVM_ASSERT(
|
||||
uvm_page_mask_subset(break_pages_in_region, uvm_va_block_resident_mask_get(block, dst_id, NUMA_NO_NODE)));
|
||||
|
||||
// Clear read_duplicated bit for all pages in region
|
||||
uvm_page_mask_andnot(&block->read_duplicated_pages, &block->read_duplicated_pages, break_pages_in_region);
|
||||
@ -3453,9 +4155,20 @@ static void break_read_duplication_in_region(uvm_va_block_t *block,
|
||||
if (uvm_id_equal(id, dst_id))
|
||||
continue;
|
||||
|
||||
other_resident_mask = uvm_va_block_resident_mask_get(block, id);
|
||||
if (UVM_ID_IS_CPU(id)) {
|
||||
int nid;
|
||||
|
||||
if (!uvm_page_mask_andnot(other_resident_mask, other_resident_mask, break_pages_in_region))
|
||||
for_each_possible_uvm_node(nid)
|
||||
uvm_va_block_cpu_clear_resident_mask(block, nid, break_pages_in_region);
|
||||
|
||||
other_resident_mask = uvm_va_block_resident_mask_get(block, UVM_ID_CPU, NUMA_NO_NODE);
|
||||
}
|
||||
else {
|
||||
other_resident_mask = uvm_va_block_resident_mask_get(block, id, NUMA_NO_NODE);
|
||||
uvm_page_mask_andnot(other_resident_mask, other_resident_mask, break_pages_in_region);
|
||||
}
|
||||
|
||||
if (uvm_page_mask_empty(other_resident_mask))
|
||||
block_clear_resident_processor(block, id);
|
||||
}
|
||||
}
|
||||
@ -3467,7 +4180,7 @@ static void block_copy_set_first_touch_residency(uvm_va_block_t *block,
|
||||
const uvm_page_mask_t *page_mask)
|
||||
{
|
||||
uvm_page_index_t page_index;
|
||||
uvm_page_mask_t *resident_mask = uvm_va_block_resident_mask_get(block, dst_id);
|
||||
uvm_page_mask_t *resident_mask = uvm_va_block_resident_mask_get(block, dst_id, NUMA_NO_NODE);
|
||||
uvm_page_mask_t *first_touch_mask = &block_context->make_resident.page_mask;
|
||||
|
||||
if (page_mask)
|
||||
@ -3483,7 +4196,14 @@ static void block_copy_set_first_touch_residency(uvm_va_block_t *block,
|
||||
UVM_ASSERT(block_check_resident_proximity(block, page_index, dst_id));
|
||||
}
|
||||
|
||||
uvm_page_mask_or(resident_mask, resident_mask, first_touch_mask);
|
||||
if (UVM_ID_IS_CPU(dst_id)) {
|
||||
uvm_va_block_cpu_set_resident_all_chunks(block, block_context, first_touch_mask);
|
||||
resident_mask = uvm_va_block_resident_mask_get(block, UVM_ID_CPU, NUMA_NO_NODE);
|
||||
}
|
||||
else {
|
||||
uvm_page_mask_or(resident_mask, resident_mask, first_touch_mask);
|
||||
}
|
||||
|
||||
if (!uvm_page_mask_empty(resident_mask))
|
||||
block_set_resident_processor(block, dst_id);
|
||||
|
||||
@ -3493,6 +4213,41 @@ static void block_copy_set_first_touch_residency(uvm_va_block_t *block,
|
||||
first_touch_mask);
|
||||
}
|
||||
|
||||
// Select the set of CPU pages to be used for the migration. The pages selected
|
||||
// could be used for either CPU destination pages (when the destination of the
|
||||
// migration is the CPU) or staging pages (when the migration to the destination
|
||||
// processor requires staging through the CPU).
|
||||
static void block_select_cpu_node_pages(uvm_va_block_t *block,
|
||||
uvm_va_block_context_t *block_context,
|
||||
const uvm_page_mask_t *page_mask,
|
||||
uvm_va_block_region_t region)
|
||||
{
|
||||
uvm_va_block_cpu_node_state_t *node_state;
|
||||
uvm_make_resident_page_tracking_t *tracking = &block_context->make_resident.cpu_pages_used;
|
||||
uvm_page_mask_t **node_masks = tracking->node_masks;
|
||||
uvm_page_mask_t *scratch_page_mask = &block_context->scratch_page_mask;
|
||||
size_t index;
|
||||
int nid;
|
||||
|
||||
nodes_clear(tracking->nodes);
|
||||
|
||||
if (uvm_page_mask_empty(page_mask))
|
||||
return;
|
||||
|
||||
block_context->scratch_node_mask = node_possible_map;
|
||||
uvm_page_mask_init_from_region(scratch_page_mask, region, page_mask);
|
||||
|
||||
for_each_closest_uvm_node(nid, uvm_va_block_context_get_node(block_context), block_context->scratch_node_mask) {
|
||||
node_state = block_node_state_get(block, nid);
|
||||
index = node_to_index(nid);
|
||||
if (uvm_page_mask_and(node_masks[index], scratch_page_mask, &node_state->allocated)) {
|
||||
node_set(nid, tracking->nodes);
|
||||
if (!uvm_page_mask_andnot(scratch_page_mask, scratch_page_mask, node_masks[index]))
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy resident pages from other processors to the destination.
|
||||
// All the pages on the destination need to be populated by the caller first.
|
||||
// Pages not resident anywhere else need to be zeroed out as well.
|
||||
@ -3509,17 +4264,18 @@ static NV_STATUS block_copy_resident_pages(uvm_va_block_t *block,
|
||||
NV_STATUS status = NV_OK;
|
||||
NV_STATUS tracker_status;
|
||||
uvm_tracker_t local_tracker = UVM_TRACKER_INIT();
|
||||
uvm_page_mask_t *resident_mask = uvm_va_block_resident_mask_get(block, dst_id);
|
||||
uvm_page_mask_t *resident_mask = uvm_va_block_resident_mask_get(block, dst_id, NUMA_NO_NODE);
|
||||
NvU32 missing_pages_count;
|
||||
NvU32 pages_copied;
|
||||
NvU32 pages_copied_to_cpu;
|
||||
NvU32 pages_copied_to_cpu = 0;
|
||||
uvm_processor_mask_t src_processor_mask;
|
||||
uvm_page_mask_t *copy_page_mask = &block_context->make_resident.page_mask;
|
||||
uvm_page_mask_t *migrated_pages = &block_context->make_resident.pages_migrated;
|
||||
uvm_page_mask_t *staged_pages = &block_context->make_resident.pages_staged;
|
||||
uvm_page_mask_t *pages_staged = &block_context->make_resident.pages_staged;
|
||||
uvm_page_mask_t *cpu_page_mask;
|
||||
int nid;
|
||||
|
||||
uvm_page_mask_zero(migrated_pages);
|
||||
uvm_page_mask_zero(staged_pages);
|
||||
|
||||
if (page_mask)
|
||||
uvm_page_mask_andnot(copy_page_mask, page_mask, resident_mask);
|
||||
@ -3538,7 +4294,7 @@ static NV_STATUS block_copy_resident_pages(uvm_va_block_t *block,
|
||||
|
||||
uvm_processor_mask_zero(&src_processor_mask);
|
||||
|
||||
if (!uvm_id_equal(dst_id, UVM_ID_CPU)) {
|
||||
if (UVM_ID_IS_GPU(dst_id)) {
|
||||
// If the destination is a GPU, first copy everything from processors
|
||||
// with copy access supported. Notably this will copy pages from the CPU
|
||||
// as well even if later some extra copies from CPU are required for
|
||||
@ -3546,6 +4302,15 @@ static NV_STATUS block_copy_resident_pages(uvm_va_block_t *block,
|
||||
uvm_processor_mask_and(&src_processor_mask, block_get_can_copy_from_mask(block, dst_id), &block->resident);
|
||||
uvm_processor_mask_clear(&src_processor_mask, dst_id);
|
||||
|
||||
cpu_page_mask = pages_staged;
|
||||
}
|
||||
else {
|
||||
cpu_page_mask = copy_page_mask;
|
||||
}
|
||||
|
||||
block_select_cpu_node_pages(block, block_context, cpu_page_mask, region);
|
||||
|
||||
if (UVM_ID_IS_GPU(dst_id)) {
|
||||
status = block_copy_resident_pages_mask(block,
|
||||
block_context,
|
||||
dst_id,
|
||||
@ -3565,8 +4330,10 @@ static NV_STATUS block_copy_resident_pages(uvm_va_block_t *block,
|
||||
if (status != NV_OK)
|
||||
goto out;
|
||||
|
||||
if (missing_pages_count == 0)
|
||||
if (missing_pages_count == 0) {
|
||||
UVM_ASSERT(uvm_page_mask_empty(pages_staged));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (pages_copied)
|
||||
uvm_page_mask_andnot(copy_page_mask, copy_page_mask, migrated_pages);
|
||||
@ -3579,28 +4346,27 @@ static NV_STATUS block_copy_resident_pages(uvm_va_block_t *block,
|
||||
uvm_processor_mask_clear(&src_processor_mask, dst_id);
|
||||
uvm_processor_mask_clear(&src_processor_mask, UVM_ID_CPU);
|
||||
|
||||
status = block_copy_resident_pages_mask(block,
|
||||
block_context,
|
||||
UVM_ID_CPU,
|
||||
&src_processor_mask,
|
||||
region,
|
||||
copy_page_mask,
|
||||
prefetch_page_mask,
|
||||
transfer_mode,
|
||||
missing_pages_count,
|
||||
staged_pages,
|
||||
&pages_copied_to_cpu,
|
||||
&local_tracker);
|
||||
if (status != NV_OK)
|
||||
goto out;
|
||||
if (!uvm_page_mask_empty(cpu_page_mask)) {
|
||||
status = block_copy_resident_pages_mask(block,
|
||||
block_context,
|
||||
UVM_ID_CPU,
|
||||
&src_processor_mask,
|
||||
region,
|
||||
cpu_page_mask,
|
||||
prefetch_page_mask,
|
||||
transfer_mode,
|
||||
missing_pages_count,
|
||||
UVM_ID_IS_CPU(dst_id) ? migrated_pages : NULL,
|
||||
&pages_copied_to_cpu,
|
||||
&local_tracker);
|
||||
|
||||
if (status != NV_OK)
|
||||
goto out;
|
||||
}
|
||||
|
||||
// If destination is the CPU then we copied everything there above
|
||||
if (UVM_ID_IS_CPU(dst_id)) {
|
||||
uvm_page_mask_or(migrated_pages, migrated_pages, staged_pages);
|
||||
missing_pages_count -= pages_copied_to_cpu;
|
||||
|
||||
if (!UVM_ID_IS_GPU(dst_id))
|
||||
goto out;
|
||||
}
|
||||
|
||||
// Add everything to the block's tracker so that the
|
||||
// block_copy_resident_pages_between() call below will acquire it.
|
||||
@ -3610,20 +4376,37 @@ static NV_STATUS block_copy_resident_pages(uvm_va_block_t *block,
|
||||
uvm_tracker_clear(&local_tracker);
|
||||
|
||||
// Now copy staged pages from the CPU to the destination.
|
||||
status = block_copy_resident_pages_between(block,
|
||||
block_context,
|
||||
dst_id,
|
||||
UVM_ID_CPU,
|
||||
region,
|
||||
staged_pages,
|
||||
prefetch_page_mask,
|
||||
transfer_mode,
|
||||
migrated_pages,
|
||||
&pages_copied,
|
||||
&local_tracker);
|
||||
// The staging copy above could have allocated pages on any NUMA node.
|
||||
// Loop over all nodes where pages were allocated and copy from those
|
||||
// nodes.
|
||||
pages_copied = 0;
|
||||
for_each_node_mask(nid, block_context->make_resident.cpu_pages_used.nodes) {
|
||||
NvU32 pages_copied_from_node;
|
||||
uvm_page_mask_t *node_pages_mask = &block_context->make_resident.node_pages_mask;
|
||||
uvm_page_mask_t *node_alloc_mask = block_context->make_resident.cpu_pages_used.node_masks[node_to_index(nid)];
|
||||
|
||||
UVM_ASSERT(missing_pages_count >= pages_copied);
|
||||
missing_pages_count -= pages_copied;
|
||||
if (uvm_page_mask_and(node_pages_mask, pages_staged, node_alloc_mask)) {
|
||||
status = block_copy_resident_pages_between(block,
|
||||
block_context,
|
||||
dst_id,
|
||||
NUMA_NO_NODE,
|
||||
UVM_ID_CPU,
|
||||
nid,
|
||||
region,
|
||||
node_pages_mask,
|
||||
prefetch_page_mask,
|
||||
transfer_mode,
|
||||
migrated_pages,
|
||||
&pages_copied_from_node,
|
||||
&local_tracker);
|
||||
UVM_ASSERT(missing_pages_count >= pages_copied_from_node);
|
||||
missing_pages_count -= pages_copied_from_node;
|
||||
pages_copied += pages_copied_from_node;
|
||||
}
|
||||
|
||||
if (status != NV_OK)
|
||||
break;
|
||||
}
|
||||
|
||||
if (status != NV_OK)
|
||||
goto out;
|
||||
@ -3668,7 +4451,7 @@ NV_STATUS uvm_va_block_make_resident_copy(uvm_va_block_t *va_block,
|
||||
uvm_assert_mutex_locked(&va_block->lock);
|
||||
UVM_ASSERT(uvm_va_block_is_hmm(va_block) || va_block->va_range->type == UVM_VA_RANGE_TYPE_MANAGED);
|
||||
|
||||
resident_mask = block_resident_mask_get_alloc(va_block, dest_id);
|
||||
resident_mask = block_resident_mask_get_alloc(va_block, dest_id, NUMA_NO_NODE);
|
||||
if (!resident_mask)
|
||||
return NV_ERR_NO_MEMORY;
|
||||
|
||||
@ -3740,9 +4523,17 @@ static void block_make_resident_update_state(uvm_va_block_t *va_block,
|
||||
uvm_page_mask_t *copy_mask,
|
||||
uvm_make_resident_cause_t cause)
|
||||
{
|
||||
uvm_page_mask_t *dst_resident_mask = uvm_va_block_resident_mask_get(va_block, dst_id);
|
||||
if (UVM_ID_IS_CPU(dst_id)) {
|
||||
// CPU chunks may not have been allocated on the preferred NUMA node. So,
|
||||
// the residency has to be updated based on the chunk's NUMA ID.
|
||||
uvm_va_block_cpu_set_resident_all_chunks(va_block, va_block_context, copy_mask);
|
||||
}
|
||||
else {
|
||||
uvm_page_mask_t *dst_resident_mask = uvm_va_block_resident_mask_get(va_block, dst_id, NUMA_NO_NODE);
|
||||
|
||||
uvm_page_mask_or(dst_resident_mask, dst_resident_mask, copy_mask);
|
||||
}
|
||||
|
||||
uvm_page_mask_or(dst_resident_mask, dst_resident_mask, copy_mask);
|
||||
block_set_resident_processor(va_block, dst_id);
|
||||
|
||||
// Accumulate the pages that migrated into the output mask.
|
||||
@ -3752,7 +4543,8 @@ static void block_make_resident_update_state(uvm_va_block_t *va_block,
|
||||
|
||||
// Any move operation implies that mappings have been removed from all
|
||||
// non-UVM-Lite GPUs.
|
||||
uvm_page_mask_andnot(&va_block->maybe_mapped_pages, &va_block->maybe_mapped_pages, copy_mask);
|
||||
if (!uvm_va_block_is_hmm(va_block))
|
||||
uvm_page_mask_andnot(&va_block->maybe_mapped_pages, &va_block->maybe_mapped_pages, copy_mask);
|
||||
|
||||
// If we are migrating due to an eviction, set the GPU as evicted and
|
||||
// mark the evicted pages. If we are migrating away from the CPU this
|
||||
@ -3814,6 +4606,10 @@ void uvm_va_block_make_resident_finish(uvm_va_block_t *va_block,
|
||||
// empty).
|
||||
if (uvm_processor_mask_test(&va_block->resident, dst_id))
|
||||
block_mark_memory_used(va_block, dst_id);
|
||||
|
||||
// Check state of all chunks after residency change.
|
||||
// TODO: Bug 4207783: Check both CPU and GPU chunks.
|
||||
UVM_ASSERT(block_check_cpu_chunks(va_block));
|
||||
}
|
||||
|
||||
NV_STATUS uvm_va_block_make_resident(uvm_va_block_t *va_block,
|
||||
@ -3906,7 +4702,6 @@ NV_STATUS uvm_va_block_make_resident_read_duplicate(uvm_va_block_t *va_block,
|
||||
NV_STATUS status = NV_OK;
|
||||
uvm_processor_id_t src_id;
|
||||
uvm_page_mask_t *dst_resident_mask;
|
||||
uvm_page_mask_t *cpu_resident_mask;
|
||||
uvm_page_mask_t *migrated_pages;
|
||||
uvm_page_mask_t *staged_pages;
|
||||
uvm_page_mask_t *first_touch_mask;
|
||||
@ -3938,7 +4733,7 @@ NV_STATUS uvm_va_block_make_resident_read_duplicate(uvm_va_block_t *va_block,
|
||||
// block_copy_resident_pages also use
|
||||
// va_block_context->make_resident.page_mask.
|
||||
uvm_page_mask_t *preprocess_page_mask = &va_block_context->make_resident.page_mask;
|
||||
const uvm_page_mask_t *resident_mask = uvm_va_block_resident_mask_get(va_block, src_id);
|
||||
const uvm_page_mask_t *resident_mask = uvm_va_block_resident_mask_get(va_block, src_id, NUMA_NO_NODE);
|
||||
UVM_ASSERT(!uvm_page_mask_empty(resident_mask));
|
||||
|
||||
if (page_mask)
|
||||
@ -3987,16 +4782,21 @@ NV_STATUS uvm_va_block_make_resident_read_duplicate(uvm_va_block_t *va_block,
|
||||
|
||||
staged_pages = &va_block_context->make_resident.pages_staged;
|
||||
if (!UVM_ID_IS_CPU(dest_id) && !uvm_page_mask_empty(staged_pages)) {
|
||||
cpu_resident_mask = uvm_va_block_resident_mask_get(va_block, UVM_ID_CPU);
|
||||
uvm_page_mask_or(cpu_resident_mask, cpu_resident_mask, staged_pages);
|
||||
uvm_va_block_cpu_set_resident_all_chunks(va_block, va_block_context, staged_pages);
|
||||
block_set_resident_processor(va_block, UVM_ID_CPU);
|
||||
uvm_page_mask_or(&va_block->read_duplicated_pages, &va_block->read_duplicated_pages, staged_pages);
|
||||
uvm_tools_record_read_duplicate(va_block, UVM_ID_CPU, region, staged_pages);
|
||||
}
|
||||
|
||||
if (!uvm_page_mask_empty(migrated_pages)) {
|
||||
dst_resident_mask = uvm_va_block_resident_mask_get(va_block, dest_id);
|
||||
uvm_page_mask_or(dst_resident_mask, dst_resident_mask, migrated_pages);
|
||||
if (UVM_ID_IS_CPU(dest_id)) {
|
||||
uvm_va_block_cpu_set_resident_all_chunks(va_block, va_block_context, migrated_pages);
|
||||
}
|
||||
else {
|
||||
dst_resident_mask = uvm_va_block_resident_mask_get(va_block, dest_id, NUMA_NO_NODE);
|
||||
uvm_page_mask_or(dst_resident_mask, dst_resident_mask, migrated_pages);
|
||||
}
|
||||
|
||||
block_set_resident_processor(va_block, dest_id);
|
||||
uvm_page_mask_or(&va_block->read_duplicated_pages, &va_block->read_duplicated_pages, migrated_pages);
|
||||
uvm_tools_record_read_duplicate(va_block, dest_id, region, migrated_pages);
|
||||
@ -4015,6 +4815,9 @@ NV_STATUS uvm_va_block_make_resident_read_duplicate(uvm_va_block_t *va_block,
|
||||
if (uvm_processor_mask_test(&va_block->resident, dest_id))
|
||||
block_mark_memory_used(va_block, dest_id);
|
||||
|
||||
// Check state of all chunks after residency change.
|
||||
// TODO: Bug 4207783: Check both CPU and GPU chunks.
|
||||
UVM_ASSERT(block_check_cpu_chunks(va_block));
|
||||
return NV_OK;
|
||||
}
|
||||
|
||||
@ -4301,7 +5104,7 @@ static bool block_check_mappings_page(uvm_va_block_t *block, uvm_page_index_t pa
|
||||
|
||||
// Pages set to zero in maybe_mapped_pages must not be mapped on any
|
||||
// non-UVM-Lite GPU
|
||||
if (!uvm_page_mask_test(&block->maybe_mapped_pages, page_index)) {
|
||||
if (!uvm_va_block_is_hmm(block) && !uvm_page_mask_test(&block->maybe_mapped_pages, page_index)) {
|
||||
UVM_ASSERT_MSG(uvm_processor_mask_get_count(&read_mappings) == 0,
|
||||
"Resident: 0x%lx - Mappings Block: 0x%lx / Page R: 0x%lx W: 0x%lx A: 0x%lx\n",
|
||||
*resident_processors.bitmap,
|
||||
@ -4346,7 +5149,9 @@ static bool block_check_mappings_page(uvm_va_block_t *block, uvm_page_index_t pa
|
||||
if (uvm_processor_mask_test(&va_space->indirect_peers[uvm_id_value(residency)], id)) {
|
||||
uvm_gpu_t *resident_gpu = uvm_va_space_get_gpu(va_space, residency);
|
||||
uvm_gpu_t *mapped_gpu = uvm_va_space_get_gpu(va_space, id);
|
||||
uvm_gpu_chunk_t *chunk = block_phys_page_chunk(block, block_phys_page(residency, page_index), NULL);
|
||||
uvm_gpu_chunk_t *chunk = block_phys_page_chunk(block,
|
||||
block_phys_page(residency, NUMA_NO_NODE, page_index),
|
||||
NULL);
|
||||
|
||||
// This function will assert if no mapping exists
|
||||
(void)uvm_pmm_gpu_indirect_peer_addr(&resident_gpu->pmm, chunk, mapped_gpu);
|
||||
@ -4519,7 +5324,7 @@ static bool block_check_mappings_ptes(uvm_va_block_t *block, uvm_gpu_t *gpu)
|
||||
|
||||
// The mapped processor should be fully resident and physically-
|
||||
// contiguous.
|
||||
UVM_ASSERT(uvm_page_mask_full(uvm_va_block_resident_mask_get(block, resident_id)));
|
||||
UVM_ASSERT(uvm_page_mask_full(uvm_va_block_resident_mask_get(block, resident_id, NUMA_NO_NODE)));
|
||||
|
||||
if (UVM_ID_IS_GPU(resident_id)) {
|
||||
resident_gpu_state = uvm_va_block_gpu_state_get(block, resident_id);
|
||||
@ -4527,13 +5332,15 @@ static bool block_check_mappings_ptes(uvm_va_block_t *block, uvm_gpu_t *gpu)
|
||||
UVM_ASSERT(uvm_gpu_chunk_get_size(resident_gpu_state->chunks[0]) == UVM_CHUNK_SIZE_2M);
|
||||
}
|
||||
else {
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_first_in_region(block,
|
||||
uvm_va_block_region_from_block(block),
|
||||
NULL);
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page_resident(block, 0);
|
||||
int chunk_nid = uvm_cpu_chunk_get_numa_node(chunk);
|
||||
|
||||
UVM_ASSERT(uvm_page_mask_full(&block->cpu.allocated));
|
||||
UVM_ASSERT(chunk);
|
||||
UVM_ASSERT(uvm_cpu_chunk_get_size(chunk) == UVM_CHUNK_SIZE_2M);
|
||||
UVM_ASSERT(uvm_va_block_cpu_is_region_resident_on(block,
|
||||
chunk_nid,
|
||||
uvm_va_block_region_from_block(block)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4572,19 +5379,28 @@ static bool block_check_mappings_ptes(uvm_va_block_t *block, uvm_gpu_t *gpu)
|
||||
// location even if the memory is resident elsewhere. Skip the
|
||||
// residency check but still verify contiguity.
|
||||
if (!uvm_processor_mask_test(block_get_uvm_lite_gpus(block), gpu->id)) {
|
||||
UVM_ASSERT(uvm_page_mask_region_full(uvm_va_block_resident_mask_get(block, resident_id),
|
||||
big_region));
|
||||
UVM_ASSERT(
|
||||
uvm_page_mask_region_full(uvm_va_block_resident_mask_get(block, resident_id, NUMA_NO_NODE),
|
||||
big_region));
|
||||
}
|
||||
|
||||
if (UVM_ID_IS_CPU(resident_id)) {
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(block, big_region.first);
|
||||
int resident_nid = block_get_page_node_residency(block, big_region.first);
|
||||
uvm_va_block_cpu_node_state_t *node_state = block_node_state_get(block, resident_nid);
|
||||
uvm_cpu_chunk_t *chunk;
|
||||
|
||||
UVM_ASSERT(resident_nid != NUMA_NO_NODE);
|
||||
UVM_ASSERT(uvm_page_mask_region_full(&node_state->allocated, big_region));
|
||||
chunk = uvm_cpu_chunk_get_chunk_for_page(block, resident_nid, big_region.first);
|
||||
UVM_ASSERT(gpu->parent->can_map_sysmem_with_large_pages);
|
||||
UVM_ASSERT(uvm_cpu_chunk_get_size(chunk) >= uvm_va_block_region_size(big_region));
|
||||
UVM_ASSERT(uvm_page_mask_region_full(&node_state->resident, big_region));
|
||||
}
|
||||
else {
|
||||
// Check GPU chunks
|
||||
chunk = block_phys_page_chunk(block, block_phys_page(resident_id, big_region.first), NULL);
|
||||
chunk = block_phys_page_chunk(block,
|
||||
block_phys_page(resident_id, NUMA_NO_NODE, big_region.first),
|
||||
NULL);
|
||||
chunk_region = uvm_va_block_chunk_region(block, uvm_gpu_chunk_get_size(chunk), big_region.first);
|
||||
UVM_ASSERT(uvm_va_block_region_contains_region(chunk_region, big_region));
|
||||
}
|
||||
@ -4611,7 +5427,7 @@ static bool block_check_mappings(uvm_va_block_t *block)
|
||||
continue;
|
||||
}
|
||||
|
||||
resident_mask = uvm_va_block_resident_mask_get(block, id);
|
||||
resident_mask = uvm_va_block_resident_mask_get(block, id, NUMA_NO_NODE);
|
||||
UVM_ASSERT(uvm_processor_mask_test(&block->resident, id) == !uvm_page_mask_empty(resident_mask));
|
||||
|
||||
map_mask = uvm_va_block_map_mask_get(block, id);
|
||||
@ -4689,7 +5505,7 @@ static void block_unmap_cpu(uvm_va_block_t *block, uvm_va_block_region_t region,
|
||||
|
||||
// If the CPU is the only processor with mappings we can safely mark
|
||||
// the pages as fully unmapped
|
||||
if (num_mapped_processors == 1)
|
||||
if (num_mapped_processors == 1 && !uvm_va_block_is_hmm(block))
|
||||
uvm_page_mask_region_clear(&block->maybe_mapped_pages, subregion);
|
||||
|
||||
unmapped_something = true;
|
||||
@ -4799,6 +5615,7 @@ static void block_gpu_pte_write_4k(uvm_va_block_t *block,
|
||||
uvm_gpu_phys_address_t page_addr = {0};
|
||||
uvm_page_index_t page_index;
|
||||
NvU64 pte_flags = block_gpu_pte_flag_cacheable(block, gpu, resident_id);
|
||||
int contig_nid = NUMA_NO_NODE;
|
||||
|
||||
UVM_ASSERT(new_prot != UVM_PROT_NONE);
|
||||
UVM_ASSERT(UVM_ID_IS_VALID(resident_id));
|
||||
@ -4806,15 +5623,22 @@ static void block_gpu_pte_write_4k(uvm_va_block_t *block,
|
||||
for_each_va_block_page_in_mask(page_index, write_page_mask, block) {
|
||||
uvm_gpu_phys_address_t pte_addr;
|
||||
size_t i;
|
||||
int nid = NUMA_NO_NODE;
|
||||
|
||||
// Assume that this mapping will be used to write to the page
|
||||
if (new_prot > UVM_PROT_READ_ONLY && UVM_ID_IS_CPU(resident_id) && !uvm_va_block_is_hmm(block))
|
||||
block_mark_cpu_page_dirty(block, page_index);
|
||||
if (UVM_ID_IS_CPU(resident_id)) {
|
||||
nid = block_get_page_node_residency(block, page_index);
|
||||
UVM_ASSERT(nid != NUMA_NO_NODE);
|
||||
|
||||
if (page_index >= contig_region.outer) {
|
||||
contig_region = block_phys_contig_region(block, page_index, resident_id);
|
||||
contig_addr = block_phys_page_address(block, block_phys_page(resident_id, contig_region.first), gpu);
|
||||
// Assume that this mapping will be used to write to the page
|
||||
if (new_prot > UVM_PROT_READ_ONLY && !uvm_va_block_is_hmm(block))
|
||||
block_mark_cpu_page_dirty(block, page_index, nid);
|
||||
}
|
||||
|
||||
if (page_index >= contig_region.outer || nid != contig_nid) {
|
||||
contig_region = block_phys_contig_region(block, page_index, resident_id, nid);
|
||||
contig_addr = block_phys_page_address(block, block_phys_page(resident_id, nid, contig_region.first), gpu);
|
||||
page_addr = contig_addr;
|
||||
contig_nid = nid;
|
||||
}
|
||||
|
||||
page_addr.address = contig_addr.address + (page_index - contig_region.first) * PAGE_SIZE;
|
||||
@ -4998,6 +5822,7 @@ static void block_gpu_pte_write_big(uvm_va_block_t *block,
|
||||
uvm_gpu_phys_address_t contig_addr = {0};
|
||||
uvm_gpu_phys_address_t page_addr = {0};
|
||||
NvU64 pte_flags = block_gpu_pte_flag_cacheable(block, gpu, resident_id);
|
||||
int contig_nid = NUMA_NO_NODE;
|
||||
|
||||
UVM_ASSERT(new_prot != UVM_PROT_NONE);
|
||||
UVM_ASSERT(UVM_ID_IS_VALID(resident_id));
|
||||
@ -5014,19 +5839,26 @@ static void block_gpu_pte_write_big(uvm_va_block_t *block,
|
||||
NvU64 pte_val;
|
||||
uvm_gpu_phys_address_t pte_addr;
|
||||
uvm_va_block_region_t big_region = uvm_va_block_big_page_region(block, big_page_index, big_page_size);
|
||||
int nid = NUMA_NO_NODE;
|
||||
|
||||
// Assume that this mapping will be used to write to the page
|
||||
if (new_prot > UVM_PROT_READ_ONLY && UVM_ID_IS_CPU(resident_id) && !uvm_va_block_is_hmm(block)) {
|
||||
uvm_page_index_t page_index;
|
||||
if (UVM_ID_IS_CPU(resident_id)) {
|
||||
nid = block_get_page_node_residency(block, big_region.first);
|
||||
UVM_ASSERT(nid != NUMA_NO_NODE);
|
||||
|
||||
for_each_va_block_page_in_region(page_index, big_region)
|
||||
block_mark_cpu_page_dirty(block, page_index);
|
||||
// Assume that this mapping will be used to write to the page
|
||||
if (new_prot > UVM_PROT_READ_ONLY && !uvm_va_block_is_hmm(block)) {
|
||||
uvm_page_index_t page_index;
|
||||
|
||||
for_each_va_block_page_in_region(page_index, big_region)
|
||||
block_mark_cpu_page_dirty(block, page_index, nid);
|
||||
}
|
||||
}
|
||||
|
||||
if (big_region.first >= contig_region.outer) {
|
||||
contig_region = block_phys_contig_region(block, big_region.first, resident_id);
|
||||
contig_addr = block_phys_page_address(block, block_phys_page(resident_id, contig_region.first), gpu);
|
||||
if (big_region.first >= contig_region.outer || nid != contig_nid) {
|
||||
contig_region = block_phys_contig_region(block, big_region.first, resident_id, nid);
|
||||
contig_addr = block_phys_page_address(block, block_phys_page(resident_id, nid, contig_region.first), gpu);
|
||||
page_addr = contig_addr;
|
||||
contig_nid = nid;
|
||||
}
|
||||
|
||||
page_addr.address = contig_addr.address + (big_region.first - contig_region.first) * PAGE_SIZE;
|
||||
@ -5164,14 +5996,19 @@ static void block_gpu_pte_write_2m(uvm_va_block_t *block,
|
||||
NvU32 pte_size = uvm_mmu_pte_size(tree, UVM_PAGE_SIZE_2M);
|
||||
NvU64 pte_val;
|
||||
NvU64 pte_flags = block_gpu_pte_flag_cacheable(block, gpu, resident_id);
|
||||
int nid = NUMA_NO_NODE;
|
||||
|
||||
UVM_ASSERT(new_prot != UVM_PROT_NONE);
|
||||
UVM_ASSERT(UVM_ID_IS_VALID(resident_id));
|
||||
|
||||
if (UVM_ID_IS_CPU(resident_id) && !uvm_va_block_is_hmm(block))
|
||||
block_mark_cpu_page_dirty(block, 0);
|
||||
if (UVM_ID_IS_CPU(resident_id)) {
|
||||
nid = block_get_page_node_residency(block, 0);
|
||||
UVM_ASSERT(nid != NUMA_NO_NODE);
|
||||
if (!uvm_va_block_is_hmm(block))
|
||||
block_mark_cpu_page_dirty(block, 0, nid);
|
||||
}
|
||||
|
||||
page_addr = block_phys_page_address(block, block_phys_page(resident_id, 0), gpu);
|
||||
page_addr = block_phys_page_address(block, block_phys_page(resident_id, nid, 0), gpu);
|
||||
pte_val = tree->hal->make_pte(page_addr.aperture, page_addr.address, new_prot, pte_flags);
|
||||
uvm_pte_batch_write_pte(pte_batch, pte_addr, pte_val, pte_size);
|
||||
|
||||
@ -6195,9 +7032,9 @@ static void block_gpu_compute_new_pte_state(uvm_va_block_t *block,
|
||||
|
||||
// If all pages in the 2M mask have the same attributes after the
|
||||
// operation is applied, we can use a 2M PTE.
|
||||
if (block_gpu_supports_2m(block, gpu) &&
|
||||
uvm_page_mask_full(page_mask_after) &&
|
||||
(UVM_ID_IS_INVALID(resident_id) || is_block_phys_contig(block, resident_id))) {
|
||||
if (block_gpu_supports_2m(block, gpu) && uvm_page_mask_full(page_mask_after) &&
|
||||
(UVM_ID_IS_INVALID(resident_id) ||
|
||||
is_block_phys_contig(block, resident_id, block_get_page_node_residency(block, 0)))) {
|
||||
new_pte_state->pte_is_2m = true;
|
||||
new_pte_state->needs_4k = false;
|
||||
return;
|
||||
@ -6233,14 +7070,18 @@ static void block_gpu_compute_new_pte_state(uvm_va_block_t *block,
|
||||
can_make_new_big_ptes = false;
|
||||
|
||||
for_each_va_block_page_in_region_mask(page_index, pages_changing, big_region_all) {
|
||||
uvm_va_block_region_t contig_region = {0};
|
||||
uvm_cpu_chunk_t *chunk = NULL;
|
||||
int nid;
|
||||
|
||||
if (UVM_ID_IS_CPU(resident_id)) {
|
||||
nid = block_get_page_node_residency(block, page_index);
|
||||
UVM_ASSERT(nid != NUMA_NO_NODE);
|
||||
chunk = uvm_cpu_chunk_get_chunk_for_page(block, nid, page_index);
|
||||
}
|
||||
|
||||
big_page_index = uvm_va_block_big_page_index(block, page_index, big_page_size);
|
||||
big_page_region = uvm_va_block_big_page_region(block, big_page_index, big_page_size);
|
||||
|
||||
if (!UVM_ID_IS_INVALID(resident_id))
|
||||
contig_region = block_phys_contig_region(block, page_index, resident_id);
|
||||
|
||||
__set_bit(big_page_index, new_pte_state->big_ptes_covered);
|
||||
|
||||
// When mapping sysmem, we can use big pages only if we are mapping all
|
||||
@ -6249,9 +7090,9 @@ static void block_gpu_compute_new_pte_state(uvm_va_block_t *block,
|
||||
if (can_make_new_big_ptes &&
|
||||
uvm_page_mask_region_full(page_mask_after, big_page_region) &&
|
||||
(!UVM_ID_IS_CPU(resident_id) ||
|
||||
(contig_region.first <= big_page_region.first && contig_region.outer >= big_page_region.outer))) {
|
||||
(uvm_cpu_chunk_get_size(chunk) >= big_page_size &&
|
||||
uvm_va_block_cpu_is_region_resident_on(block, nid, big_page_region))))
|
||||
__set_bit(big_page_index, new_pte_state->big_ptes);
|
||||
}
|
||||
|
||||
if (!test_bit(big_page_index, new_pte_state->big_ptes))
|
||||
new_pte_state->needs_4k = true;
|
||||
@ -6667,7 +7508,7 @@ static NV_STATUS block_unmap_gpu(uvm_va_block_t *block,
|
||||
|
||||
// If the GPU is the only non-UVM-Lite processor with mappings, we can
|
||||
// safely mark pages as fully unmapped
|
||||
if (uvm_processor_mask_get_count(&non_uvm_lite_gpus) == 1)
|
||||
if (uvm_processor_mask_get_count(&non_uvm_lite_gpus) == 1 && !uvm_va_block_is_hmm(block))
|
||||
uvm_page_mask_andnot(&block->maybe_mapped_pages, &block->maybe_mapped_pages, pages_to_unmap);
|
||||
}
|
||||
|
||||
@ -6803,7 +7644,7 @@ static struct page *block_page_get(uvm_va_block_t *block, block_phys_page_t bloc
|
||||
struct page *page;
|
||||
|
||||
if (UVM_ID_IS_CPU(block_page.processor)) {
|
||||
page = uvm_cpu_chunk_get_cpu_page(block, block_page.page_index);
|
||||
page = uvm_va_block_get_cpu_page(block, block_page.page_index);
|
||||
}
|
||||
else {
|
||||
uvm_va_space_t *va_space = uvm_va_block_get_va_space(block);
|
||||
@ -6853,6 +7694,7 @@ static NV_STATUS block_map_cpu_page_to(uvm_va_block_t *block,
|
||||
NV_STATUS status;
|
||||
NvU64 addr;
|
||||
struct page *page;
|
||||
int nid = NUMA_NO_NODE;
|
||||
|
||||
UVM_ASSERT((uvm_va_block_is_hmm(block) && hmm_vma) || va_range->type == UVM_VA_RANGE_TYPE_MANAGED);
|
||||
UVM_ASSERT(new_prot != UVM_PROT_NONE);
|
||||
@ -6885,16 +7727,21 @@ static NV_STATUS block_map_cpu_page_to(uvm_va_block_t *block,
|
||||
|
||||
UVM_ASSERT(va_range);
|
||||
|
||||
if (UVM_ID_IS_CPU(resident_id) && UVM_ID_IS_CPU(uvm_va_range_get_policy(va_range)->preferred_location)) {
|
||||
// Add the page's range group range to the range group's migrated list.
|
||||
uvm_range_group_range_t *rgr = uvm_range_group_range_find(va_space,
|
||||
uvm_va_block_cpu_page_address(block, page_index));
|
||||
if (rgr != NULL) {
|
||||
uvm_spin_lock(&rgr->range_group->migrated_ranges_lock);
|
||||
if (list_empty(&rgr->range_group_migrated_list_node))
|
||||
list_move_tail(&rgr->range_group_migrated_list_node, &rgr->range_group->migrated_ranges);
|
||||
uvm_spin_unlock(&rgr->range_group->migrated_ranges_lock);
|
||||
if (UVM_ID_IS_CPU(resident_id)) {
|
||||
if (UVM_ID_IS_CPU(uvm_va_range_get_policy(va_range)->preferred_location)) {
|
||||
// Add the page's range group range to the range group's migrated list.
|
||||
uvm_range_group_range_t *rgr = uvm_range_group_range_find(va_space,
|
||||
uvm_va_block_cpu_page_address(block, page_index));
|
||||
if (rgr != NULL) {
|
||||
uvm_spin_lock(&rgr->range_group->migrated_ranges_lock);
|
||||
if (list_empty(&rgr->range_group_migrated_list_node))
|
||||
list_move_tail(&rgr->range_group_migrated_list_node, &rgr->range_group->migrated_ranges);
|
||||
uvm_spin_unlock(&rgr->range_group->migrated_ranges_lock);
|
||||
}
|
||||
}
|
||||
|
||||
nid = block_get_page_node_residency(block, page_index);
|
||||
UVM_ASSERT(nid != NUMA_NO_NODE);
|
||||
}
|
||||
|
||||
// It's possible here that current->mm != vma->vm_mm. That can happen for
|
||||
@ -6923,7 +7770,7 @@ static NV_STATUS block_map_cpu_page_to(uvm_va_block_t *block,
|
||||
if (status != NV_OK)
|
||||
return status;
|
||||
|
||||
page = block_page_get(block, block_phys_page(resident_id, page_index));
|
||||
page = block_page_get(block, block_phys_page(resident_id, nid, page_index));
|
||||
return uvm_cpu_insert_page(vma, addr, page, new_prot);
|
||||
}
|
||||
|
||||
@ -6946,7 +7793,7 @@ static NV_STATUS block_map_cpu_to(uvm_va_block_t *block,
|
||||
uvm_va_space_t *va_space = uvm_va_block_get_va_space(block);
|
||||
uvm_page_index_t page_index;
|
||||
uvm_page_mask_t *pages_to_map = &block_context->mapping.page_mask;
|
||||
const uvm_page_mask_t *resident_mask = uvm_va_block_resident_mask_get(block, resident_id);
|
||||
const uvm_page_mask_t *resident_mask = uvm_va_block_resident_mask_get(block, resident_id, NUMA_NO_NODE);
|
||||
uvm_pte_bits_cpu_t prot_pte_bit = get_cpu_pte_bit_index(new_prot);
|
||||
uvm_pte_bits_cpu_t pte_bit;
|
||||
|
||||
@ -7012,7 +7859,8 @@ static NV_STATUS block_map_cpu_to(uvm_va_block_t *block,
|
||||
for (pte_bit = 0; pte_bit <= prot_pte_bit; pte_bit++)
|
||||
uvm_page_mask_or(&block->cpu.pte_bits[pte_bit], &block->cpu.pte_bits[pte_bit], pages_to_map);
|
||||
|
||||
uvm_page_mask_or(&block->maybe_mapped_pages, &block->maybe_mapped_pages, pages_to_map);
|
||||
if (!uvm_va_block_is_hmm(block))
|
||||
uvm_page_mask_or(&block->maybe_mapped_pages, &block->maybe_mapped_pages, pages_to_map);
|
||||
|
||||
UVM_ASSERT(block_check_mappings(block));
|
||||
|
||||
@ -7042,7 +7890,7 @@ static NV_STATUS block_map_gpu_to(uvm_va_block_t *va_block,
|
||||
uvm_push_t push;
|
||||
NV_STATUS status;
|
||||
uvm_page_mask_t *pages_to_map = &block_context->mapping.page_mask;
|
||||
const uvm_page_mask_t *resident_mask = uvm_va_block_resident_mask_get(va_block, resident_id);
|
||||
const uvm_page_mask_t *resident_mask = uvm_va_block_resident_mask_get(va_block, resident_id, NUMA_NO_NODE);
|
||||
uvm_pte_bits_gpu_t pte_bit;
|
||||
uvm_pte_bits_gpu_t prot_pte_bit = get_gpu_pte_bit_index(new_prot);
|
||||
uvm_va_block_new_pte_state_t *new_pte_state = &block_context->mapping.new_pte_state;
|
||||
@ -7139,8 +7987,10 @@ static NV_STATUS block_map_gpu_to(uvm_va_block_t *va_block,
|
||||
|
||||
uvm_processor_mask_set(&va_block->mapped, gpu->id);
|
||||
|
||||
// If we are mapping a UVM-Lite GPU do not update maybe_mapped_pages
|
||||
if (!uvm_processor_mask_test(block_get_uvm_lite_gpus(va_block), gpu->id))
|
||||
// If we are mapping a UVM-Lite GPU or HMM va_block, do not update
|
||||
// maybe_mapped_pages.
|
||||
if (!uvm_processor_mask_test(block_get_uvm_lite_gpus(va_block), gpu->id) &&
|
||||
!uvm_va_block_is_hmm(va_block))
|
||||
uvm_page_mask_or(&va_block->maybe_mapped_pages, &va_block->maybe_mapped_pages, pages_to_map);
|
||||
|
||||
// Remove all pages resident on this processor from the input mask, which
|
||||
@ -7389,7 +8239,7 @@ static NV_STATUS block_revoke_prot_gpu_to(uvm_va_block_t *va_block,
|
||||
uvm_prot_t new_prot = prot_to_revoke - 1;
|
||||
uvm_va_block_new_pte_state_t *new_pte_state = &block_context->mapping.new_pte_state;
|
||||
block_pte_op_t pte_op;
|
||||
const uvm_page_mask_t *resident_mask = uvm_va_block_resident_mask_get(va_block, resident_id);
|
||||
const uvm_page_mask_t *resident_mask = uvm_va_block_resident_mask_get(va_block, resident_id, NUMA_NO_NODE);
|
||||
uvm_page_mask_t *pages_to_revoke = &block_context->mapping.page_mask;
|
||||
|
||||
UVM_ASSERT(revoke_page_mask);
|
||||
@ -7663,7 +8513,7 @@ static void update_read_duplicated_pages_mask(uvm_va_block_t *block,
|
||||
if (uvm_id_equal(running_id, id))
|
||||
continue;
|
||||
|
||||
running_residency_mask = uvm_va_block_resident_mask_get(block, running_id);
|
||||
running_residency_mask = uvm_va_block_resident_mask_get(block, running_id, NUMA_NO_NODE);
|
||||
|
||||
if (first) {
|
||||
uvm_page_mask_copy(running_page_mask, running_residency_mask);
|
||||
@ -7920,8 +8770,8 @@ void uvm_va_block_disable_peer(uvm_va_block_t *va_block, uvm_gpu_t *gpu0, uvm_gp
|
||||
if (!uvm_va_block_gpu_state_get(va_block, gpu0->id) || !uvm_va_block_gpu_state_get(va_block, gpu1->id))
|
||||
return;
|
||||
|
||||
resident0 = uvm_va_block_resident_mask_get(va_block, gpu0->id);
|
||||
resident1 = uvm_va_block_resident_mask_get(va_block, gpu1->id);
|
||||
resident0 = uvm_va_block_resident_mask_get(va_block, gpu0->id, NUMA_NO_NODE);
|
||||
resident1 = uvm_va_block_resident_mask_get(va_block, gpu1->id, NUMA_NO_NODE);
|
||||
|
||||
// Unmap all pages resident on gpu1, but not on gpu0, from gpu0
|
||||
if (uvm_page_mask_andnot(unmap_page_mask, resident1, resident0)) {
|
||||
@ -7998,7 +8848,7 @@ void uvm_va_block_unmap_preferred_location_uvm_lite(uvm_va_block_t *va_block, uv
|
||||
static NV_STATUS block_evict_pages_from_gpu(uvm_va_block_t *va_block, uvm_gpu_t *gpu, struct mm_struct *mm)
|
||||
{
|
||||
NV_STATUS status = NV_OK;
|
||||
const uvm_page_mask_t *resident = uvm_va_block_resident_mask_get(va_block, gpu->id);
|
||||
const uvm_page_mask_t *resident = uvm_va_block_resident_mask_get(va_block, gpu->id, NUMA_NO_NODE);
|
||||
uvm_va_block_region_t region = uvm_va_block_region_from_block(va_block);
|
||||
uvm_va_block_region_t subregion;
|
||||
uvm_va_space_t *va_space = uvm_va_block_get_va_space(va_block);
|
||||
@ -8083,11 +8933,15 @@ void uvm_va_block_unregister_gpu(uvm_va_block_t *va_block, uvm_gpu_t *gpu, struc
|
||||
static void block_mark_region_cpu_dirty(uvm_va_block_t *va_block, uvm_va_block_region_t region)
|
||||
{
|
||||
uvm_page_index_t page_index;
|
||||
uvm_page_mask_t *resident_mask;
|
||||
|
||||
uvm_assert_mutex_locked(&va_block->lock);
|
||||
|
||||
for_each_va_block_page_in_region_mask (page_index, &va_block->cpu.resident, region)
|
||||
block_mark_cpu_page_dirty(va_block, page_index);
|
||||
resident_mask = uvm_va_block_resident_mask_get(va_block, UVM_ID_CPU, NUMA_NO_NODE);
|
||||
for_each_va_block_page_in_region_mask(page_index, resident_mask, region) {
|
||||
int nid = block_get_page_node_residency(va_block, page_index);
|
||||
UVM_ASSERT(nid != NUMA_NO_NODE);
|
||||
block_mark_cpu_page_dirty(va_block, page_index, nid);
|
||||
}
|
||||
}
|
||||
|
||||
// Tears down everything within the block, but doesn't free the block itself.
|
||||
@ -8105,6 +8959,7 @@ static void block_kill(uvm_va_block_t *block)
|
||||
uvm_va_block_region_t region = uvm_va_block_region_from_block(block);
|
||||
uvm_page_index_t page_index;
|
||||
uvm_page_index_t next_page_index;
|
||||
int nid;
|
||||
|
||||
if (uvm_va_block_is_dead(block))
|
||||
return;
|
||||
@ -8147,18 +9002,28 @@ static void block_kill(uvm_va_block_t *block)
|
||||
UVM_ASSERT(block_check_processor_not_mapped(block, UVM_ID_CPU));
|
||||
|
||||
// Free CPU pages
|
||||
for_each_cpu_chunk_in_block_safe(chunk, page_index, next_page_index, block) {
|
||||
// be conservative.
|
||||
// Tell the OS we wrote to the page because we sometimes clear the dirty
|
||||
// bit after writing to it. HMM dirty flags are managed by the kernel.
|
||||
if (!uvm_va_block_is_hmm(block))
|
||||
uvm_cpu_chunk_mark_dirty(chunk, 0);
|
||||
uvm_cpu_chunk_remove_from_block(block, page_index);
|
||||
uvm_cpu_chunk_free(chunk);
|
||||
for_each_possible_uvm_node(nid) {
|
||||
uvm_va_block_cpu_node_state_t *node_state = block_node_state_get(block, nid);
|
||||
size_t index = node_to_index(nid);
|
||||
|
||||
for_each_cpu_chunk_in_block_safe(chunk, page_index, next_page_index, block, nid) {
|
||||
// be conservative.
|
||||
// Tell the OS we wrote to the page because we sometimes clear the dirty
|
||||
// bit after writing to it. HMM dirty flags are managed by the kernel.
|
||||
if (!uvm_va_block_is_hmm(block))
|
||||
uvm_cpu_chunk_mark_dirty(chunk, 0);
|
||||
|
||||
uvm_cpu_chunk_remove_from_block(block, nid, page_index);
|
||||
uvm_cpu_chunk_free(chunk);
|
||||
}
|
||||
|
||||
UVM_ASSERT(uvm_page_mask_empty(&node_state->allocated));
|
||||
UVM_ASSERT(node_state->chunks == 0);
|
||||
kmem_cache_free(g_uvm_va_block_cpu_node_state_cache, block->cpu.node_state[index]);
|
||||
}
|
||||
|
||||
uvm_kvfree((void *)block->cpu.chunks);
|
||||
block->cpu.chunks = 0;
|
||||
uvm_kvfree((void *)block->cpu.node_state);
|
||||
block->cpu.node_state = NULL;
|
||||
|
||||
// Clearing the resident bit isn't strictly necessary since this block
|
||||
// is getting destroyed, but it keeps state consistent for assertions.
|
||||
@ -8185,15 +9050,7 @@ void uvm_va_block_destroy(nv_kref_t *nv_kref)
|
||||
uvm_mutex_lock(&block->lock);
|
||||
block_kill(block);
|
||||
uvm_mutex_unlock(&block->lock);
|
||||
|
||||
if (uvm_enable_builtin_tests) {
|
||||
uvm_va_block_wrapper_t *block_wrapper = container_of(block, uvm_va_block_wrapper_t, block);
|
||||
|
||||
kmem_cache_free(g_uvm_va_block_cache, block_wrapper);
|
||||
}
|
||||
else {
|
||||
kmem_cache_free(g_uvm_va_block_cache, block);
|
||||
}
|
||||
uvm_va_block_free(block);
|
||||
}
|
||||
|
||||
void uvm_va_block_kill(uvm_va_block_t *va_block)
|
||||
@ -8248,14 +9105,6 @@ void uvm_va_block_munmap_region(uvm_va_block_t *va_block,
|
||||
event_data.block_munmap.region = region;
|
||||
uvm_perf_event_notify(&va_space->perf_events, UVM_PERF_EVENT_BLOCK_MUNMAP, &event_data);
|
||||
|
||||
// Set a flag so that GPU fault events are flushed since they might refer
|
||||
// to the region being unmapped.
|
||||
// Note that holding the va_block lock prevents GPU VA spaces from
|
||||
// being removed so the registered_gpu_va_spaces mask is stable.
|
||||
for_each_gpu_id_in_mask(gpu_id, &va_space->registered_gpu_va_spaces) {
|
||||
uvm_processor_mask_set_atomic(&va_space->needs_fault_buffer_flush, gpu_id);
|
||||
}
|
||||
|
||||
// Release any remaining vidmem chunks in the given region.
|
||||
for_each_gpu_id(gpu_id) {
|
||||
uvm_va_block_gpu_state_t *gpu_state = uvm_va_block_gpu_state_get(va_block, gpu_id);
|
||||
@ -8545,14 +9394,15 @@ error:
|
||||
return status;
|
||||
}
|
||||
|
||||
static NV_STATUS block_split_cpu_chunk_to_64k(uvm_va_block_t *block)
|
||||
static NV_STATUS block_split_cpu_chunk_to_64k(uvm_va_block_t *block, int nid)
|
||||
{
|
||||
uvm_cpu_chunk_storage_mixed_t *mixed;
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(block, 0);
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(block, nid, 0);
|
||||
uvm_va_block_cpu_node_state_t *node_state = block_node_state_get(block, nid);
|
||||
NV_STATUS status;
|
||||
|
||||
UVM_ASSERT(uvm_cpu_chunk_get_size(chunk) == UVM_CHUNK_SIZE_2M);
|
||||
UVM_ASSERT(uvm_cpu_storage_get_type(block) == UVM_CPU_CHUNK_STORAGE_CHUNK);
|
||||
UVM_ASSERT(uvm_cpu_storage_get_type(node_state) == UVM_CPU_CHUNK_STORAGE_CHUNK);
|
||||
|
||||
mixed = uvm_kvmalloc_zero(sizeof(*mixed));
|
||||
if (!mixed)
|
||||
@ -8565,23 +9415,25 @@ static NV_STATUS block_split_cpu_chunk_to_64k(uvm_va_block_t *block)
|
||||
}
|
||||
|
||||
bitmap_fill(mixed->big_chunks, MAX_BIG_CPU_CHUNK_SLOTS_PER_UVM_VA_BLOCK);
|
||||
block->cpu.chunks = (unsigned long)mixed | UVM_CPU_CHUNK_STORAGE_MIXED;
|
||||
node_state->chunks = (unsigned long)mixed | UVM_CPU_CHUNK_STORAGE_MIXED;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static NV_STATUS block_split_cpu_chunk_to_4k(uvm_va_block_t *block, uvm_page_index_t page_index)
|
||||
static NV_STATUS block_split_cpu_chunk_to_4k(uvm_va_block_t *block, uvm_page_index_t page_index, int nid)
|
||||
{
|
||||
uvm_va_block_cpu_node_state_t *node_state = block_node_state_get(block, nid);
|
||||
uvm_cpu_chunk_storage_mixed_t *mixed;
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(block, page_index);
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(block, nid, page_index);
|
||||
uvm_cpu_chunk_t **small_chunks;
|
||||
size_t slot_index;
|
||||
NV_STATUS status;
|
||||
|
||||
UVM_ASSERT(chunk);
|
||||
UVM_ASSERT(uvm_cpu_chunk_get_size(chunk) == UVM_CHUNK_SIZE_64K);
|
||||
UVM_ASSERT(uvm_cpu_storage_get_type(block) == UVM_CPU_CHUNK_STORAGE_MIXED);
|
||||
UVM_ASSERT(uvm_cpu_storage_get_type(node_state) == UVM_CPU_CHUNK_STORAGE_MIXED);
|
||||
|
||||
mixed = uvm_cpu_storage_get_ptr(block);
|
||||
mixed = uvm_cpu_storage_get_ptr(node_state);
|
||||
slot_index = compute_slot_index(block, page_index);
|
||||
small_chunks = uvm_kvmalloc_zero(sizeof(*small_chunks) * MAX_SMALL_CHUNKS_PER_BIG_SLOT);
|
||||
if (!small_chunks)
|
||||
@ -8595,12 +9447,13 @@ static NV_STATUS block_split_cpu_chunk_to_4k(uvm_va_block_t *block, uvm_page_ind
|
||||
|
||||
mixed->slots[slot_index] = small_chunks;
|
||||
clear_bit(slot_index, mixed->big_chunks);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static NV_STATUS block_split_cpu_chunk_one(uvm_va_block_t *block, uvm_page_index_t page_index)
|
||||
static NV_STATUS block_split_cpu_chunk_one(uvm_va_block_t *block, uvm_page_index_t page_index, int nid)
|
||||
{
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(block, page_index);
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(block, nid, page_index);
|
||||
uvm_chunk_size_t chunk_size = uvm_cpu_chunk_get_size(chunk);
|
||||
uvm_chunk_size_t new_size;
|
||||
uvm_gpu_t *gpu;
|
||||
@ -8638,9 +9491,9 @@ static NV_STATUS block_split_cpu_chunk_one(uvm_va_block_t *block, uvm_page_index
|
||||
}
|
||||
|
||||
if (new_size == UVM_CHUNK_SIZE_64K)
|
||||
status = block_split_cpu_chunk_to_64k(block);
|
||||
status = block_split_cpu_chunk_to_64k(block, nid);
|
||||
else
|
||||
status = block_split_cpu_chunk_to_4k(block, page_index);
|
||||
status = block_split_cpu_chunk_to_4k(block, page_index, nid);
|
||||
|
||||
if (status != NV_OK) {
|
||||
merge:
|
||||
@ -8656,16 +9509,18 @@ merge:
|
||||
return status;
|
||||
}
|
||||
|
||||
static NV_STATUS block_prealloc_cpu_chunk_storage(uvm_va_block_t *existing, uvm_va_block_t *new)
|
||||
static NV_STATUS block_prealloc_cpu_chunk_storage(uvm_va_block_t *existing, uvm_va_block_t *new, int nid)
|
||||
{
|
||||
uvm_cpu_chunk_storage_mixed_t *existing_mixed;
|
||||
uvm_cpu_chunk_storage_mixed_t *new_mixed = NULL;
|
||||
uvm_va_block_cpu_node_state_t *node_state = block_node_state_get(existing, nid);
|
||||
uvm_va_block_cpu_node_state_t *new_node_state = block_node_state_get(new, nid);
|
||||
size_t slot_offset;
|
||||
size_t existing_slot;
|
||||
NV_STATUS status = NV_OK;
|
||||
|
||||
UVM_ASSERT(uvm_cpu_storage_get_type(existing) == UVM_CPU_CHUNK_STORAGE_MIXED);
|
||||
existing_mixed = uvm_cpu_storage_get_ptr(existing);
|
||||
UVM_ASSERT(uvm_cpu_storage_get_type(node_state) == UVM_CPU_CHUNK_STORAGE_MIXED);
|
||||
existing_mixed = uvm_cpu_storage_get_ptr(node_state);
|
||||
|
||||
// Pre-allocate chunk storage for the new block. By definition, the new block
|
||||
// will contain either 64K and/or 4K chunks.
|
||||
@ -8692,7 +9547,7 @@ static NV_STATUS block_prealloc_cpu_chunk_storage(uvm_va_block_t *existing, uvm_
|
||||
}
|
||||
}
|
||||
|
||||
new->cpu.chunks = (unsigned long)new_mixed | UVM_CPU_CHUNK_STORAGE_MIXED;
|
||||
new_node_state->chunks = (unsigned long)new_mixed | UVM_CPU_CHUNK_STORAGE_MIXED;
|
||||
UVM_ASSERT(status == NV_OK);
|
||||
|
||||
done:
|
||||
@ -8706,19 +9561,21 @@ done:
|
||||
return status;
|
||||
}
|
||||
|
||||
static void block_free_cpu_chunk_storage(uvm_va_block_t *block)
|
||||
static void block_free_cpu_chunk_storage(uvm_va_block_t *block, int nid)
|
||||
{
|
||||
if (block->cpu.chunks) {
|
||||
uvm_va_block_cpu_node_state_t *node_state = block_node_state_get(block, nid);
|
||||
|
||||
if (node_state->chunks) {
|
||||
uvm_cpu_chunk_storage_mixed_t *mixed;
|
||||
size_t slot_index;
|
||||
|
||||
UVM_ASSERT(uvm_cpu_storage_get_type(block) == UVM_CPU_CHUNK_STORAGE_MIXED);
|
||||
mixed = uvm_cpu_storage_get_ptr(block);
|
||||
UVM_ASSERT(uvm_cpu_storage_get_type(node_state) == UVM_CPU_CHUNK_STORAGE_MIXED);
|
||||
mixed = uvm_cpu_storage_get_ptr(node_state);
|
||||
for (slot_index = 0; slot_index < MAX_BIG_CPU_CHUNK_SLOTS_PER_UVM_VA_BLOCK; slot_index++)
|
||||
uvm_kvfree(mixed->slots[slot_index]);
|
||||
|
||||
uvm_kvfree(mixed);
|
||||
block->cpu.chunks = 0;
|
||||
node_state->chunks = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -8731,42 +9588,51 @@ static NV_STATUS block_presplit_cpu_chunks(uvm_va_block_t *existing, uvm_va_bloc
|
||||
uvm_chunk_sizes_mask_t split_sizes = uvm_cpu_chunk_get_allocation_sizes();
|
||||
uvm_chunk_size_t subchunk_size;
|
||||
NV_STATUS status = NV_OK;
|
||||
int nid;
|
||||
|
||||
UVM_ASSERT(!IS_ALIGNED(new->start, UVM_VA_BLOCK_SIZE));
|
||||
splitting_chunk = uvm_cpu_chunk_get_chunk_for_page(existing, page_index);
|
||||
|
||||
// If the page covering the split point has not been populated, there is no
|
||||
// need to split.
|
||||
if (!splitting_chunk)
|
||||
return NV_OK;
|
||||
for_each_possible_uvm_node(nid) {
|
||||
splitting_chunk = uvm_cpu_chunk_get_chunk_for_page(existing, nid, page_index);
|
||||
|
||||
// If the split point is aligned on the chunk size, there is no need to
|
||||
// split.
|
||||
if (IS_ALIGNED(new->start, uvm_cpu_chunk_get_size(splitting_chunk)))
|
||||
return NV_OK;
|
||||
// If the page covering the split point has not been populated, there is no
|
||||
// need to split.
|
||||
if (!splitting_chunk)
|
||||
continue;
|
||||
|
||||
// Remove all sizes above the chunk's current size.
|
||||
split_sizes &= uvm_cpu_chunk_get_size(splitting_chunk) - 1;
|
||||
// Remove all sizes below the alignment of the new block's start.
|
||||
split_sizes &= ~(IS_ALIGNED(new->start, UVM_CHUNK_SIZE_64K) ? UVM_CHUNK_SIZE_64K - 1 : 0);
|
||||
// If the split point is aligned on the chunk size, there is no need to
|
||||
// split.
|
||||
if (IS_ALIGNED(new->start, uvm_cpu_chunk_get_size(splitting_chunk)))
|
||||
continue;
|
||||
|
||||
for_each_chunk_size_rev(subchunk_size, split_sizes) {
|
||||
status = block_split_cpu_chunk_one(existing, page_index);
|
||||
// Remove all sizes above the chunk's current size.
|
||||
split_sizes &= uvm_cpu_chunk_get_size(splitting_chunk) - 1;
|
||||
// Remove all sizes below the alignment of the new block's start.
|
||||
split_sizes &= ~(IS_ALIGNED(new->start, UVM_CHUNK_SIZE_64K) ? UVM_CHUNK_SIZE_64K - 1 : 0);
|
||||
|
||||
for_each_chunk_size_rev(subchunk_size, split_sizes) {
|
||||
status = block_split_cpu_chunk_one(existing, page_index, nid);
|
||||
if (status != NV_OK)
|
||||
return status;
|
||||
}
|
||||
|
||||
status = block_prealloc_cpu_chunk_storage(existing, new, nid);
|
||||
if (status != NV_OK)
|
||||
return status;
|
||||
break;
|
||||
}
|
||||
|
||||
return block_prealloc_cpu_chunk_storage(existing, new);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void block_merge_cpu_chunks_to_64k(uvm_va_block_t *block, uvm_page_index_t page_index)
|
||||
static void block_merge_cpu_chunks_to_64k(uvm_va_block_t *block, uvm_page_index_t page_index, int nid)
|
||||
{
|
||||
uvm_cpu_chunk_storage_mixed_t *mixed = uvm_cpu_storage_get_ptr(block);
|
||||
uvm_va_block_cpu_node_state_t *node_state = block_node_state_get(block, nid);
|
||||
uvm_cpu_chunk_storage_mixed_t *mixed = uvm_cpu_storage_get_ptr(node_state);
|
||||
size_t slot_index = compute_slot_index(block, page_index);
|
||||
uvm_cpu_chunk_t **small_chunks = mixed->slots[slot_index];
|
||||
uvm_cpu_chunk_t *merged_chunk;
|
||||
|
||||
UVM_ASSERT(uvm_cpu_storage_get_type(block) == UVM_CPU_CHUNK_STORAGE_MIXED);
|
||||
UVM_ASSERT(uvm_cpu_storage_get_type(node_state) == UVM_CPU_CHUNK_STORAGE_MIXED);
|
||||
UVM_ASSERT(small_chunks);
|
||||
UVM_ASSERT(!test_bit(slot_index, mixed->big_chunks));
|
||||
|
||||
@ -8776,34 +9642,38 @@ static void block_merge_cpu_chunks_to_64k(uvm_va_block_t *block, uvm_page_index_
|
||||
uvm_kvfree(small_chunks);
|
||||
}
|
||||
|
||||
static void block_merge_cpu_chunks_to_2m(uvm_va_block_t *block, uvm_page_index_t page_index)
|
||||
static void block_merge_cpu_chunks_to_2m(uvm_va_block_t *block, uvm_page_index_t page_index, int nid)
|
||||
{
|
||||
uvm_cpu_chunk_storage_mixed_t *mixed = uvm_cpu_storage_get_ptr(block);
|
||||
uvm_va_block_cpu_node_state_t *node_state = block_node_state_get(block, nid);
|
||||
uvm_cpu_chunk_storage_mixed_t *mixed = uvm_cpu_storage_get_ptr(node_state);
|
||||
uvm_cpu_chunk_t **big_chunks = (uvm_cpu_chunk_t **)&mixed->slots;
|
||||
uvm_cpu_chunk_t *merged_chunk;
|
||||
|
||||
UVM_ASSERT(uvm_cpu_storage_get_type(block) == UVM_CPU_CHUNK_STORAGE_MIXED);
|
||||
UVM_ASSERT(uvm_cpu_storage_get_type(node_state) == UVM_CPU_CHUNK_STORAGE_MIXED);
|
||||
UVM_ASSERT(bitmap_full(mixed->big_chunks, MAX_BIG_CPU_CHUNK_SLOTS_PER_UVM_VA_BLOCK));
|
||||
|
||||
merged_chunk = uvm_cpu_chunk_merge(big_chunks);
|
||||
block->cpu.chunks = (unsigned long)merged_chunk | UVM_CPU_CHUNK_STORAGE_CHUNK;
|
||||
node_state->chunks = (unsigned long)merged_chunk | UVM_CPU_CHUNK_STORAGE_CHUNK;
|
||||
uvm_kvfree(mixed);
|
||||
}
|
||||
|
||||
static void block_merge_cpu_chunks_one(uvm_va_block_t *block, uvm_page_index_t page_index)
|
||||
static void block_merge_cpu_chunks_one(uvm_va_block_t *block, uvm_page_index_t page_index, int nid)
|
||||
{
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(block, page_index);
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(block, nid, page_index);
|
||||
uvm_gpu_id_t id;
|
||||
|
||||
if (!chunk)
|
||||
return;
|
||||
|
||||
if (uvm_cpu_chunk_get_size(chunk) == UVM_CHUNK_SIZE_4K) {
|
||||
block_merge_cpu_chunks_to_64k(block, page_index);
|
||||
block_merge_cpu_chunks_to_64k(block, page_index, nid);
|
||||
}
|
||||
else {
|
||||
UVM_ASSERT(uvm_cpu_chunk_get_size(chunk) == UVM_CHUNK_SIZE_64K);
|
||||
block_merge_cpu_chunks_to_2m(block, page_index);
|
||||
block_merge_cpu_chunks_to_2m(block, page_index, nid);
|
||||
}
|
||||
|
||||
chunk = uvm_cpu_chunk_get_chunk_for_page(block, page_index);
|
||||
chunk = uvm_cpu_chunk_get_chunk_for_page(block, nid, page_index);
|
||||
|
||||
for_each_gpu_id(id) {
|
||||
NvU64 gpu_mapping_addr;
|
||||
@ -8826,50 +9696,56 @@ static void block_merge_cpu_chunks_one(uvm_va_block_t *block, uvm_page_index_t p
|
||||
static void block_merge_cpu_chunks(uvm_va_block_t *existing, uvm_va_block_t *new)
|
||||
{
|
||||
uvm_page_index_t page_index = uvm_va_block_cpu_page_index(existing, new->start);
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(existing, page_index);
|
||||
uvm_chunk_sizes_mask_t merge_sizes = uvm_cpu_chunk_get_allocation_sizes();
|
||||
uvm_chunk_size_t largest_size;
|
||||
uvm_chunk_size_t chunk_size;
|
||||
uvm_chunk_size_t merge_size;
|
||||
size_t block_size = uvm_va_block_size(existing);
|
||||
int nid;
|
||||
|
||||
if (!chunk || uvm_cpu_chunk_is_physical(chunk))
|
||||
return;
|
||||
|
||||
chunk_size = uvm_cpu_chunk_get_size(chunk);
|
||||
|
||||
// Remove all CPU chunk sizes above the size of the existing VA block.
|
||||
// Since block sizes are not always powers of 2, use the largest power of 2
|
||||
// less than or equal to the block size since we can't merge to a size
|
||||
// larger than the block's size.
|
||||
largest_size = rounddown_pow_of_two(block_size);
|
||||
merge_sizes &= (largest_size | (largest_size - 1));
|
||||
|
||||
// Remove all CPU chunk sizes smaller than the size of the chunk being merged up.
|
||||
merge_sizes &= ~(chunk_size | (chunk_size - 1));
|
||||
for_each_possible_uvm_node(nid) {
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(existing, nid, page_index);
|
||||
uvm_va_block_cpu_node_state_t *node_state = block_node_state_get(existing, nid);
|
||||
uvm_chunk_size_t chunk_size;
|
||||
uvm_chunk_size_t merge_size;
|
||||
|
||||
for_each_chunk_size(merge_size, merge_sizes) {
|
||||
uvm_va_block_region_t chunk_region;
|
||||
if (!chunk || uvm_cpu_chunk_is_physical(chunk))
|
||||
continue;
|
||||
|
||||
// The block has to fully contain the VA range after the merge.
|
||||
if (!uvm_va_block_contains_address(existing, UVM_ALIGN_DOWN(new->start, merge_size)) ||
|
||||
!uvm_va_block_contains_address(existing, UVM_ALIGN_DOWN(new->start, merge_size) + merge_size - 1))
|
||||
break;
|
||||
chunk_size = uvm_cpu_chunk_get_size(chunk);
|
||||
|
||||
chunk_region = uvm_va_block_chunk_region(existing, merge_size, page_index);
|
||||
// Remove all CPU chunk sizes above the size of the existing VA block.
|
||||
merge_sizes &= (largest_size | (largest_size - 1));
|
||||
|
||||
// If not all pages in the region covered by the chunk are allocated,
|
||||
// we can't merge.
|
||||
if (!uvm_page_mask_region_full(&existing->cpu.allocated, chunk_region))
|
||||
break;
|
||||
// Remove all CPU chunk sizes smaller than the size of the chunk being merged up.
|
||||
merge_sizes &= ~(chunk_size | (chunk_size - 1));
|
||||
|
||||
block_merge_cpu_chunks_one(existing, chunk_region.first);
|
||||
chunk = uvm_cpu_chunk_get_chunk_for_page(existing, page_index);
|
||||
if (uvm_cpu_chunk_is_physical(chunk))
|
||||
break;
|
||||
for_each_chunk_size(merge_size, merge_sizes) {
|
||||
uvm_va_block_region_t chunk_region;
|
||||
|
||||
// The block has to fully contain the VA range after the merge.
|
||||
if (!uvm_va_block_contains_address(existing, UVM_ALIGN_DOWN(new->start, merge_size)) ||
|
||||
!uvm_va_block_contains_address(existing, UVM_ALIGN_DOWN(new->start, merge_size) + merge_size - 1))
|
||||
break;
|
||||
|
||||
chunk_region = uvm_va_block_chunk_region(existing, merge_size, page_index);
|
||||
|
||||
// If not all pages in the region covered by the chunk are allocated,
|
||||
// we can't merge.
|
||||
if (!uvm_page_mask_region_full(&node_state->allocated, chunk_region))
|
||||
break;
|
||||
|
||||
block_merge_cpu_chunks_one(existing, chunk_region.first, nid);
|
||||
chunk = uvm_cpu_chunk_get_chunk_for_page(existing, nid, page_index);
|
||||
if (uvm_cpu_chunk_is_physical(chunk))
|
||||
break;
|
||||
}
|
||||
|
||||
block_free_cpu_chunk_storage(new, nid);
|
||||
}
|
||||
|
||||
block_free_cpu_chunk_storage(new);
|
||||
}
|
||||
|
||||
// Pre-allocate everything which doesn't require retry on both existing and new
|
||||
@ -8977,7 +9853,7 @@ static void block_set_processor_masks(uvm_va_block_t *block)
|
||||
uvm_processor_mask_set(&block->mapped, UVM_ID_CPU);
|
||||
}
|
||||
|
||||
if (uvm_page_mask_region_empty(&block->cpu.resident, block_region)) {
|
||||
if (uvm_page_mask_region_empty(uvm_va_block_resident_mask_get(block, UVM_ID_CPU, NUMA_NO_NODE), block_region)) {
|
||||
uvm_va_space_t *va_space = uvm_va_block_get_va_space(block);
|
||||
|
||||
if (uvm_processor_mask_get_gpu_count(&va_space->can_access[UVM_ID_CPU_VALUE]) == 0)
|
||||
@ -9046,6 +9922,7 @@ static void block_split_cpu(uvm_va_block_t *existing, uvm_va_block_t *new)
|
||||
uvm_page_index_t next_page_index;
|
||||
uvm_cpu_chunk_t *chunk;
|
||||
uvm_va_range_t *existing_va_range = existing->va_range;
|
||||
int nid;
|
||||
|
||||
if (existing_va_range) {
|
||||
UVM_ASSERT(existing->va_range->type == UVM_VA_RANGE_TYPE_MANAGED);
|
||||
@ -9063,30 +9940,35 @@ static void block_split_cpu(uvm_va_block_t *existing, uvm_va_block_t *new)
|
||||
// We don't have to unmap the CPU since its virtual -> physical mappings
|
||||
// don't change.
|
||||
|
||||
page_index = uvm_va_block_next_page_in_mask(block_region, &existing->cpu.allocated, split_page_index - 1);
|
||||
for_each_possible_uvm_node(nid) {
|
||||
uvm_page_mask_t *existing_resident_mask = uvm_va_block_resident_mask_get(existing, UVM_ID_CPU, nid);
|
||||
uvm_page_mask_t *new_resident_mask = uvm_va_block_resident_mask_get(new, UVM_ID_CPU, nid);
|
||||
|
||||
for_each_cpu_chunk_in_block_region_safe(chunk,
|
||||
page_index,
|
||||
next_page_index,
|
||||
existing,
|
||||
uvm_va_block_region(split_page_index, block_region.outer)) {
|
||||
uvm_page_index_t new_chunk_page_index;
|
||||
NV_STATUS status;
|
||||
for_each_cpu_chunk_in_block_region_safe(chunk,
|
||||
page_index,
|
||||
next_page_index,
|
||||
existing,
|
||||
nid,
|
||||
uvm_va_block_region(split_page_index, block_region.outer)) {
|
||||
uvm_page_index_t new_chunk_page_index;
|
||||
NV_STATUS status;
|
||||
|
||||
uvm_cpu_chunk_remove_from_block(existing, page_index);
|
||||
uvm_cpu_chunk_remove_from_block(existing, nid, page_index);
|
||||
|
||||
// The chunk has to be adjusted for the new block before inserting it.
|
||||
new_chunk_page_index = page_index - split_page_index;
|
||||
// The chunk has to be adjusted for the new block before inserting it.
|
||||
new_chunk_page_index = page_index - split_page_index;
|
||||
|
||||
// This should never fail because all necessary storage was allocated
|
||||
// in block_presplit_cpu_chunks().
|
||||
status = uvm_cpu_chunk_insert_in_block(new, chunk, new_chunk_page_index);
|
||||
UVM_ASSERT(status == NV_OK);
|
||||
// This should never fail because all necessary storage was allocated
|
||||
// in block_presplit_cpu_chunks().
|
||||
status = uvm_cpu_chunk_insert_in_block(new, chunk, new_chunk_page_index);
|
||||
UVM_ASSERT(status == NV_OK);
|
||||
}
|
||||
|
||||
block_split_page_mask(existing_resident_mask, existing_pages, new_resident_mask, new_pages);
|
||||
}
|
||||
|
||||
new->cpu.ever_mapped = existing->cpu.ever_mapped;
|
||||
|
||||
block_split_page_mask(&existing->cpu.resident, existing_pages, &new->cpu.resident, new_pages);
|
||||
new->cpu.ever_mapped = existing->cpu.ever_mapped;
|
||||
|
||||
for (pte_bit = 0; pte_bit < UVM_PTE_BITS_CPU_MAX; pte_bit++)
|
||||
block_split_page_mask(&existing->cpu.pte_bits[pte_bit], existing_pages, &new->cpu.pte_bits[pte_bit], new_pages);
|
||||
@ -9236,6 +10118,7 @@ static void block_split_gpu(uvm_va_block_t *existing, uvm_va_block_t *new, uvm_g
|
||||
size_t num_chunks, i;
|
||||
uvm_cpu_chunk_t *cpu_chunk;
|
||||
uvm_page_index_t page_index;
|
||||
int nid;
|
||||
|
||||
if (!existing_gpu_state)
|
||||
return;
|
||||
@ -9249,10 +10132,12 @@ static void block_split_gpu(uvm_va_block_t *existing, uvm_va_block_t *new, uvm_g
|
||||
UVM_ASSERT(PAGE_ALIGNED(existing->start));
|
||||
existing_pages = (new->start - existing->start) / PAGE_SIZE;
|
||||
|
||||
for_each_cpu_chunk_in_block(cpu_chunk, page_index, new) {
|
||||
uvm_pmm_sysmem_mappings_reparent_gpu_mapping(&gpu->pmm_reverse_sysmem_mappings,
|
||||
uvm_cpu_chunk_get_gpu_phys_addr(cpu_chunk, gpu->parent),
|
||||
new);
|
||||
for_each_possible_uvm_node(nid) {
|
||||
for_each_cpu_chunk_in_block(cpu_chunk, page_index, new, nid) {
|
||||
uvm_pmm_sysmem_mappings_reparent_gpu_mapping(&gpu->pmm_reverse_sysmem_mappings,
|
||||
uvm_cpu_chunk_get_gpu_phys_addr(cpu_chunk, gpu->parent),
|
||||
new);
|
||||
}
|
||||
}
|
||||
|
||||
block_copy_split_gpu_chunks(existing, new, gpu);
|
||||
@ -9483,10 +10368,12 @@ NV_STATUS uvm_va_block_split_locked(uvm_va_block_t *existing_va_block,
|
||||
&new_block->read_duplicated_pages,
|
||||
uvm_va_block_num_cpu_pages(new_block));
|
||||
|
||||
block_split_page_mask(&existing_va_block->maybe_mapped_pages,
|
||||
uvm_va_block_num_cpu_pages(existing_va_block),
|
||||
&new_block->maybe_mapped_pages,
|
||||
uvm_va_block_num_cpu_pages(new_block));
|
||||
if (!uvm_va_block_is_hmm(existing_va_block)) {
|
||||
block_split_page_mask(&existing_va_block->maybe_mapped_pages,
|
||||
uvm_va_block_num_cpu_pages(existing_va_block),
|
||||
&new_block->maybe_mapped_pages,
|
||||
uvm_va_block_num_cpu_pages(new_block));
|
||||
}
|
||||
|
||||
block_set_processor_masks(existing_va_block);
|
||||
block_set_processor_masks(new_block);
|
||||
@ -9506,7 +10393,10 @@ out:
|
||||
UVM_ASSERT(block_check_mappings(new_block));
|
||||
}
|
||||
else {
|
||||
block_free_cpu_chunk_storage(new_block);
|
||||
int nid;
|
||||
|
||||
for_each_possible_uvm_node(nid)
|
||||
block_free_cpu_chunk_storage(new_block, nid);
|
||||
}
|
||||
|
||||
uvm_mutex_unlock_no_tracking(&new_block->lock);
|
||||
@ -9868,7 +10758,8 @@ uvm_prot_t uvm_va_block_page_compute_highest_permission(uvm_va_block_t *va_block
|
||||
|
||||
// Fast path: if the page is not mapped anywhere else, it can be safely
|
||||
// mapped with RWA permission
|
||||
if (!uvm_page_mask_test(&va_block->maybe_mapped_pages, page_index))
|
||||
if (!uvm_page_mask_test(&va_block->maybe_mapped_pages, page_index) &&
|
||||
!uvm_va_block_is_hmm(va_block))
|
||||
return UVM_PROT_READ_WRITE_ATOMIC;
|
||||
|
||||
block_page_authorized_processors(va_block, page_index, UVM_PROT_READ_WRITE_ATOMIC, &atomic_mappings);
|
||||
@ -10155,30 +11046,6 @@ static uvm_processor_id_t block_select_residency(uvm_va_block_t *va_block,
|
||||
uvm_processor_mask_test(&va_space->accessible_from[uvm_id_value(preferred_location)], processor_id))
|
||||
return preferred_location;
|
||||
|
||||
// Check if we should map the closest resident processor remotely on remote CPU fault
|
||||
//
|
||||
// When faulting on CPU, there's a linux process on behalf of it, which is associated
|
||||
// with a unique VM pointed by current->mm. A block of memory residing on GPU is also
|
||||
// associated with VM, pointed by va_block_context->mm. If they match, it's a regular
|
||||
// (local) fault, and we may want to migrate a page from GPU to CPU.
|
||||
// If it's a 'remote' fault, i.e. linux process differs from one associated with block
|
||||
// VM, we might preserve residence.
|
||||
//
|
||||
// Establishing a remote fault without access counters means the memory could stay in
|
||||
// the wrong spot for a long time, which is why we prefer to avoid creating remote
|
||||
// mappings. However when NIC accesses a memory residing on GPU, it's worth to keep it
|
||||
// in place for NIC accesses.
|
||||
//
|
||||
// The logic that's used to detect remote faulting also keeps memory in place for
|
||||
// ptrace accesses. We would prefer to control those policies separately, but the
|
||||
// NIC case takes priority.
|
||||
if (UVM_ID_IS_CPU(processor_id) &&
|
||||
uvm_processor_mask_test(&va_space->accessible_from[uvm_id_value(closest_resident_processor)], processor_id) &&
|
||||
va_block_context->mm != current->mm) {
|
||||
UVM_ASSERT(va_block_context->mm != NULL);
|
||||
return closest_resident_processor;
|
||||
}
|
||||
|
||||
// If the page is resident on a processor other than the preferred location,
|
||||
// or the faulting processor can't access the preferred location, we select
|
||||
// the faulting processor as the new residency.
|
||||
@ -10214,9 +11081,14 @@ uvm_processor_id_t uvm_va_block_select_residency(uvm_va_block_t *va_block,
|
||||
// If the intended residency doesn't have memory, fall back to the CPU.
|
||||
if (!block_processor_has_memory(va_block, id)) {
|
||||
*read_duplicate = false;
|
||||
return UVM_ID_CPU;
|
||||
id = UVM_ID_CPU;
|
||||
}
|
||||
|
||||
// Set the destination NUMA node unconditionally since it could be used
|
||||
// for CPU allocations (if staging pages are needed) even if the new
|
||||
// residency is not the CPU.
|
||||
va_block_context->make_resident.dest_nid = policy->preferred_nid;
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
@ -10260,7 +11132,7 @@ static void uvm_va_block_get_prefetch_hint(uvm_va_block_t *va_block,
|
||||
// Update prefetch tracking structure with the pages that will migrate
|
||||
// due to faults
|
||||
uvm_perf_prefetch_get_hint_va_block(va_block,
|
||||
&service_context->block_context,
|
||||
service_context->block_context,
|
||||
new_residency,
|
||||
new_residency_mask,
|
||||
service_context->region,
|
||||
@ -10304,11 +11176,11 @@ NV_STATUS uvm_va_block_service_copy(uvm_processor_id_t processor_id,
|
||||
{
|
||||
uvm_va_space_t *va_space = uvm_va_block_get_va_space(va_block);
|
||||
uvm_processor_mask_t *all_involved_processors =
|
||||
&service_context->block_context.make_resident.all_involved_processors;
|
||||
&service_context->block_context->make_resident.all_involved_processors;
|
||||
uvm_page_mask_t *new_residency_mask =
|
||||
&service_context->per_processor_masks[uvm_id_value(new_residency)].new_residency;
|
||||
uvm_page_mask_t *did_migrate_mask = &service_context->block_context.make_resident.pages_changed_residency;
|
||||
uvm_page_mask_t *caller_page_mask = &service_context->block_context.caller_page_mask;
|
||||
uvm_page_mask_t *did_migrate_mask = &service_context->block_context->make_resident.pages_changed_residency;
|
||||
uvm_page_mask_t *caller_page_mask = &service_context->block_context->caller_page_mask;
|
||||
uvm_make_resident_cause_t cause;
|
||||
NV_STATUS status;
|
||||
|
||||
@ -10343,7 +11215,7 @@ NV_STATUS uvm_va_block_service_copy(uvm_processor_id_t processor_id,
|
||||
&service_context->read_duplicate_mask)) {
|
||||
status = uvm_va_block_make_resident_read_duplicate(va_block,
|
||||
block_retry,
|
||||
&service_context->block_context,
|
||||
service_context->block_context,
|
||||
new_residency,
|
||||
service_context->region,
|
||||
caller_page_mask,
|
||||
@ -10359,7 +11231,7 @@ NV_STATUS uvm_va_block_service_copy(uvm_processor_id_t processor_id,
|
||||
uvm_page_mask_copy(caller_page_mask, new_residency_mask);
|
||||
status = uvm_va_block_make_resident_copy(va_block,
|
||||
block_retry,
|
||||
&service_context->block_context,
|
||||
service_context->block_context,
|
||||
new_residency,
|
||||
service_context->region,
|
||||
caller_page_mask,
|
||||
@ -10417,11 +11289,11 @@ NV_STATUS uvm_va_block_service_finish(uvm_processor_id_t processor_id,
|
||||
uvm_va_block_t *va_block,
|
||||
uvm_service_block_context_t *service_context)
|
||||
{
|
||||
uvm_processor_id_t new_residency = service_context->block_context.make_resident.dest_id;
|
||||
uvm_processor_id_t new_residency = service_context->block_context->make_resident.dest_id;
|
||||
uvm_page_mask_t *new_residency_mask =
|
||||
&service_context->per_processor_masks[uvm_id_value(new_residency)].new_residency;
|
||||
uvm_page_mask_t *did_migrate_mask = &service_context->block_context.make_resident.pages_changed_residency;
|
||||
uvm_page_mask_t *caller_page_mask = &service_context->block_context.caller_page_mask;
|
||||
uvm_page_mask_t *did_migrate_mask = &service_context->block_context->make_resident.pages_changed_residency;
|
||||
uvm_page_mask_t *caller_page_mask = &service_context->block_context->caller_page_mask;
|
||||
uvm_va_space_t *va_space = uvm_va_block_get_va_space(va_block);
|
||||
uvm_prot_t new_prot;
|
||||
uvm_page_index_t page_index;
|
||||
@ -10430,7 +11302,7 @@ NV_STATUS uvm_va_block_service_finish(uvm_processor_id_t processor_id,
|
||||
// Update residency.
|
||||
if (service_context->read_duplicate_count == 0 || !uvm_page_mask_empty(caller_page_mask))
|
||||
uvm_va_block_make_resident_finish(va_block,
|
||||
&service_context->block_context,
|
||||
service_context->block_context,
|
||||
service_context->region,
|
||||
caller_page_mask);
|
||||
|
||||
@ -10450,7 +11322,7 @@ NV_STATUS uvm_va_block_service_finish(uvm_processor_id_t processor_id,
|
||||
|
||||
for_each_va_block_page_in_region_mask(page_index, new_residency_mask, service_context->region) {
|
||||
new_prot = compute_new_permission(va_block,
|
||||
service_context->block_context.hmm.vma,
|
||||
service_context->block_context->hmm.vma,
|
||||
page_index,
|
||||
processor_id,
|
||||
new_residency,
|
||||
@ -10523,7 +11395,7 @@ NV_STATUS uvm_va_block_service_finish(uvm_processor_id_t processor_id,
|
||||
// uvm_va_block_make_resident_read_duplicate, above.
|
||||
if (service_context->operation == UVM_SERVICE_OPERATION_ACCESS_COUNTERS) {
|
||||
UVM_ASSERT(check_access_counters_dont_revoke(va_block,
|
||||
&service_context->block_context,
|
||||
service_context->block_context,
|
||||
service_context->region,
|
||||
&revoke_processors,
|
||||
&service_context->revocation_mask,
|
||||
@ -10532,7 +11404,7 @@ NV_STATUS uvm_va_block_service_finish(uvm_processor_id_t processor_id,
|
||||
|
||||
// Downgrade other processors' mappings
|
||||
status = uvm_va_block_revoke_prot_mask(va_block,
|
||||
&service_context->block_context,
|
||||
service_context->block_context,
|
||||
&revoke_processors,
|
||||
service_context->region,
|
||||
&service_context->revocation_mask,
|
||||
@ -10562,7 +11434,7 @@ NV_STATUS uvm_va_block_service_finish(uvm_processor_id_t processor_id,
|
||||
// A CPU fault is unexpected if:
|
||||
// curr_prot == RW || (!is_write && curr_prot == RO)
|
||||
status = uvm_va_block_unmap(va_block,
|
||||
&service_context->block_context,
|
||||
service_context->block_context,
|
||||
UVM_ID_CPU,
|
||||
service_context->region,
|
||||
map_prot_mask,
|
||||
@ -10579,13 +11451,13 @@ NV_STATUS uvm_va_block_service_finish(uvm_processor_id_t processor_id,
|
||||
|
||||
// Map pages that are thrashing first
|
||||
if (service_context->thrashing_pin_count > 0 && va_space->tools.enabled) {
|
||||
uvm_page_mask_t *helper_page_mask = &service_context->block_context.caller_page_mask;
|
||||
uvm_page_mask_t *helper_page_mask = &service_context->block_context->caller_page_mask;
|
||||
bool pages_need_mapping = uvm_page_mask_and(helper_page_mask,
|
||||
map_prot_mask,
|
||||
&service_context->thrashing_pin_mask);
|
||||
if (pages_need_mapping) {
|
||||
status = uvm_va_block_map(va_block,
|
||||
&service_context->block_context,
|
||||
service_context->block_context,
|
||||
processor_id,
|
||||
service_context->region,
|
||||
helper_page_mask,
|
||||
@ -10607,7 +11479,7 @@ NV_STATUS uvm_va_block_service_finish(uvm_processor_id_t processor_id,
|
||||
}
|
||||
|
||||
status = uvm_va_block_map(va_block,
|
||||
&service_context->block_context,
|
||||
service_context->block_context,
|
||||
processor_id,
|
||||
service_context->region,
|
||||
map_prot_mask,
|
||||
@ -10649,7 +11521,7 @@ NV_STATUS uvm_va_block_service_finish(uvm_processor_id_t processor_id,
|
||||
map_thrashing_processors = uvm_perf_thrashing_get_thrashing_processors(va_block, page_addr);
|
||||
|
||||
status = uvm_va_block_add_mappings_after_migration(va_block,
|
||||
&service_context->block_context,
|
||||
service_context->block_context,
|
||||
new_residency,
|
||||
processor_id,
|
||||
uvm_va_block_region_for_page(page_index),
|
||||
@ -10669,7 +11541,7 @@ NV_STATUS uvm_va_block_service_finish(uvm_processor_id_t processor_id,
|
||||
|
||||
// Map the rest of pages in a single shot
|
||||
status = uvm_va_block_add_mappings_after_migration(va_block,
|
||||
&service_context->block_context,
|
||||
service_context->block_context,
|
||||
new_residency,
|
||||
processor_id,
|
||||
service_context->region,
|
||||
@ -10694,7 +11566,7 @@ NV_STATUS uvm_va_block_service_locked(uvm_processor_id_t processor_id,
|
||||
|
||||
uvm_assert_mutex_locked(&va_block->lock);
|
||||
UVM_ASSERT(uvm_hmm_check_context_vma_is_valid(va_block,
|
||||
service_context->block_context.hmm.vma,
|
||||
service_context->block_context->hmm.vma,
|
||||
service_context->region));
|
||||
|
||||
// GPU fault servicing must be done under the VA space read lock. GPU fault
|
||||
@ -10777,7 +11649,7 @@ NV_STATUS uvm_va_block_check_logical_permissions(uvm_va_block_t *va_block,
|
||||
return NV_ERR_INVALID_OPERATION;
|
||||
}
|
||||
else {
|
||||
uvm_va_space_t *va_space = va_range->va_space;
|
||||
uvm_va_space_t *va_space = uvm_va_block_get_va_space(va_block);
|
||||
|
||||
return uvm_processor_mask_test(
|
||||
&va_space->accessible_from[uvm_id_value(uvm_va_range_get_policy(va_range)->preferred_location)],
|
||||
@ -10856,7 +11728,7 @@ static NV_STATUS block_cpu_fault_locked(uvm_va_block_t *va_block,
|
||||
UVM_ASSERT(fault_addr >= va_block->start);
|
||||
UVM_ASSERT(fault_addr <= va_block->end);
|
||||
|
||||
uvm_assert_mmap_lock_locked(service_context->block_context.mm);
|
||||
uvm_assert_mmap_lock_locked(service_context->block_context->mm);
|
||||
|
||||
policy = uvm_va_policy_get(va_block, fault_addr);
|
||||
|
||||
@ -10873,7 +11745,7 @@ static NV_STATUS block_cpu_fault_locked(uvm_va_block_t *va_block,
|
||||
// Check logical permissions
|
||||
page_index = uvm_va_block_cpu_page_index(va_block, fault_addr);
|
||||
status = uvm_va_block_check_logical_permissions(va_block,
|
||||
&service_context->block_context,
|
||||
service_context->block_context,
|
||||
UVM_ID_CPU,
|
||||
page_index,
|
||||
fault_access_type,
|
||||
@ -10905,7 +11777,7 @@ static NV_STATUS block_cpu_fault_locked(uvm_va_block_t *va_block,
|
||||
|
||||
// Compute new residency and update the masks
|
||||
new_residency = uvm_va_block_select_residency(va_block,
|
||||
&service_context->block_context,
|
||||
service_context->block_context,
|
||||
page_index,
|
||||
UVM_ID_CPU,
|
||||
uvm_fault_access_type_mask_bit(fault_access_type),
|
||||
@ -11251,7 +12123,7 @@ NV_STATUS uvm_va_block_write_from_cpu(uvm_va_block_t *va_block,
|
||||
|
||||
if (UVM_ID_IS_CPU(proc)) {
|
||||
char *mapped_page;
|
||||
struct page *page = uvm_cpu_chunk_get_cpu_page(va_block, page_index);
|
||||
struct page *page = uvm_va_block_get_cpu_page(va_block, page_index);
|
||||
void *src = uvm_mem_get_cpu_addr_kernel(src_mem);
|
||||
|
||||
status = uvm_tracker_wait(&va_block->tracker);
|
||||
@ -11272,7 +12144,9 @@ NV_STATUS uvm_va_block_write_from_cpu(uvm_va_block_t *va_block,
|
||||
|
||||
dst_gpu = block_get_gpu(va_block, proc);
|
||||
|
||||
dst_gpu_address = block_phys_page_copy_address(va_block, block_phys_page(proc, page_index), dst_gpu);
|
||||
dst_gpu_address = block_phys_page_copy_address(va_block,
|
||||
block_phys_page(proc, NUMA_NO_NODE, page_index),
|
||||
dst_gpu);
|
||||
dst_gpu_address.address += page_offset;
|
||||
|
||||
return va_block_write_cpu_to_gpu(va_block, dst_gpu, dst_gpu_address, dst, src_mem, size);
|
||||
@ -11333,7 +12207,7 @@ NV_STATUS uvm_va_block_read_to_cpu(uvm_va_block_t *va_block, uvm_mem_t *dst_mem,
|
||||
else if (UVM_ID_IS_CPU(proc)) {
|
||||
NV_STATUS status;
|
||||
char *mapped_page;
|
||||
struct page *page = uvm_cpu_chunk_get_cpu_page(va_block, page_index);
|
||||
struct page *page = uvm_va_block_get_cpu_page(va_block, page_index);
|
||||
|
||||
status = uvm_tracker_wait(&va_block->tracker);
|
||||
if (status != NV_OK)
|
||||
@ -11349,7 +12223,9 @@ NV_STATUS uvm_va_block_read_to_cpu(uvm_va_block_t *va_block, uvm_mem_t *dst_mem,
|
||||
uvm_gpu_address_t src_gpu_address;
|
||||
uvm_gpu_t *gpu = block_get_gpu(va_block, proc);
|
||||
|
||||
src_gpu_address = block_phys_page_copy_address(va_block, block_phys_page(proc, page_index), gpu);
|
||||
src_gpu_address = block_phys_page_copy_address(va_block,
|
||||
block_phys_page(proc, NUMA_NO_NODE, page_index),
|
||||
gpu);
|
||||
src_gpu_address.address += page_offset;
|
||||
|
||||
return va_block_read_gpu_to_cpu(va_block, dst_mem, gpu, src_gpu_address, src, size);
|
||||
@ -11539,7 +12415,7 @@ NV_STATUS uvm_va_block_evict_chunks(uvm_va_block_t *va_block,
|
||||
goto out;
|
||||
|
||||
// Only move pages resident on the GPU
|
||||
uvm_page_mask_and(pages_to_evict, pages_to_evict, uvm_va_block_resident_mask_get(va_block, gpu->id));
|
||||
uvm_page_mask_and(pages_to_evict, pages_to_evict, uvm_va_block_resident_mask_get(va_block, gpu->id, NUMA_NO_NODE));
|
||||
uvm_processor_mask_zero(&block_context->make_resident.all_involved_processors);
|
||||
|
||||
if (uvm_va_block_is_hmm(va_block)) {
|
||||
@ -11776,6 +12652,12 @@ NV_STATUS uvm_test_va_block_inject_error(UVM_TEST_VA_BLOCK_INJECT_ERROR_PARAMS *
|
||||
va_block_test->cpu_chunk_allocation_size_mask = params->cpu_chunk_allocation_size_mask & UVM_CPU_CHUNK_SIZES;
|
||||
}
|
||||
|
||||
if (params->cpu_chunk_allocation_target_id != NUMA_NO_NODE)
|
||||
va_block_test->cpu_chunk_allocation_target_id = params->cpu_chunk_allocation_target_id;
|
||||
|
||||
if (params->cpu_chunk_allocation_actual_id != NUMA_NO_NODE)
|
||||
va_block_test->cpu_chunk_allocation_actual_id = params->cpu_chunk_allocation_actual_id;
|
||||
|
||||
if (params->eviction_error)
|
||||
va_block_test->inject_eviction_error = params->eviction_error;
|
||||
|
||||
@ -12028,6 +12910,7 @@ NV_STATUS uvm_test_va_residency_info(UVM_TEST_VA_RESIDENCY_INFO_PARAMS *params,
|
||||
params->resident_on_count = 0;
|
||||
params->populated_on_count = 0;
|
||||
params->mapped_on_count = 0;
|
||||
params->resident_nid = -1;
|
||||
|
||||
status = NV_OK;
|
||||
|
||||
@ -12040,19 +12923,34 @@ NV_STATUS uvm_test_va_residency_info(UVM_TEST_VA_RESIDENCY_INFO_PARAMS *params,
|
||||
page_index = uvm_va_block_cpu_page_index(block, addr);
|
||||
uvm_va_block_page_resident_processors(block, page_index, &resident_on_mask);
|
||||
|
||||
params->resident_nid = -1;
|
||||
for_each_id_in_mask(id, &resident_on_mask) {
|
||||
block_phys_page_t block_page = block_phys_page(id, page_index);
|
||||
block_phys_page_t block_page;
|
||||
int nid = block_get_page_node_residency(block, page_index);
|
||||
|
||||
block_page = block_phys_page(id, nid, page_index);
|
||||
uvm_va_space_processor_uuid(va_space, ¶ms->resident_on[count], id);
|
||||
params->resident_physical_size[count] = block_phys_page_size(block, block_page);
|
||||
if (UVM_ID_IS_CPU(id)) {
|
||||
params->resident_physical_address[count] = page_to_phys(uvm_cpu_chunk_get_cpu_page(block, page_index));
|
||||
params->resident_physical_address[count] = page_to_phys(uvm_va_block_get_cpu_page(block, page_index));
|
||||
params->resident_nid = nid;
|
||||
|
||||
// Check that the page is only resident on a single CPU NUMA node.
|
||||
for_each_possible_uvm_node(nid) {
|
||||
if (uvm_va_block_cpu_is_page_resident_on(block, nid, page_index) && nid != params->resident_nid) {
|
||||
status = NV_ERR_INVALID_STATE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
params->resident_physical_address[count] =
|
||||
block_phys_page_address(block, block_page, uvm_va_space_get_gpu(va_space, id)).address;
|
||||
}
|
||||
|
||||
++count;
|
||||
}
|
||||
|
||||
params->resident_on_count = count;
|
||||
|
||||
count = 0;
|
||||
@ -12060,6 +12958,7 @@ NV_STATUS uvm_test_va_residency_info(UVM_TEST_VA_RESIDENCY_INFO_PARAMS *params,
|
||||
uvm_processor_id_t processor_to_map;
|
||||
block_phys_page_t block_page;
|
||||
NvU32 page_size = uvm_va_block_page_size_processor(block, id, page_index);
|
||||
int nid = NUMA_NO_NODE;
|
||||
|
||||
if (page_size == 0)
|
||||
continue;
|
||||
@ -12069,7 +12968,10 @@ NV_STATUS uvm_test_va_residency_info(UVM_TEST_VA_RESIDENCY_INFO_PARAMS *params,
|
||||
params->mapping_type[count] = g_uvm_prot_to_test_pte_mapping[block_page_prot(block, id, page_index)];
|
||||
UVM_ASSERT(params->mapping_type[count] != UVM_TEST_PTE_MAPPING_INVALID);
|
||||
processor_to_map = block_get_processor_to_map(block, id, page_index);
|
||||
block_page = block_phys_page(processor_to_map, page_index);
|
||||
if (UVM_ID_IS_CPU(processor_to_map))
|
||||
nid = block_get_page_node_residency(block, page_index);
|
||||
|
||||
block_page = block_phys_page(processor_to_map, nid, page_index);
|
||||
|
||||
if (!UVM_ID_IS_CPU(id)) {
|
||||
uvm_gpu_phys_address_t gpu_phys_addr = block_phys_page_address(block,
|
||||
@ -12093,7 +12995,7 @@ NV_STATUS uvm_test_va_residency_info(UVM_TEST_VA_RESIDENCY_INFO_PARAMS *params,
|
||||
for_each_gpu_id(id) {
|
||||
NvU32 page_size = uvm_va_block_page_size_processor(block, id, page_index);
|
||||
uvm_reverse_map_t sysmem_page;
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page(block, page_index);
|
||||
uvm_cpu_chunk_t *chunk = uvm_cpu_chunk_get_chunk_for_page_resident(block, page_index);
|
||||
size_t num_pages;
|
||||
uvm_gpu_t *gpu;
|
||||
|
||||
|
@ -44,6 +44,7 @@
|
||||
|
||||
#include <linux/mmu_notifier.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/nodemask.h>
|
||||
|
||||
// VA blocks are the leaf nodes in the uvm_va_space tree for managed allocations
|
||||
// (VA ranges with type == UVM_VA_RANGE_TYPE_MANAGED):
|
||||
@ -229,6 +230,42 @@ typedef struct
|
||||
|
||||
} uvm_va_block_gpu_state_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// Per-page residency bit vector, used for fast traversal of resident
|
||||
// pages.
|
||||
//
|
||||
// A set bit means the CPU has a coherent copy of the physical page
|
||||
// resident in the NUMA node's memory, and that a CPU chunk for the
|
||||
// corresponding page index has been allocated. This does not mean that
|
||||
// the coherent copy is currently mapped anywhere, however. A page may be
|
||||
// resident on multiple processors (but not multiple CPU NUMA nodes) when in
|
||||
// read-duplicate mode.
|
||||
//
|
||||
// A cleared bit means the CPU NUMA node does not have a coherent copy of
|
||||
// that page resident. A CPU chunk for the corresponding page index may or
|
||||
// may not have been allocated. If the chunk is present, it's a cached chunk
|
||||
// which can be reused in the future.
|
||||
//
|
||||
// Allocating PAGES_PER_UVM_VA_BLOCK is overkill when the block is
|
||||
// smaller than UVM_VA_BLOCK_SIZE, but it's not much extra memory
|
||||
// overhead on the whole.
|
||||
uvm_page_mask_t resident;
|
||||
|
||||
// Per-page allocation bit vector.
|
||||
//
|
||||
// A set bit means that a CPU chunk has been allocated for the
|
||||
// corresponding page index on this NUMA node.
|
||||
uvm_page_mask_t allocated;
|
||||
|
||||
// CPU memory chunks represent physically contiguous CPU memory
|
||||
// allocations. See uvm_pmm_sysmem.h for more details on CPU chunks.
|
||||
// This member is meant to hold an opaque value indicating the CPU
|
||||
// chunk storage method. For more details on CPU chunk storage,
|
||||
// see uvm_cpu_chunk_storage_type_t in uvm_va_block.c.
|
||||
unsigned long chunks;
|
||||
} uvm_va_block_cpu_node_state_t;
|
||||
|
||||
// TODO: Bug 1766180: Worst-case we could have one of these per system page.
|
||||
// Options:
|
||||
// 1) Rely on the OOM killer to prevent the user from trying to do that
|
||||
@ -306,38 +343,30 @@ struct uvm_va_block_struct
|
||||
|
||||
struct
|
||||
{
|
||||
// Per-page residency bit vector, used for fast traversal of resident
|
||||
// pages.
|
||||
//
|
||||
// A set bit means the CPU has a coherent copy of the physical page
|
||||
// resident in its memory, and that the corresponding entry in the pages
|
||||
// array is present. This does not mean that the coherent copy is
|
||||
// currently mapped anywhere, however. A page may be resident on
|
||||
// multiple processors when in read-duplicate mode.
|
||||
//
|
||||
// A cleared bit means the CPU does not have a coherent copy of that
|
||||
// page resident. The corresponding entry in the pages array may or may
|
||||
// not present. If the entry is present, it's a cached page which can be
|
||||
// reused in the future.
|
||||
//
|
||||
// Allocating PAGES_PER_UVM_VA_BLOCK is overkill when the block is
|
||||
// smaller than UVM_VA_BLOCK_SIZE, but it's not much extra memory
|
||||
// overhead on the whole.
|
||||
uvm_page_mask_t resident;
|
||||
|
||||
// CPU memory chunks represent physically contiguous CPU memory
|
||||
// allocations. See uvm_pmm_sysmem.h for more details on CPU chunks.
|
||||
// This member is meant to hold an opaque value indicating the CPU
|
||||
// chunk storage method. For more details on CPU chunk storage,
|
||||
// see uvm_cpu_chunk_storage_type_t in uvm_va_block.c.
|
||||
unsigned long chunks;
|
||||
// Per-NUMA node tracking of CPU allocations.
|
||||
// This is a dense array with one entry per possible NUMA node.
|
||||
uvm_va_block_cpu_node_state_t **node_state;
|
||||
|
||||
// Per-page allocation bit vector.
|
||||
//
|
||||
// A set bit means that a CPU page has been allocated for the
|
||||
// corresponding page index.
|
||||
// corresponding page index on at least one CPU NUMA node.
|
||||
uvm_page_mask_t allocated;
|
||||
|
||||
// Per-page residency bit vector. See
|
||||
// uvm_va_block_cpu_numa_state_t::resident for a detailed description.
|
||||
// This mask is a cumulative mask (logical OR) of all
|
||||
// uvm_va_block_cpu_node_state_t::resident masks. It is meant to be used
|
||||
// only for fast testing of page residency when it matters only if the
|
||||
// page is resident on the CPU.
|
||||
//
|
||||
// Note that this mask cannot be set directly as this will cause
|
||||
// inconsistencies between this mask and the per-NUMA residency masks.
|
||||
// In order to properly maintain consistency between the per-NUMA masks
|
||||
// and this one, uvm_va_block_cpu_[set|clear]_residency_*() helpers
|
||||
// should be used.
|
||||
uvm_page_mask_t resident;
|
||||
|
||||
// Per-page mapping bit vectors, one per bit we need to track. These are
|
||||
// used for fast traversal of valid mappings in the block. These contain
|
||||
// all non-address bits needed to establish a virtual mapping on this
|
||||
@ -418,7 +447,8 @@ struct uvm_va_block_struct
|
||||
uvm_page_mask_t read_duplicated_pages;
|
||||
|
||||
// Mask to keep track of the pages that are not mapped on any non-UVM-Lite
|
||||
// processor.
|
||||
// processor. This mask is not used for HMM because the CPU can map pages
|
||||
// at any time without notifying the driver.
|
||||
// 0: Page is definitely not mapped by any processors
|
||||
// 1: Page may or may not be mapped by a processor
|
||||
//
|
||||
@ -525,6 +555,13 @@ struct uvm_va_block_wrapper_struct
|
||||
// a successful migration if this error flag is cleared.
|
||||
NvU32 inject_cpu_pages_allocation_error_count;
|
||||
|
||||
// A NUMA node ID on which any CPU chunks will be allocated from.
|
||||
// This will override any other setting and/or policy.
|
||||
// Note that the kernel is still free to allocate from any of the
|
||||
// nodes in the thread's policy.
|
||||
int cpu_chunk_allocation_target_id;
|
||||
int cpu_chunk_allocation_actual_id;
|
||||
|
||||
// Force the next eviction attempt on this block to fail. Used for
|
||||
// testing only.
|
||||
bool inject_eviction_error;
|
||||
@ -668,17 +705,12 @@ void uvm_va_block_context_free(uvm_va_block_context_t *va_block_context);
|
||||
// Initialization of an already-allocated uvm_va_block_context_t.
|
||||
//
|
||||
// mm is used to initialize the value of va_block_context->mm. NULL is allowed.
|
||||
static void uvm_va_block_context_init(uvm_va_block_context_t *va_block_context, struct mm_struct *mm)
|
||||
{
|
||||
UVM_ASSERT(va_block_context);
|
||||
void uvm_va_block_context_init(uvm_va_block_context_t *va_block_context, struct mm_struct *mm);
|
||||
|
||||
// Write garbage into the VA Block context to ensure that the UVM code
|
||||
// clears masks appropriately
|
||||
if (UVM_IS_DEBUG())
|
||||
memset(va_block_context, 0xff, sizeof(*va_block_context));
|
||||
|
||||
va_block_context->mm = mm;
|
||||
}
|
||||
// Return the preferred NUMA node ID for the block's policy.
|
||||
// If the preferred node ID is NUMA_NO_NODE, the current NUMA node ID
|
||||
// is returned.
|
||||
int uvm_va_block_context_get_node(uvm_va_block_context_t *va_block_context);
|
||||
|
||||
// TODO: Bug 1766480: Using only page masks instead of a combination of regions
|
||||
// and page masks could simplify the below APIs and their implementations
|
||||
@ -734,6 +766,9 @@ static void uvm_va_block_context_init(uvm_va_block_context_t *va_block_context,
|
||||
// those masks. It is the caller's responsiblity to zero the masks or
|
||||
// not first.
|
||||
//
|
||||
// va_block_context->make_resident.dest_nid is used to guide the NUMA node for
|
||||
// CPU allocations.
|
||||
//
|
||||
// Notably any status other than NV_OK indicates that the block's lock might
|
||||
// have been unlocked and relocked.
|
||||
//
|
||||
@ -1377,8 +1412,14 @@ static uvm_va_block_test_t *uvm_va_block_get_test(uvm_va_block_t *va_block)
|
||||
|
||||
// Get the page residency mask for a processor if it's known to be there.
|
||||
//
|
||||
// If the processor is the CPU, the residency mask for the NUMA node ID
|
||||
// specified by nid will be returned (see
|
||||
// uvm_va_block_cpu_node_state_t::resident). If nid is NUMA_NO_NODE,
|
||||
// the cumulative CPU residency mask will be returned (see
|
||||
// uvm_va_block_t::cpu::resident).
|
||||
//
|
||||
// If the processor is a GPU, this will assert that GPU state is indeed present.
|
||||
uvm_page_mask_t *uvm_va_block_resident_mask_get(uvm_va_block_t *block, uvm_processor_id_t processor);
|
||||
uvm_page_mask_t *uvm_va_block_resident_mask_get(uvm_va_block_t *block, uvm_processor_id_t processor, int nid);
|
||||
|
||||
// Get the page mapped mask for a processor. The returned mask cannot be
|
||||
// directly modified by the caller
|
||||
@ -1386,6 +1427,13 @@ uvm_page_mask_t *uvm_va_block_resident_mask_get(uvm_va_block_t *block, uvm_proce
|
||||
// If the processor is a GPU, this will assert that GPU state is indeed present.
|
||||
const uvm_page_mask_t *uvm_va_block_map_mask_get(uvm_va_block_t *block, uvm_processor_id_t processor);
|
||||
|
||||
// Return a mask of non-UVM-Lite pages that are unmapped within the given
|
||||
// region.
|
||||
// Locking: The block lock must be held.
|
||||
void uvm_va_block_unmapped_pages_get(uvm_va_block_t *va_block,
|
||||
uvm_va_block_region_t region,
|
||||
uvm_page_mask_t *out_mask);
|
||||
|
||||
// VA block lookup functions. There are a number of permutations which might be
|
||||
// useful, such as looking up the block from {va_space, va_range} x {addr,
|
||||
// block index}. The ones implemented here and in uvm_va_range.h support the
|
||||
@ -1756,17 +1804,28 @@ static bool uvm_page_mask_full(const uvm_page_mask_t *mask)
|
||||
return bitmap_full(mask->bitmap, PAGES_PER_UVM_VA_BLOCK);
|
||||
}
|
||||
|
||||
static bool uvm_page_mask_and(uvm_page_mask_t *mask_out, const uvm_page_mask_t *mask_in1, const uvm_page_mask_t *mask_in2)
|
||||
static void uvm_page_mask_fill(uvm_page_mask_t *mask)
|
||||
{
|
||||
bitmap_fill(mask->bitmap, PAGES_PER_UVM_VA_BLOCK);
|
||||
}
|
||||
|
||||
static bool uvm_page_mask_and(uvm_page_mask_t *mask_out,
|
||||
const uvm_page_mask_t *mask_in1,
|
||||
const uvm_page_mask_t *mask_in2)
|
||||
{
|
||||
return bitmap_and(mask_out->bitmap, mask_in1->bitmap, mask_in2->bitmap, PAGES_PER_UVM_VA_BLOCK);
|
||||
}
|
||||
|
||||
static bool uvm_page_mask_andnot(uvm_page_mask_t *mask_out, const uvm_page_mask_t *mask_in1, const uvm_page_mask_t *mask_in2)
|
||||
static bool uvm_page_mask_andnot(uvm_page_mask_t *mask_out,
|
||||
const uvm_page_mask_t *mask_in1,
|
||||
const uvm_page_mask_t *mask_in2)
|
||||
{
|
||||
return bitmap_andnot(mask_out->bitmap, mask_in1->bitmap, mask_in2->bitmap, PAGES_PER_UVM_VA_BLOCK);
|
||||
}
|
||||
|
||||
static void uvm_page_mask_or(uvm_page_mask_t *mask_out, const uvm_page_mask_t *mask_in1, const uvm_page_mask_t *mask_in2)
|
||||
static void uvm_page_mask_or(uvm_page_mask_t *mask_out,
|
||||
const uvm_page_mask_t *mask_in1,
|
||||
const uvm_page_mask_t *mask_in2)
|
||||
{
|
||||
bitmap_or(mask_out->bitmap, mask_in1->bitmap, mask_in2->bitmap, PAGES_PER_UVM_VA_BLOCK);
|
||||
}
|
||||
@ -2036,30 +2095,49 @@ uvm_processor_id_t uvm_va_block_page_get_closest_resident(uvm_va_block_t *va_blo
|
||||
uvm_page_index_t page_index,
|
||||
uvm_processor_id_t processor);
|
||||
|
||||
// Mark CPU page page_index as resident on NUMA node specified by nid.
|
||||
// nid cannot be NUMA_NO_NODE.
|
||||
void uvm_va_block_cpu_set_resident_page(uvm_va_block_t *va_block, int nid, uvm_page_index_t page_index);
|
||||
|
||||
// Test if a CPU page is resident on NUMA node nid. If nid is NUMA_NO_NODE,
|
||||
// the function will return True if the page is resident on any CPU NUMA node.
|
||||
bool uvm_va_block_cpu_is_page_resident_on(uvm_va_block_t *va_block, int nid, uvm_page_index_t page_index);
|
||||
|
||||
// Test if all pages in region are resident on NUMA node nid. If nid is
|
||||
// NUMA_NO_NODE, the function will test if the pages in the region are
|
||||
// resident on any CPU NUMA node.
|
||||
bool uvm_va_block_cpu_is_region_resident_on(uvm_va_block_t *va_block, int nid, uvm_va_block_region_t region);
|
||||
|
||||
// Insert a CPU chunk at the given page_index into the va_block.
|
||||
// Locking: The va_block lock must be held.
|
||||
NV_STATUS uvm_cpu_chunk_insert_in_block(uvm_va_block_t *va_block,
|
||||
uvm_cpu_chunk_t *chunk,
|
||||
uvm_page_index_t page_index);
|
||||
NV_STATUS uvm_cpu_chunk_insert_in_block(uvm_va_block_t *va_block, uvm_cpu_chunk_t *chunk, uvm_page_index_t page_index);
|
||||
|
||||
// Remove a CPU chunk at the given page_index from the va_block.
|
||||
// nid cannot be NUMA_NO_NODE.
|
||||
// Locking: The va_block lock must be held.
|
||||
void uvm_cpu_chunk_remove_from_block(uvm_va_block_t *va_block,
|
||||
uvm_page_index_t page_index);
|
||||
void uvm_cpu_chunk_remove_from_block(uvm_va_block_t *va_block, int nid, uvm_page_index_t page_index);
|
||||
|
||||
// Return the CPU chunk at the given page_index from the va_block.
|
||||
// Return the CPU chunk at the given page_index on the given NUMA node from the
|
||||
// va_block. nid cannot be NUMA_NO_NODE.
|
||||
// Locking: The va_block lock must be held.
|
||||
uvm_cpu_chunk_t *uvm_cpu_chunk_get_chunk_for_page(uvm_va_block_t *va_block,
|
||||
int nid,
|
||||
uvm_page_index_t page_index);
|
||||
|
||||
// Return the CPU chunk at the given page_index from the va_block.
|
||||
// Return the struct page * from the chunk corresponding to the given page_index
|
||||
// Locking: The va_block lock must be held.
|
||||
struct page *uvm_cpu_chunk_get_cpu_page(uvm_va_block_t *va_block,
|
||||
uvm_page_index_t page_index);
|
||||
struct page *uvm_cpu_chunk_get_cpu_page(uvm_va_block_t *va_block, uvm_cpu_chunk_t *chunk, uvm_page_index_t page_index);
|
||||
|
||||
// Return the struct page * of the resident chunk at the given page_index from
|
||||
// the va_block. The given page_index must be resident on the CPU.
|
||||
// Locking: The va_block lock must be held.
|
||||
struct page *uvm_va_block_get_cpu_page(uvm_va_block_t *va_block, uvm_page_index_t page_index);
|
||||
|
||||
// Physically map a CPU chunk so it is DMA'able from all registered GPUs.
|
||||
// nid cannot be NUMA_NO_NODE.
|
||||
// Locking: The va_block lock must be held.
|
||||
NV_STATUS uvm_va_block_map_cpu_chunk_on_gpus(uvm_va_block_t *va_block,
|
||||
uvm_cpu_chunk_t *chunk,
|
||||
uvm_page_index_t page_index);
|
||||
|
||||
// Physically unmap a CPU chunk from all registered GPUs.
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "uvm_forward_decl.h"
|
||||
|
||||
#include <linux/migrate.h>
|
||||
#include <linux/nodemask.h>
|
||||
|
||||
// UVM_VA_BLOCK_BITS is 21, meaning the maximum block size is 2MB. Rationale:
|
||||
// - 2MB matches the largest Pascal GPU page size so it's a natural fit
|
||||
@ -145,6 +146,18 @@ typedef struct
|
||||
unsigned count;
|
||||
} uvm_prot_page_mask_array_t[UVM_PROT_MAX - 1];
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// A per-NUMA-node array of page masks (size num_possible_nodes()) that hold
|
||||
// the set of CPU pages used by the migration operation.
|
||||
uvm_page_mask_t **node_masks;
|
||||
|
||||
// Node mask used to iterate over the page masks above.
|
||||
// If a node's bit is set, it means that the page mask given by
|
||||
// node_to_index() in node_masks has set pages.
|
||||
nodemask_t nodes;
|
||||
} uvm_make_resident_page_tracking_t;
|
||||
|
||||
// In the worst case some VA block operations require more state than we should
|
||||
// reasonably store on the stack. Instead, we dynamically allocate VA block
|
||||
// contexts. These are used for almost all operations on VA blocks.
|
||||
@ -159,6 +172,9 @@ typedef struct
|
||||
// this block_context.
|
||||
uvm_page_mask_t scratch_page_mask;
|
||||
|
||||
// Scratch node mask. This follows the same rules as scratch_page_mask;
|
||||
nodemask_t scratch_node_mask;
|
||||
|
||||
// State used by uvm_va_block_make_resident
|
||||
struct uvm_make_resident_context_struct
|
||||
{
|
||||
@ -181,10 +197,24 @@ typedef struct
|
||||
// Used to perform ECC checks after the migration is done.
|
||||
uvm_processor_mask_t all_involved_processors;
|
||||
|
||||
// Page mask used to compute the set of CPU pages for each CPU node.
|
||||
uvm_page_mask_t node_pages_mask;
|
||||
|
||||
// Final residency for the data. This is useful for callees to know if
|
||||
// a migration is part of a staging copy
|
||||
uvm_processor_id_t dest_id;
|
||||
|
||||
// Final residency NUMA node if the migration destination is the CPU.
|
||||
int dest_nid;
|
||||
|
||||
// This structure is used to track CPU pages used for migrations on
|
||||
// a per-NUMA node basis.
|
||||
//
|
||||
// The pages could be used for either migrations to the CPU (used to
|
||||
// track the destination CPU pages) or staging copies (used to track
|
||||
// the CPU pages used for the staging).
|
||||
uvm_make_resident_page_tracking_t cpu_pages_used;
|
||||
|
||||
// Event that triggered the call
|
||||
uvm_make_resident_cause_t cause;
|
||||
} make_resident;
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
const uvm_va_policy_t uvm_va_policy_default = {
|
||||
.preferred_location = UVM_ID_INVALID,
|
||||
.preferred_nid = NUMA_NO_NODE,
|
||||
.read_duplication = UVM_READ_DUPLICATION_UNSET,
|
||||
};
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user