mirror of
https://github.com/armbian/build
synced 2025-09-24 19:47:06 +07:00
314 lines
10 KiB
Diff
314 lines
10 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Iouri Tarassov <iourit@linux.microsoft.com>
|
|
Date: Fri, 8 Oct 2021 14:17:39 -0700
|
|
Subject: drivers: hv: dxgkrnl: Add support to map guest pages by host
|
|
|
|
Implement support for mapping guest memory pages by the host.
|
|
This removes hyper-v limitations of using GPADL (guest physical
|
|
address list).
|
|
|
|
Dxgkrnl uses hyper-v GPADLs to share guest system memory with the
|
|
host. This method has limitations:
|
|
- a single GPADL can represent only ~32MB of memory
|
|
- there is a limit of how much memory the total size of GPADLs
|
|
in a VM can represent.
|
|
To avoid these limitations the host implemented mapping guest memory
|
|
pages. Presence of this support is determined by reading PCI config
|
|
space. When the support is enabled, dxgkrnl does not use GPADLs and
|
|
instead uses the following code flow:
|
|
- memory pages of an existing system memory buffer are pinned
|
|
- PFNs of the pages are sent to the host via a VM bus message
|
|
- the host maps the PFNs to get access to the memory
|
|
|
|
Signed-off-by: Iouri Tarassov <iourit@linux.microsoft.com>
|
|
[kms: Forward port to v6.1]
|
|
Signed-off-by: Kelsey Steele <kelseysteele@microsoft.com>
|
|
---
|
|
drivers/hv/dxgkrnl/Makefile | 2 +-
|
|
drivers/hv/dxgkrnl/dxgkrnl.h | 1 +
|
|
drivers/hv/dxgkrnl/dxgmodule.c | 33 ++-
|
|
drivers/hv/dxgkrnl/dxgvmbus.c | 117 +++++++---
|
|
drivers/hv/dxgkrnl/dxgvmbus.h | 10 +
|
|
drivers/hv/dxgkrnl/misc.c | 1 +
|
|
6 files changed, 129 insertions(+), 35 deletions(-)
|
|
|
|
diff --git a/drivers/hv/dxgkrnl/Makefile b/drivers/hv/dxgkrnl/Makefile
|
|
index 111111111111..222222222222 100644
|
|
--- a/drivers/hv/dxgkrnl/Makefile
|
|
+++ b/drivers/hv/dxgkrnl/Makefile
|
|
@@ -2,4 +2,4 @@
|
|
# Makefile for the hyper-v compute device driver (dxgkrnl).
|
|
|
|
obj-$(CONFIG_DXGKRNL) += dxgkrnl.o
|
|
-dxgkrnl-y := dxgmodule.o hmgr.o misc.o dxgadapter.o ioctl.o dxgvmbus.o dxgprocess.o
|
|
+dxgkrnl-y := dxgmodule.o hmgr.o misc.o dxgadapter.o ioctl.o dxgvmbus.o dxgprocess.o
|
|
diff --git a/drivers/hv/dxgkrnl/dxgkrnl.h b/drivers/hv/dxgkrnl/dxgkrnl.h
|
|
index 111111111111..222222222222 100644
|
|
--- a/drivers/hv/dxgkrnl/dxgkrnl.h
|
|
+++ b/drivers/hv/dxgkrnl/dxgkrnl.h
|
|
@@ -316,6 +316,7 @@ struct dxgglobal {
|
|
bool misc_registered;
|
|
bool pci_registered;
|
|
bool vmbus_registered;
|
|
+ bool map_guest_pages_enabled;
|
|
};
|
|
|
|
static inline struct dxgglobal *dxggbl(void)
|
|
diff --git a/drivers/hv/dxgkrnl/dxgmodule.c b/drivers/hv/dxgkrnl/dxgmodule.c
|
|
index 111111111111..222222222222 100644
|
|
--- a/drivers/hv/dxgkrnl/dxgmodule.c
|
|
+++ b/drivers/hv/dxgkrnl/dxgmodule.c
|
|
@@ -147,7 +147,7 @@ void dxgglobal_remove_host_event(struct dxghostevent *event)
|
|
|
|
void signal_host_cpu_event(struct dxghostevent *eventhdr)
|
|
{
|
|
- struct dxghosteventcpu *event = (struct dxghosteventcpu *)eventhdr;
|
|
+ struct dxghosteventcpu *event = (struct dxghosteventcpu *)eventhdr;
|
|
|
|
if (event->remove_from_list ||
|
|
event->destroy_after_signal) {
|
|
@@ -426,7 +426,11 @@ const struct file_operations dxgk_fops = {
|
|
#define DXGK_VMBUS_VGPU_LUID_OFFSET (DXGK_VMBUS_VERSION_OFFSET + \
|
|
sizeof(u32))
|
|
|
|
-/* The guest writes its capabilities to this address */
|
|
+/* The host caps (dxgk_vmbus_hostcaps) */
|
|
+#define DXGK_VMBUS_HOSTCAPS_OFFSET (DXGK_VMBUS_VGPU_LUID_OFFSET + \
|
|
+ sizeof(struct winluid))
|
|
+
|
|
+/* The guest writes its capavilities to this adderss */
|
|
#define DXGK_VMBUS_GUESTCAPS_OFFSET (DXGK_VMBUS_VERSION_OFFSET + \
|
|
sizeof(u32))
|
|
|
|
@@ -441,6 +445,23 @@ struct dxgk_vmbus_guestcaps {
|
|
};
|
|
};
|
|
|
|
+/*
|
|
+ * The structure defines features, supported by the host.
|
|
+ *
|
|
+ * map_guest_memory
|
|
+ * Host can map guest memory pages, so the guest can avoid using GPADLs
|
|
+ * to represent existing system memory allocations.
|
|
+ */
|
|
+struct dxgk_vmbus_hostcaps {
|
|
+ union {
|
|
+ struct {
|
|
+ u32 map_guest_memory : 1;
|
|
+ u32 reserved : 31;
|
|
+ };
|
|
+ u32 host_caps;
|
|
+ };
|
|
+};
|
|
+
|
|
/*
|
|
* A helper function to read PCI config space.
|
|
*/
|
|
@@ -475,6 +496,7 @@ static int dxg_pci_probe_device(struct pci_dev *dev,
|
|
struct winluid vgpu_luid = {};
|
|
struct dxgk_vmbus_guestcaps guest_caps = {.wsl2 = 1};
|
|
struct dxgglobal *dxgglobal = dxggbl();
|
|
+ struct dxgk_vmbus_hostcaps host_caps = {};
|
|
|
|
mutex_lock(&dxgglobal->device_mutex);
|
|
|
|
@@ -503,6 +525,13 @@ static int dxg_pci_probe_device(struct pci_dev *dev,
|
|
if (ret)
|
|
goto cleanup;
|
|
|
|
+ ret = pci_read_config_dword(dev, DXGK_VMBUS_HOSTCAPS_OFFSET,
|
|
+ &host_caps.host_caps);
|
|
+ if (ret == 0) {
|
|
+ if (host_caps.map_guest_memory)
|
|
+ dxgglobal->map_guest_pages_enabled = true;
|
|
+ }
|
|
+
|
|
if (dxgglobal->vmbus_ver > DXGK_VMBUS_INTERFACE_VERSION)
|
|
dxgglobal->vmbus_ver = DXGK_VMBUS_INTERFACE_VERSION;
|
|
}
|
|
diff --git a/drivers/hv/dxgkrnl/dxgvmbus.c b/drivers/hv/dxgkrnl/dxgvmbus.c
|
|
index 111111111111..222222222222 100644
|
|
--- a/drivers/hv/dxgkrnl/dxgvmbus.c
|
|
+++ b/drivers/hv/dxgkrnl/dxgvmbus.c
|
|
@@ -1383,15 +1383,19 @@ int create_existing_sysmem(struct dxgdevice *device,
|
|
void *kmem = NULL;
|
|
int ret = 0;
|
|
struct dxgkvmb_command_setexistingsysmemstore *set_store_command;
|
|
+ struct dxgkvmb_command_setexistingsysmempages *set_pages_command;
|
|
u64 alloc_size = host_alloc->allocation_size;
|
|
u32 npages = alloc_size >> PAGE_SHIFT;
|
|
struct dxgvmbusmsg msg = {.hdr = NULL};
|
|
-
|
|
- ret = init_message(&msg, device->adapter, device->process,
|
|
- sizeof(*set_store_command));
|
|
- if (ret)
|
|
- goto cleanup;
|
|
- set_store_command = (void *)msg.msg;
|
|
+ const u32 max_pfns_in_message =
|
|
+ (DXG_MAX_VM_BUS_PACKET_SIZE - sizeof(*set_pages_command) -
|
|
+ PAGE_SIZE) / sizeof(__u64);
|
|
+ u32 alloc_offset_in_pages = 0;
|
|
+ struct page **page_in;
|
|
+ u64 *pfn;
|
|
+ u32 pages_to_send;
|
|
+ u32 i;
|
|
+ struct dxgglobal *dxgglobal = dxggbl();
|
|
|
|
/*
|
|
* Create a guest physical address list and set it as the allocation
|
|
@@ -1402,6 +1406,7 @@ int create_existing_sysmem(struct dxgdevice *device,
|
|
DXG_TRACE("Alloc size: %lld", alloc_size);
|
|
|
|
dxgalloc->cpu_address = (void *)sysmem;
|
|
+
|
|
dxgalloc->pages = vzalloc(npages * sizeof(void *));
|
|
if (dxgalloc->pages == NULL) {
|
|
DXG_ERR("failed to allocate pages");
|
|
@@ -1419,39 +1424,87 @@ int create_existing_sysmem(struct dxgdevice *device,
|
|
ret = -ENOMEM;
|
|
goto cleanup;
|
|
}
|
|
- kmem = vmap(dxgalloc->pages, npages, VM_MAP, PAGE_KERNEL);
|
|
- if (kmem == NULL) {
|
|
- DXG_ERR("vmap failed");
|
|
- ret = -ENOMEM;
|
|
- goto cleanup;
|
|
- }
|
|
- ret1 = vmbus_establish_gpadl(dxgglobal_get_vmbus(), kmem,
|
|
- alloc_size, &dxgalloc->gpadl);
|
|
- if (ret1) {
|
|
- DXG_ERR("establish_gpadl failed: %d", ret1);
|
|
- ret = -ENOMEM;
|
|
- goto cleanup;
|
|
- }
|
|
+ if (!dxgglobal->map_guest_pages_enabled) {
|
|
+ ret = init_message(&msg, device->adapter, device->process,
|
|
+ sizeof(*set_store_command));
|
|
+ if (ret)
|
|
+ goto cleanup;
|
|
+ set_store_command = (void *)msg.msg;
|
|
+
|
|
+ kmem = vmap(dxgalloc->pages, npages, VM_MAP, PAGE_KERNEL);
|
|
+ if (kmem == NULL) {
|
|
+ DXG_ERR("vmap failed");
|
|
+ ret = -ENOMEM;
|
|
+ goto cleanup;
|
|
+ }
|
|
+ ret1 = vmbus_establish_gpadl(dxgglobal_get_vmbus(), kmem,
|
|
+ alloc_size, &dxgalloc->gpadl);
|
|
+ if (ret1) {
|
|
+ DXG_ERR("establish_gpadl failed: %d", ret1);
|
|
+ ret = -ENOMEM;
|
|
+ goto cleanup;
|
|
+ }
|
|
#ifdef _MAIN_KERNEL_
|
|
- DXG_TRACE("New gpadl %d", dxgalloc->gpadl.gpadl_handle);
|
|
+ DXG_TRACE("New gpadl %d", dxgalloc->gpadl.gpadl_handle);
|
|
#else
|
|
- DXG_TRACE("New gpadl %d", dxgalloc->gpadl);
|
|
+ DXG_TRACE("New gpadl %d", dxgalloc->gpadl);
|
|
#endif
|
|
|
|
- command_vgpu_to_host_init2(&set_store_command->hdr,
|
|
- DXGK_VMBCOMMAND_SETEXISTINGSYSMEMSTORE,
|
|
- device->process->host_handle);
|
|
- set_store_command->device = device->handle;
|
|
- set_store_command->device = device->handle;
|
|
- set_store_command->allocation = host_alloc->allocation;
|
|
+ command_vgpu_to_host_init2(&set_store_command->hdr,
|
|
+ DXGK_VMBCOMMAND_SETEXISTINGSYSMEMSTORE,
|
|
+ device->process->host_handle);
|
|
+ set_store_command->device = device->handle;
|
|
+ set_store_command->allocation = host_alloc->allocation;
|
|
#ifdef _MAIN_KERNEL_
|
|
- set_store_command->gpadl = dxgalloc->gpadl.gpadl_handle;
|
|
+ set_store_command->gpadl = dxgalloc->gpadl.gpadl_handle;
|
|
#else
|
|
- set_store_command->gpadl = dxgalloc->gpadl;
|
|
+ set_store_command->gpadl = dxgalloc->gpadl;
|
|
#endif
|
|
- ret = dxgvmb_send_sync_msg_ntstatus(msg.channel, msg.hdr, msg.size);
|
|
- if (ret < 0)
|
|
- DXG_ERR("failed to set existing store: %x", ret);
|
|
+ ret = dxgvmb_send_sync_msg_ntstatus(msg.channel, msg.hdr,
|
|
+ msg.size);
|
|
+ if (ret < 0)
|
|
+ DXG_ERR("failed set existing store: %x", ret);
|
|
+ } else {
|
|
+ /*
|
|
+ * Send the list of the allocation PFNs to the host. The host
|
|
+ * will map the pages for GPU access.
|
|
+ */
|
|
+
|
|
+ ret = init_message(&msg, device->adapter, device->process,
|
|
+ sizeof(*set_pages_command) +
|
|
+ max_pfns_in_message * sizeof(u64));
|
|
+ if (ret)
|
|
+ goto cleanup;
|
|
+ set_pages_command = (void *)msg.msg;
|
|
+ command_vgpu_to_host_init2(&set_pages_command->hdr,
|
|
+ DXGK_VMBCOMMAND_SETEXISTINGSYSMEMPAGES,
|
|
+ device->process->host_handle);
|
|
+ set_pages_command->device = device->handle;
|
|
+ set_pages_command->allocation = host_alloc->allocation;
|
|
+
|
|
+ page_in = dxgalloc->pages;
|
|
+ while (alloc_offset_in_pages < npages) {
|
|
+ pfn = (u64 *)((char *)msg.msg +
|
|
+ sizeof(*set_pages_command));
|
|
+ pages_to_send = min(npages - alloc_offset_in_pages,
|
|
+ max_pfns_in_message);
|
|
+ set_pages_command->num_pages = pages_to_send;
|
|
+ set_pages_command->alloc_offset_in_pages =
|
|
+ alloc_offset_in_pages;
|
|
+
|
|
+ for (i = 0; i < pages_to_send; i++)
|
|
+ *pfn++ = page_to_pfn(*page_in++);
|
|
+
|
|
+ ret = dxgvmb_send_sync_msg_ntstatus(msg.channel,
|
|
+ msg.hdr,
|
|
+ msg.size);
|
|
+ if (ret < 0) {
|
|
+ DXG_ERR("failed set existing pages: %x", ret);
|
|
+ break;
|
|
+ }
|
|
+ alloc_offset_in_pages += pages_to_send;
|
|
+ }
|
|
+ }
|
|
|
|
cleanup:
|
|
if (kmem)
|
|
diff --git a/drivers/hv/dxgkrnl/dxgvmbus.h b/drivers/hv/dxgkrnl/dxgvmbus.h
|
|
index 111111111111..222222222222 100644
|
|
--- a/drivers/hv/dxgkrnl/dxgvmbus.h
|
|
+++ b/drivers/hv/dxgkrnl/dxgvmbus.h
|
|
@@ -234,6 +234,16 @@ struct dxgkvmb_command_setexistingsysmemstore {
|
|
u32 gpadl;
|
|
};
|
|
|
|
+/* Returns ntstatus */
|
|
+struct dxgkvmb_command_setexistingsysmempages {
|
|
+ struct dxgkvmb_command_vgpu_to_host hdr;
|
|
+ struct d3dkmthandle device;
|
|
+ struct d3dkmthandle allocation;
|
|
+ u32 num_pages;
|
|
+ u32 alloc_offset_in_pages;
|
|
+ /* u64 pfn_array[num_pages] */
|
|
+};
|
|
+
|
|
struct dxgkvmb_command_createprocess {
|
|
struct dxgkvmb_command_vm_to_host hdr;
|
|
void *process;
|
|
diff --git a/drivers/hv/dxgkrnl/misc.c b/drivers/hv/dxgkrnl/misc.c
|
|
index 111111111111..222222222222 100644
|
|
--- a/drivers/hv/dxgkrnl/misc.c
|
|
+++ b/drivers/hv/dxgkrnl/misc.c
|
|
@@ -35,3 +35,4 @@ u16 *wcsncpy(u16 *dest, const u16 *src, size_t n)
|
|
dest[i - 1] = 0;
|
|
return dest;
|
|
}
|
|
+
|
|
--
|
|
Armbian
|
|
|