Skip to content
Permalink
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
Iouri Tarassov authored and intel-lab-lkp committed Jan 12, 2022
1 parent e3084ed commit 00f97c12e2cf0ba4ba1108e2fce9a3d0e287cc8c
Show file tree
Hide file tree
Showing 18 changed files with 5,886 additions and 0 deletions.
@@ -8937,6 +8937,13 @@ F: Documentation/devicetree/bindings/mtd/ti,am654-hbmc.yaml
F: drivers/mtd/hyperbus/
F: include/linux/mtd/hyperbus.h

Hyper-V vGPU DRIVER
M: Iouri Tarassov <iourit@microsoft.com>
L: linux-hyperv@vger.kernel.org
S: Supported
F: drivers/hv/dxgkrnl/
F: include/uapi/misc/d3dkmthk.h

HYPERVISOR VIRTUAL CONSOLE DRIVER
L: linuxppc-dev@lists.ozlabs.org
S: Odd Fixes
@@ -30,4 +30,6 @@ config HYPERV_BALLOON
help
Select this option to enable Hyper-V Balloon driver.

source "drivers/hv/dxgkrnl/Kconfig"

endmenu
@@ -2,6 +2,7 @@
obj-$(CONFIG_HYPERV) += hv_vmbus.o
obj-$(CONFIG_HYPERV_UTILS) += hv_utils.o
obj-$(CONFIG_HYPERV_BALLOON) += hv_balloon.o
obj-$(CONFIG_DXGKRNL) += dxgkrnl/

CFLAGS_hv_trace.o = -I$(src)
CFLAGS_hv_balloon.o = -I$(src)
@@ -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.
@@ -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
@@ -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 */
}

0 comments on commit 00f97c1

Please sign in to comment.