forked from torvalds/linux
Permalink
Show file tree
Hide file tree
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
drivers: hv: dxgkrnl: Driver initialization and creation of dxgadapter
- Add support for a Hyper-V based vGPU implementation that exposes the DirectX API to Linux userspace. - Handle driver loading, registration for the PCI and VM bus device notifications - Add headers for user mode interfaces, internal driver objects and VM bus communication interface - Handle initialization of VM bus channels and creation of the dxgadapter object - Removed dxg_copy_from_user and dxg_copy_to_user - Connect the dxgkrnl module to the drivers/hv/ makefile and Kconfig. - Create a MAINTAINERS entry PCI driver registration A PCI device is created for each virtual GPU (vGPU) device, projected by the host. The device vendor is PCI_VENDOR_ID_MICROSOFT and device id is PCI_DEVICE_ID_VIRTUAL_RENDER. dxg_pci_probe_device handles arrival of such devices and it creates dxgadapter objects. The PCI config space of the vGPU device has luid of the corresponding per GPU VM bus channel. This is how the adapters are linked to VM bus channels. dxgadapter initialization A dxgadapter object represents a virtual GPU, projected to the VM by the host. This object can start functioning only when the global VM bus channel and the corresponding per vGPU VM bus channel are initialized in the guest. Notifications about arrival of vGPU PCI device and VM bus channels can happen in any order. Therefore, the initial dxgadapter object state is DXGADAPTER_STATE_WAITING_VMBUS. A list of VM bus channels and a list of dxgadapter objects are created. When dxgkrnl is notified about a VM bus channel arrival, if tries to start all adapters, which are not started yet. VM bus interface version is exchanged by reading/writing the PCI config space of the vGPU device. Signed-off-by: Iouri Tarassov <iourit@linux.microsoft.com>
- Loading branch information
1 parent
e3084ed
commit 00f97c12e2cf0ba4ba1108e2fce9a3d0e287cc8c
Showing
18 changed files
with
5,886 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| # | ||
| # dxgkrnl configuration | ||
| # | ||
|
|
||
| config DXGKRNL | ||
| tristate "Microsoft Paravirtualized GPU support" | ||
| depends on HYPERV | ||
| depends on 64BIT || COMPILE_TEST | ||
| help | ||
| This driver supports paravirtualized virtual compute devices, exposed | ||
| by Microsoft Hyper-V when Linux is running inside of a virtual machine | ||
| hosted by Windows. The virtual machines needs to be configured to use | ||
| host compute adapters. The driver name is dxgkrnl. | ||
|
|
||
| An example of such virtual machine is a Windows Subsystem for | ||
| Linux container. When such container is instantiated, the Windows host | ||
| assigns compatible host GPU adapters to the container. The corresponding | ||
| virtual GPU devices appear on the PCI bus in the container. These | ||
| devices are enumerated and accessed by this driver. | ||
|
|
||
| Communications with the driver are done by using the Microsoft libdxcore | ||
| library, which translates the D3DKMT interface | ||
| <https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/d3dkmthk/> | ||
| to the driver IOCTLs. The virtual GPU devices are paravirtualized, | ||
| which means that access to the hardware is done in the host. The driver | ||
| communicates with the host using Hyper-V VM bus communication channels. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| # SPDX-License-Identifier: GPL-2.0 | ||
| # Makefile for the Linux video drivers. | ||
|
|
||
| obj-$(CONFIG_DXGKRNL) += dxgkrnl.o | ||
| dxgkrnl-y := dxgmodule.o hmgr.o misc.o dxgadapter.o ioctl.o dxgvmbus.o dxgprocess.o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,189 @@ | ||
| // SPDX-License-Identifier: GPL-2.0 | ||
|
|
||
| /* | ||
| * Copyright (c) 2019, Microsoft Corporation. | ||
| * | ||
| * Author: | ||
| * Iouri Tarassov <iourit@linux.microsoft.com> | ||
| * | ||
| * Dxgkrnl Graphics Driver | ||
| * Implementation of dxgadapter and its objects | ||
| * | ||
| */ | ||
|
|
||
| #include <linux/module.h> | ||
| #include <linux/hyperv.h> | ||
| #include <linux/pagemap.h> | ||
| #include <linux/eventfd.h> | ||
|
|
||
| #include "dxgkrnl.h" | ||
|
|
||
| #undef pr_fmt | ||
| #define pr_fmt(fmt) "dxgk:err: " fmt | ||
| #undef dev_fmt | ||
| #define dev_fmt(fmt) "dxgk: " fmt | ||
|
|
||
| int dxgadapter_set_vmbus(struct dxgadapter *adapter, struct hv_device *hdev) | ||
| { | ||
| int ret; | ||
|
|
||
| guid_to_luid(&hdev->channel->offermsg.offer.if_instance, | ||
| &adapter->luid); | ||
| dev_dbg(dxgglobaldev, "%s: %x:%x %p %pUb\n", | ||
| __func__, adapter->luid.b, adapter->luid.a, hdev->channel, | ||
| &hdev->channel->offermsg.offer.if_instance); | ||
|
|
||
| ret = dxgvmbuschannel_init(&adapter->channel, hdev); | ||
| if (ret) | ||
| goto cleanup; | ||
|
|
||
| adapter->channel.adapter = adapter; | ||
| adapter->hv_dev = hdev; | ||
|
|
||
| ret = dxgvmb_send_open_adapter(adapter); | ||
| if (ret < 0) { | ||
| pr_err("dxgvmb_send_open_adapter failed: %d\n", ret); | ||
| goto cleanup; | ||
| } | ||
|
|
||
| ret = dxgvmb_send_get_internal_adapter_info(adapter); | ||
| if (ret < 0) | ||
| pr_err("get_internal_adapter_info failed: %d", ret); | ||
|
|
||
| cleanup: | ||
| if (ret) | ||
| dev_dbg(dxgglobaldev, "err: %s %d", __func__, ret); | ||
| return ret; | ||
| } | ||
|
|
||
| void dxgadapter_start(struct dxgadapter *adapter) | ||
| { | ||
| struct dxgvgpuchannel *ch = NULL; | ||
| struct dxgvgpuchannel *entry; | ||
| int ret; | ||
|
|
||
| dev_dbg(dxgglobaldev, "%s %x-%x", | ||
| __func__, adapter->luid.a, adapter->luid.b); | ||
|
|
||
| /* Find the corresponding vGPU vm bus channel */ | ||
| list_for_each_entry(entry, &dxgglobal->vgpu_ch_list_head, | ||
| vgpu_ch_list_entry) { | ||
| if (memcmp(&adapter->luid, | ||
| &entry->adapter_luid, | ||
| sizeof(struct winluid)) == 0) { | ||
| ch = entry; | ||
| break; | ||
| } | ||
| } | ||
| if (ch == NULL) { | ||
| dev_dbg(dxgglobaldev, "%s vGPU chanel is not ready", __func__); | ||
| return; | ||
| } | ||
|
|
||
| /* The global channel is initialized when the first adapter starts */ | ||
| if (!dxgglobal->global_channel_initialized) { | ||
| ret = dxgglobal_init_global_channel(); | ||
| if (ret) { | ||
| dxgglobal_destroy_global_channel(); | ||
| return; | ||
| } | ||
| dxgglobal->global_channel_initialized = true; | ||
| } | ||
|
|
||
| /* Initialize vGPU vm bus channel */ | ||
| ret = dxgadapter_set_vmbus(adapter, ch->hdev); | ||
| if (ret) { | ||
| pr_err("Failed to start adapter %p", adapter); | ||
| adapter->adapter_state = DXGADAPTER_STATE_STOPPED; | ||
| return; | ||
| } | ||
|
|
||
| adapter->adapter_state = DXGADAPTER_STATE_ACTIVE; | ||
| dev_dbg(dxgglobaldev, "%s Adapter started %p", __func__, adapter); | ||
| } | ||
|
|
||
| void dxgadapter_stop(struct dxgadapter *adapter) | ||
| { | ||
| struct dxgprocess_adapter *entry; | ||
| bool adapter_stopped = false; | ||
|
|
||
| down_write(&adapter->core_lock); | ||
| if (!adapter->stopping_adapter) | ||
| adapter->stopping_adapter = true; | ||
| else | ||
| adapter_stopped = true; | ||
| up_write(&adapter->core_lock); | ||
|
|
||
| if (adapter_stopped) | ||
| return; | ||
|
|
||
| dxgglobal_acquire_process_adapter_lock(); | ||
|
|
||
| list_for_each_entry(entry, &adapter->adapter_process_list_head, | ||
| adapter_process_list_entry) { | ||
| dxgprocess_adapter_stop(entry); | ||
| } | ||
|
|
||
| dxgglobal_release_process_adapter_lock(); | ||
|
|
||
| if (dxgadapter_acquire_lock_exclusive(adapter) == 0) { | ||
| dxgvmb_send_close_adapter(adapter); | ||
| dxgadapter_release_lock_exclusive(adapter); | ||
| } | ||
| dxgvmbuschannel_destroy(&adapter->channel); | ||
|
|
||
| adapter->adapter_state = DXGADAPTER_STATE_STOPPED; | ||
| } | ||
|
|
||
| void dxgadapter_release(struct kref *refcount) | ||
| { | ||
| struct dxgadapter *adapter; | ||
|
|
||
| adapter = container_of(refcount, struct dxgadapter, adapter_kref); | ||
| dev_dbg(dxgglobaldev, "%s %p\n", __func__, adapter); | ||
| vfree(adapter); | ||
| } | ||
|
|
||
| bool dxgadapter_is_active(struct dxgadapter *adapter) | ||
| { | ||
| return adapter->adapter_state == DXGADAPTER_STATE_ACTIVE; | ||
| } | ||
|
|
||
| int dxgadapter_acquire_lock_exclusive(struct dxgadapter *adapter) | ||
| { | ||
| down_write(&adapter->core_lock); | ||
| if (adapter->adapter_state != DXGADAPTER_STATE_ACTIVE) { | ||
| dxgadapter_release_lock_exclusive(adapter); | ||
| return -ENODEV; | ||
| } | ||
| return 0; | ||
| } | ||
|
|
||
| void dxgadapter_acquire_lock_forced(struct dxgadapter *adapter) | ||
| { | ||
| down_write(&adapter->core_lock); | ||
| } | ||
|
|
||
| void dxgadapter_release_lock_exclusive(struct dxgadapter *adapter) | ||
| { | ||
| up_write(&adapter->core_lock); | ||
| } | ||
|
|
||
| int dxgadapter_acquire_lock_shared(struct dxgadapter *adapter) | ||
| { | ||
| down_read(&adapter->core_lock); | ||
| if (adapter->adapter_state == DXGADAPTER_STATE_ACTIVE) | ||
| return 0; | ||
| dxgadapter_release_lock_shared(adapter); | ||
| return -ENODEV; | ||
| } | ||
|
|
||
| void dxgadapter_release_lock_shared(struct dxgadapter *adapter) | ||
| { | ||
| up_read(&adapter->core_lock); | ||
| } | ||
|
|
||
| void dxgprocess_adapter_stop(struct dxgprocess_adapter *adapter_info) | ||
| { | ||
| /* Placeholder */ | ||
| } |
Oops, something went wrong.