From nobody Mon Apr 6 10:42:03 2026 Received: from mail-wm1-f54.google.com (mail-wm1-f54.google.com [209.85.128.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 53E683F7E80 for ; Thu, 19 Mar 2026 20:25:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773951923; cv=none; b=BmkwvY49tP+O3+LNjnERSWpuPY0siLuE8sZs2A5FHDc7hsONjAntpAWmKHdBkDkxoNhrEp46HGpUwzhLg+fXPjRIhCqpvY4wukYYgUs6jtuJ2hz3xOjPPftTdd7Z/VJeRqhL1wMEJiFePlRcXzH2sOUNFCi9nG7dmWa+qre3Co8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773951923; c=relaxed/simple; bh=mpxogzttqGY2RdWi+DEFWY0TBddMViPrUSqcjrOXYUg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=moSuxQPDEBsD87j9iRrVvC39semsQnxMrCZaOVK26jU99m/a8Bw64sVBvu07SMP2tj1LrJnCOHxsmj7i1/DWmFN9PgCvZwOzPoDbnyFsIHpdTemlGSPxMjJQfENwmDyKyTY6BBb6Vm6jc07bQABuQlRcsLc0pS0SehRHvM0svHU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=e2WYGxAp; arc=none smtp.client-ip=209.85.128.54 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="e2WYGxAp" Received: by mail-wm1-f54.google.com with SMTP id 5b1f17b1804b1-485392de558so9135135e9.1 for ; Thu, 19 Mar 2026 13:25:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1773951918; x=1774556718; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=aQmUt8PE03Mo2YFvmbyVRyjPor6QZPpPG2GHlQ6KxFI=; b=e2WYGxApfteDMeNfmsoOPnZ440Q+TkwOcpPjksgxI/9o2GNHVvVJKbNJW5v4obkNd4 pHYDXmtAB9AGQdSv37Ckzi5gx70OSVKiLmjHmbKYJaSD8B4NzANv09G/3V2cxiBiLOuv 4eqw24cLMjpKc0bRiKzV9AXxjQ2ULEzTdtcXFS4CgZJCjkjNgCMFogcMtLdAdpXwa6+9 mzMCVqE0D2qVuWLvRZTJeViwwBDcR9cUPyZLE+kXHi1YooVZOx2Wa1k7o+tPbtnWocAY Jp+McMVKlwmIbc+v52wFodDgoGSry4a8bAzxmo65PAcNRfkFBeF4OVCJTroxxb//aOZ4 AhLg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773951918; x=1774556718; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=aQmUt8PE03Mo2YFvmbyVRyjPor6QZPpPG2GHlQ6KxFI=; b=ftcvcRAVaTL2pi5fXG8Y4RK0n2ZsYdqhTWRcUgdfyVYRtMtrrX/BYGXmCg/g/uRE8P 84HA0uniBJ/d0Nng3FS2yKYUaBVpJlQ/sOsxkaOdaJ1ppBtdWdrTeceM/z/uKuMhQ7gY Akndx2POon3I3xSbsA9W4Dau28EWrqiAGy96DcurGlH/PH0MjeB7niSWzzsQZml0EZEE eSWg2YbiU2Nlmbi3TWhVjrh17jZMa6AI2Rc1K9Ry4+qBOgTAWHohcaQVjRlzIXWcIzID AzHk+b3y96Ieg44FjVzD3F7GM4cwWrwqoNPVPqwA77tZokXmuWhIxOMS7hzBAxW2UMGM yhNw== X-Gm-Message-State: AOJu0YysMCehyQk1BgLbaLZ50SAAxEpKjHndiEIwQDJw/FnnJq3CVvpL iHi5QlwSE/qDHw4ZDdGtMCfI12yhY7U3GHrOYUVUNv7WSA7DFQ1OdCsCDUhgTrcQof4= X-Gm-Gg: ATEYQzzE2llH72L6+goYyJOA0HN9AJF7m0igWbBQHs2ElAF1gd6niqu3eeaSz0hUgwv C4GbHtKLiKGry991XXmQfl2Ts4kx8kvJWdquxkESi5hJ+JaVDidLKs+r3wYBhTTZATuCr/2yQrx 8BELqpe7+GdUw8Aw4YrsqfWOhaQqoZRWs5MCcX4UsyZGlOc67W7qdePG44fdA1VpFvg7NWspU2h aF4i8XK++fIntDMcjadaCO9KTuURu5z6asB1WwYRPwo2T5n9SSLUNgztCDMaKWbe/junyg77XAs jNpKlWakXO5kmu9j1QfoG5TiOiGrbEH7PWEytcMAaxWIpASEycLfIWY/0Q6zsHoKbOv98zfzlmE 9BnEE/jxp+THg8JMvGFYlMinCOi5RA1i8XULV0TJM5UMEDakJgCU3PYc34o8LxeIovKKbmVIX0Q 2z3qw71N1M07KaK3ktcHz1rbQD8apr3EdScEVVxNV2PqAnQ0ci X-Received: by 2002:a05:600c:3112:b0:485:3f72:323f with SMTP id 5b1f17b1804b1-486fedb8c6emr7194075e9.11.1773951917225; Thu, 19 Mar 2026 13:25:17 -0700 (PDT) Received: from LQ5W56KC4T ([2001:8a0:672f:7800:e0e1:55cd:f0b:b1e5]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43b644ae16fsm1347544f8f.8.2026.03.19.13.25.16 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Thu, 19 Mar 2026 13:25:16 -0700 (PDT) From: Eric Curtin X-Google-Original-From: Eric Curtin To: linux-hyperv@vger.kernel.org Cc: linux-kernel@vger.kernel.org, iourit@linux.microsoft.com, wei.liu@kernel.org, decui@microsoft.com, haiyangz@microsoft.com Subject: [PATCH 04/55] drivers: hv: dxgkrnl: Opening of /dev/dxg device and dxgprocess creation Date: Thu, 19 Mar 2026 20:24:18 +0000 Message-ID: <20260319202509.63802-5-eric.curtin@docker.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260319202509.63802-1-eric.curtin@docker.com> References: <20260319202509.63802-1-eric.curtin@docker.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Iouri Tarassov - Implement opening of the device (/dev/dxg) file object and creation of dxgprocess objects. - Add VM bus messages to create and destroy the host side of a dxgprocess object. - Implement the handle manager, which manages d3dkmthandle handles for the internal process objects. The handles are used by a user mode client to reference dxgkrnl objects. dxgprocess is created for each process, which opens /dev/dxg. dxgprocess is ref counted, so the existing dxgprocess objects is used for a process, which opens the device object multiple time. dxgprocess is destroyed when the file object is released. A corresponding dxgprocess object is created on the host for every dxgprocess object in the guest. When a dxgkrnl object is created, in most cases the corresponding object is created in the host. The VM references the host objects by handles (d3dkmthandle). d3dkmthandle values for a host object and the corresponding VM object are the same. A host handle is allocated first and its value is assigned to the guest object. Signed-off-by: Iouri Tarassov [kms: forward port to 6.6 from 6.1. No code changes made.] Signed-off-by: Kelsey Steele --- drivers/hv/dxgkrnl/Makefile | 2 +- drivers/hv/dxgkrnl/dxgadapter.c | 72 ++++ drivers/hv/dxgkrnl/dxgkrnl.h | 95 +++++- drivers/hv/dxgkrnl/dxgmodule.c | 97 ++++++ drivers/hv/dxgkrnl/dxgprocess.c | 262 +++++++++++++++ drivers/hv/dxgkrnl/dxgvmbus.c | 164 ++++++++++ drivers/hv/dxgkrnl/dxgvmbus.h | 36 ++ drivers/hv/dxgkrnl/hmgr.c | 563 ++++++++++++++++++++++++++++++++ drivers/hv/dxgkrnl/hmgr.h | 112 +++++++ drivers/hv/dxgkrnl/ioctl.c | 60 ++++ drivers/hv/dxgkrnl/misc.h | 9 +- include/uapi/misc/d3dkmthk.h | 103 ++++++ 12 files changed, 1569 insertions(+), 6 deletions(-) create mode 100644 drivers/hv/dxgkrnl/dxgprocess.c create mode 100644 drivers/hv/dxgkrnl/hmgr.c create mode 100644 drivers/hv/dxgkrnl/hmgr.h diff --git a/drivers/hv/dxgkrnl/Makefile b/drivers/hv/dxgkrnl/Makefile index 2ed07d877c91..9d821e83448a 100644 --- a/drivers/hv/dxgkrnl/Makefile +++ b/drivers/hv/dxgkrnl/Makefile @@ -2,4 +2,4 @@ # Makefile for the hyper-v compute device driver (dxgkrnl). =20 obj-$(CONFIG_DXGKRNL) +=3D dxgkrnl.o -dxgkrnl-y :=3D dxgmodule.o misc.o dxgadapter.o ioctl.o dxgvmbus.o +dxgkrnl-y :=3D dxgmodule.o hmgr.o misc.o dxgadapter.o ioctl.o dxgvmbus.o = dxgprocess.o diff --git a/drivers/hv/dxgkrnl/dxgadapter.c b/drivers/hv/dxgkrnl/dxgadapte= r.c index 07d47699d255..fa0d6beca157 100644 --- a/drivers/hv/dxgkrnl/dxgadapter.c +++ b/drivers/hv/dxgkrnl/dxgadapter.c @@ -100,6 +100,7 @@ void dxgadapter_start(struct dxgadapter *adapter) =20 void dxgadapter_stop(struct dxgadapter *adapter) { + struct dxgprocess_adapter *entry; bool adapter_stopped =3D false; =20 down_write(&adapter->core_lock); @@ -112,6 +113,15 @@ void dxgadapter_stop(struct dxgadapter *adapter) if (adapter_stopped) return; =20 + 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) =3D=3D 0) { dxgvmb_send_close_adapter(adapter); dxgadapter_release_lock_exclusive(adapter); @@ -135,6 +145,21 @@ bool dxgadapter_is_active(struct dxgadapter *adapter) return adapter->adapter_state =3D=3D DXGADAPTER_STATE_ACTIVE; } =20 +/* Protected by dxgglobal_acquire_process_adapter_lock */ +void dxgadapter_add_process(struct dxgadapter *adapter, + struct dxgprocess_adapter *process_info) +{ + DXG_TRACE("%p %p", adapter, process_info); + list_add_tail(&process_info->adapter_process_list_entry, + &adapter->adapter_process_list_head); +} + +void dxgadapter_remove_process(struct dxgprocess_adapter *process_info) +{ + DXG_TRACE("%p %p", process_info->adapter, process_info); + list_del(&process_info->adapter_process_list_entry); +} + int dxgadapter_acquire_lock_exclusive(struct dxgadapter *adapter) { down_write(&adapter->core_lock); @@ -168,3 +193,50 @@ void dxgadapter_release_lock_shared(struct dxgadapter = *adapter) { up_read(&adapter->core_lock); } + +struct dxgprocess_adapter *dxgprocess_adapter_create(struct dxgprocess *pr= ocess, + struct dxgadapter *adapter) +{ + struct dxgprocess_adapter *adapter_info; + + adapter_info =3D kzalloc(sizeof(*adapter_info), GFP_KERNEL); + if (adapter_info) { + if (kref_get_unless_zero(&adapter->adapter_kref) =3D=3D 0) { + DXG_ERR("failed to acquire adapter reference"); + goto cleanup; + } + adapter_info->adapter =3D adapter; + adapter_info->process =3D process; + adapter_info->refcount =3D 1; + list_add_tail(&adapter_info->process_adapter_list_entry, + &process->process_adapter_list_head); + dxgadapter_add_process(adapter, adapter_info); + } + return adapter_info; +cleanup: + if (adapter_info) + kfree(adapter_info); + return NULL; +} + +void dxgprocess_adapter_stop(struct dxgprocess_adapter *adapter_info) +{ +} + +void dxgprocess_adapter_destroy(struct dxgprocess_adapter *adapter_info) +{ + dxgadapter_remove_process(adapter_info); + kref_put(&adapter_info->adapter->adapter_kref, dxgadapter_release); + list_del(&adapter_info->process_adapter_list_entry); + kfree(adapter_info); +} + +/* + * Must be called when dxgglobal::process_adapter_mutex is held + */ +void dxgprocess_adapter_release(struct dxgprocess_adapter *adapter_info) +{ + adapter_info->refcount--; + if (adapter_info->refcount =3D=3D 0) + dxgprocess_adapter_destroy(adapter_info); +} diff --git a/drivers/hv/dxgkrnl/dxgkrnl.h b/drivers/hv/dxgkrnl/dxgkrnl.h index ba2a7c6001aa..b089d126f801 100644 --- a/drivers/hv/dxgkrnl/dxgkrnl.h +++ b/drivers/hv/dxgkrnl/dxgkrnl.h @@ -29,8 +29,10 @@ #include #include #include "misc.h" +#include "hmgr.h" #include =20 +struct dxgprocess; struct dxgadapter; =20 /* @@ -111,6 +113,10 @@ struct dxgglobal { struct miscdevice dxgdevice; struct mutex device_mutex; =20 + /* list of created processes */ + struct list_head plisthead; + struct mutex plistmutex; + /* list of created adapters */ struct list_head adapter_list_head; struct rw_semaphore adapter_list_lock; @@ -124,6 +130,9 @@ struct dxgglobal { /* protects acces to the global VM bus channel */ struct rw_semaphore channel_lock; =20 + /* protects the dxgprocess_adapter lists */ + struct mutex process_adapter_mutex; + bool global_channel_initialized; bool async_msg_enabled; bool misc_registered; @@ -144,13 +153,84 @@ int dxgglobal_init_global_channel(void); void dxgglobal_destroy_global_channel(void); struct vmbus_channel *dxgglobal_get_vmbus(void); struct dxgvmbuschannel *dxgglobal_get_dxgvmbuschannel(void); +void dxgglobal_acquire_process_adapter_lock(void); +void dxgglobal_release_process_adapter_lock(void); int dxgglobal_acquire_channel_lock(void); void dxgglobal_release_channel_lock(void); =20 +/* + * Describes adapter information for each process + */ +struct dxgprocess_adapter { + /* Entry in dxgadapter::adapter_process_list_head */ + struct list_head adapter_process_list_entry; + /* Entry in dxgprocess::process_adapter_list_head */ + struct list_head process_adapter_list_entry; + struct dxgadapter *adapter; + struct dxgprocess *process; + int refcount; +}; + +struct dxgprocess_adapter *dxgprocess_adapter_create(struct dxgprocess *pr= ocess, + struct dxgadapter + *adapter); +void dxgprocess_adapter_release(struct dxgprocess_adapter *adapter); +void dxgprocess_adapter_stop(struct dxgprocess_adapter *adapter_info); +void dxgprocess_adapter_destroy(struct dxgprocess_adapter *adapter_info); + +/* + * The structure represents a process, which opened the /dev/dxg device. + * A corresponding object is created on the host. + */ struct dxgprocess { - /* Placeholder */ + /* + * Process list entry in dxgglobal. + * Protected by the dxgglobal->plistmutex. + */ + struct list_head plistentry; + pid_t pid; + pid_t tgid; + /* how many time the process was opened */ + struct kref process_kref; + /* + * This handle table is used for all objects except dxgadapter + * The handle table lock order is higher than the local_handle_table + * lock + */ + struct hmgrtable handle_table; + /* + * This handle table is used for dxgadapter objects. + * The handle table lock order is lowest. + */ + struct hmgrtable local_handle_table; + /* Handle of the corresponding objec on the host */ + struct d3dkmthandle host_handle; + + /* List of opened adapters (dxgprocess_adapter) */ + struct list_head process_adapter_list_head; }; =20 +struct dxgprocess *dxgprocess_create(void); +void dxgprocess_destroy(struct dxgprocess *process); +void dxgprocess_release(struct kref *refcount); +int dxgprocess_open_adapter(struct dxgprocess *process, + struct dxgadapter *adapter, + struct d3dkmthandle *handle); +int dxgprocess_close_adapter(struct dxgprocess *process, + struct d3dkmthandle handle); +struct dxgadapter *dxgprocess_get_adapter(struct dxgprocess *process, + struct d3dkmthandle handle); +struct dxgadapter *dxgprocess_adapter_by_handle(struct dxgprocess *process, + struct d3dkmthandle handle); +void dxgprocess_ht_lock_shared_down(struct dxgprocess *process); +void dxgprocess_ht_lock_shared_up(struct dxgprocess *process); +void dxgprocess_ht_lock_exclusive_down(struct dxgprocess *process); +void dxgprocess_ht_lock_exclusive_up(struct dxgprocess *process); +struct dxgprocess_adapter *dxgprocess_get_adapter_info(struct dxgprocess + *process, + struct dxgadapter + *adapter); + enum dxgadapter_state { DXGADAPTER_STATE_ACTIVE =3D 0, DXGADAPTER_STATE_STOPPED =3D 1, @@ -168,6 +248,8 @@ struct dxgadapter { struct kref adapter_kref; /* Entry in the list of adapters in dxgglobal */ struct list_head adapter_list_entry; + /* The list of dxgprocess_adapter entries */ + struct list_head adapter_process_list_head; struct pci_dev *pci_dev; struct hv_device *hv_dev; struct dxgvmbuschannel channel; @@ -191,6 +273,12 @@ void dxgadapter_release_lock_shared(struct dxgadapter = *adapter); int dxgadapter_acquire_lock_exclusive(struct dxgadapter *adapter); void dxgadapter_acquire_lock_forced(struct dxgadapter *adapter); void dxgadapter_release_lock_exclusive(struct dxgadapter *adapter); +void dxgadapter_add_process(struct dxgadapter *adapter, + struct dxgprocess_adapter *process_info); +void dxgadapter_remove_process(struct dxgprocess_adapter *process_info); + +long dxgk_compat_ioctl(struct file *f, unsigned int p1, unsigned long p2); +long dxgk_unlocked_ioctl(struct file *f, unsigned int p1, unsigned long p2= ); =20 /* * The convention is that VNBus instance id is a GUID, but the host sets @@ -220,9 +308,14 @@ static inline void guid_to_luid(guid_t *guid, struct w= inluid *luid) =20 void dxgvmb_initialize(void); int dxgvmb_send_set_iospace_region(u64 start, u64 len); +int dxgvmb_send_create_process(struct dxgprocess *process); +int dxgvmb_send_destroy_process(struct d3dkmthandle process); int dxgvmb_send_open_adapter(struct dxgadapter *adapter); int dxgvmb_send_close_adapter(struct dxgadapter *adapter); int dxgvmb_send_get_internal_adapter_info(struct dxgadapter *adapter); +int dxgvmb_send_query_adapter_info(struct dxgprocess *process, + struct dxgadapter *adapter, + struct d3dkmt_queryadapterinfo *args); int dxgvmb_send_async_msg(struct dxgvmbuschannel *channel, void *command, u32 cmd_size); diff --git a/drivers/hv/dxgkrnl/dxgmodule.c b/drivers/hv/dxgkrnl/dxgmodule.c index ef80b920f010..17c22001ca6c 100644 --- a/drivers/hv/dxgkrnl/dxgmodule.c +++ b/drivers/hv/dxgkrnl/dxgmodule.c @@ -123,6 +123,20 @@ static struct dxgadapter *find_adapter(struct winluid = *luid) return adapter; } =20 +void dxgglobal_acquire_process_adapter_lock(void) +{ + struct dxgglobal *dxgglobal =3D dxggbl(); + + mutex_lock(&dxgglobal->process_adapter_mutex); +} + +void dxgglobal_release_process_adapter_lock(void) +{ + struct dxgglobal *dxgglobal =3D dxggbl(); + + mutex_unlock(&dxgglobal->process_adapter_mutex); +} + /* * Creates a new dxgadapter object, which represents a virtual GPU, projec= ted * by the host. @@ -147,6 +161,7 @@ int dxgglobal_create_adapter(struct pci_dev *dev, guid_= t *guid, kref_init(&adapter->adapter_kref); init_rwsem(&adapter->core_lock); =20 + INIT_LIST_HEAD(&adapter->adapter_process_list_head); adapter->pci_dev =3D dev; guid_to_luid(guid, &adapter->luid); =20 @@ -205,8 +220,87 @@ static void dxgglobal_stop_adapters(void) dxgglobal_release_adapter_list_lock(DXGLOCK_EXCL); } =20 +/* + * Returns dxgprocess for the current executing process. + * Creates dxgprocess if it doesn't exist. + */ +static struct dxgprocess *dxgglobal_get_current_process(void) +{ + /* + * Find the DXG process for the current process. + * A new process is created if necessary. + */ + struct dxgprocess *process =3D NULL; + struct dxgprocess *entry =3D NULL; + struct dxgglobal *dxgglobal =3D dxggbl(); + + mutex_lock(&dxgglobal->plistmutex); + list_for_each_entry(entry, &dxgglobal->plisthead, plistentry) { + /* All threads of a process have the same thread group ID */ + if (entry->tgid =3D=3D current->tgid) { + if (kref_get_unless_zero(&entry->process_kref)) { + process =3D entry; + DXG_TRACE("found dxgprocess"); + } else { + DXG_TRACE("process is destroyed"); + } + break; + } + } + mutex_unlock(&dxgglobal->plistmutex); + + if (process =3D=3D NULL) + process =3D dxgprocess_create(); + + return process; +} + +/* + * File operations for the /dev/dxg device + */ + +static int dxgk_open(struct inode *n, struct file *f) +{ + int ret =3D 0; + struct dxgprocess *process; + + DXG_TRACE("%p %d %d", f, current->pid, current->tgid); + + /* Find/create a dxgprocess structure for this process */ + process =3D dxgglobal_get_current_process(); + + if (process) { + f->private_data =3D process; + } else { + DXG_TRACE("cannot create dxgprocess"); + ret =3D -EBADF; + } + + return ret; +} + +static int dxgk_release(struct inode *n, struct file *f) +{ + struct dxgprocess *process; + + process =3D (struct dxgprocess *)f->private_data; + DXG_TRACE("%p, %p", f, process); + + if (process =3D=3D NULL) + return -EINVAL; + + kref_put(&process->process_kref, dxgprocess_release); + + f->private_data =3D NULL; + return 0; +} + const struct file_operations dxgk_fops =3D { .owner =3D THIS_MODULE, + .open =3D dxgk_open, + .release =3D dxgk_release, + .compat_ioctl =3D dxgk_compat_ioctl, + .unlocked_ioctl =3D dxgk_unlocked_ioctl, }; =20 /* @@ -616,7 +710,10 @@ static struct dxgglobal *dxgglobal_create(void) if (!dxgglobal) return NULL; =20 + INIT_LIST_HEAD(&dxgglobal->plisthead); + mutex_init(&dxgglobal->plistmutex); mutex_init(&dxgglobal->device_mutex); + mutex_init(&dxgglobal->process_adapter_mutex); =20 INIT_LIST_HEAD(&dxgglobal->vgpu_ch_list_head); INIT_LIST_HEAD(&dxgglobal->adapter_list_head); diff --git a/drivers/hv/dxgkrnl/dxgprocess.c b/drivers/hv/dxgkrnl/dxgproces= s.c new file mode 100644 index 000000000000..ab9a01e3c8c8 --- /dev/null +++ b/drivers/hv/dxgkrnl/dxgprocess.c @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2022, Microsoft Corporation. + * + * Author: + * Iouri Tarassov + * + * Dxgkrnl Graphics Driver + * DXGPROCESS implementation + * + */ + +#include "dxgkrnl.h" + +#undef pr_fmt +#define pr_fmt(fmt) "dxgk: " fmt + +/* + * Creates a new dxgprocess object + * Must be called when dxgglobal->plistmutex is held + */ +struct dxgprocess *dxgprocess_create(void) +{ + struct dxgprocess *process; + int ret; + struct dxgglobal *dxgglobal =3D dxggbl(); + + process =3D kzalloc(sizeof(struct dxgprocess), GFP_KERNEL); + if (process !=3D NULL) { + DXG_TRACE("new dxgprocess created"); + process->pid =3D current->pid; + process->tgid =3D current->tgid; + ret =3D dxgvmb_send_create_process(process); + if (ret < 0) { + DXG_TRACE("send_create_process failed"); + kfree(process); + process =3D NULL; + } else { + INIT_LIST_HEAD(&process->plistentry); + kref_init(&process->process_kref); + + mutex_lock(&dxgglobal->plistmutex); + list_add_tail(&process->plistentry, + &dxgglobal->plisthead); + mutex_unlock(&dxgglobal->plistmutex); + + hmgrtable_init(&process->handle_table, process); + hmgrtable_init(&process->local_handle_table, process); + INIT_LIST_HEAD(&process->process_adapter_list_head); + } + } + return process; +} + +void dxgprocess_destroy(struct dxgprocess *process) +{ + int i; + enum hmgrentry_type t; + struct d3dkmthandle h; + void *o; + struct dxgprocess_adapter *entry; + struct dxgprocess_adapter *tmp; + + /* Destroy all adapter state */ + dxgglobal_acquire_process_adapter_lock(); + list_for_each_entry_safe(entry, tmp, + &process->process_adapter_list_head, + process_adapter_list_entry) { + dxgprocess_adapter_destroy(entry); + } + dxgglobal_release_process_adapter_lock(); + + i =3D 0; + while (hmgrtable_next_entry(&process->local_handle_table, + &i, &t, &h, &o)) { + switch (t) { + case HMGRENTRY_TYPE_DXGADAPTER: + dxgprocess_close_adapter(process, h); + break; + default: + DXG_ERR("invalid entry in handle table %d", t); + break; + } + } + + hmgrtable_destroy(&process->handle_table); + hmgrtable_destroy(&process->local_handle_table); +} + +void dxgprocess_release(struct kref *refcount) +{ + struct dxgprocess *process; + struct dxgglobal *dxgglobal =3D dxggbl(); + + process =3D container_of(refcount, struct dxgprocess, process_kref); + + mutex_lock(&dxgglobal->plistmutex); + list_del(&process->plistentry); + mutex_unlock(&dxgglobal->plistmutex); + + dxgprocess_destroy(process); + + if (process->host_handle.v) + dxgvmb_send_destroy_process(process->host_handle); + kfree(process); +} + +struct dxgprocess_adapter *dxgprocess_get_adapter_info(struct dxgprocess + *process, + struct dxgadapter + *adapter) +{ + struct dxgprocess_adapter *entry; + + list_for_each_entry(entry, &process->process_adapter_list_head, + process_adapter_list_entry) { + if (adapter =3D=3D entry->adapter) { + DXG_TRACE("Found process info %p", entry); + return entry; + } + } + return NULL; +} + +/* + * Dxgprocess takes references on dxgadapter and dxgprocess_adapter. + * + * The process_adapter lock is held. + * + */ +int dxgprocess_open_adapter(struct dxgprocess *process, + struct dxgadapter *adapter, + struct d3dkmthandle *h) +{ + int ret =3D 0; + struct dxgprocess_adapter *adapter_info; + struct d3dkmthandle handle; + + h->v =3D 0; + adapter_info =3D dxgprocess_get_adapter_info(process, adapter); + if (adapter_info =3D=3D NULL) { + DXG_TRACE("creating new process adapter info"); + adapter_info =3D dxgprocess_adapter_create(process, adapter); + if (adapter_info =3D=3D NULL) { + ret =3D -ENOMEM; + goto cleanup; + } + } else { + adapter_info->refcount++; + } + + handle =3D hmgrtable_alloc_handle_safe(&process->local_handle_table, + adapter, HMGRENTRY_TYPE_DXGADAPTER, + true); + if (handle.v) { + *h =3D handle; + } else { + DXG_ERR("failed to create adapter handle"); + ret =3D -ENOMEM; + } + +cleanup: + + if (ret < 0) { + if (adapter_info) + dxgprocess_adapter_release(adapter_info); + } + + return ret; +} + +int dxgprocess_close_adapter(struct dxgprocess *process, + struct d3dkmthandle handle) +{ + struct dxgadapter *adapter; + struct dxgprocess_adapter *adapter_info; + int ret =3D 0; + + if (handle.v =3D=3D 0) + return 0; + + hmgrtable_lock(&process->local_handle_table, DXGLOCK_EXCL); + adapter =3D dxgprocess_get_adapter(process, handle); + if (adapter) + hmgrtable_free_handle(&process->local_handle_table, + HMGRENTRY_TYPE_DXGADAPTER, handle); + hmgrtable_unlock(&process->local_handle_table, DXGLOCK_EXCL); + + if (adapter) { + adapter_info =3D dxgprocess_get_adapter_info(process, adapter); + if (adapter_info) { + dxgglobal_acquire_process_adapter_lock(); + dxgprocess_adapter_release(adapter_info); + dxgglobal_release_process_adapter_lock(); + } else { + ret =3D -EINVAL; + } + } else { + DXG_ERR("Adapter not found %x", handle.v); + ret =3D -EINVAL; + } + + return ret; +} + +struct dxgadapter *dxgprocess_get_adapter(struct dxgprocess *process, + struct d3dkmthandle handle) +{ + struct dxgadapter *adapter; + + adapter =3D hmgrtable_get_object_by_type(&process->local_handle_table, + HMGRENTRY_TYPE_DXGADAPTER, + handle); + if (adapter =3D=3D NULL) + DXG_ERR("Adapter not found %x", handle.v); + return adapter; +} + +/* + * Gets the adapter object from the process handle table. + * The adapter object is referenced. + * The function acquired the handle table lock shared. + */ +struct dxgadapter *dxgprocess_adapter_by_handle(struct dxgprocess *process, + struct d3dkmthandle handle) +{ + struct dxgadapter *adapter; + + hmgrtable_lock(&process->local_handle_table, DXGLOCK_SHARED); + adapter =3D hmgrtable_get_object_by_type(&process->local_handle_table, + HMGRENTRY_TYPE_DXGADAPTER, + handle); + if (adapter =3D=3D NULL) + DXG_ERR("adapter_by_handle failed %x", handle.v); + else if (kref_get_unless_zero(&adapter->adapter_kref) =3D=3D 0) { + DXG_ERR("failed to acquire adapter reference"); + adapter =3D NULL; + } + hmgrtable_unlock(&process->local_handle_table, DXGLOCK_SHARED); + return adapter; +} + +void dxgprocess_ht_lock_shared_down(struct dxgprocess *process) +{ + hmgrtable_lock(&process->handle_table, DXGLOCK_SHARED); +} + +void dxgprocess_ht_lock_shared_up(struct dxgprocess *process) +{ + hmgrtable_unlock(&process->handle_table, DXGLOCK_SHARED); +} + +void dxgprocess_ht_lock_exclusive_down(struct dxgprocess *process) +{ + hmgrtable_lock(&process->handle_table, DXGLOCK_EXCL); +} + +void dxgprocess_ht_lock_exclusive_up(struct dxgprocess *process) +{ + hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL); +} diff --git a/drivers/hv/dxgkrnl/dxgvmbus.c b/drivers/hv/dxgkrnl/dxgvmbus.c index 6d4b8d9d8d07..0abf45d0d3f7 100644 --- a/drivers/hv/dxgkrnl/dxgvmbus.c +++ b/drivers/hv/dxgkrnl/dxgvmbus.c @@ -497,6 +497,87 @@ int dxgvmb_send_set_iospace_region(u64 start, u64 len) return ret; } =20 +int dxgvmb_send_create_process(struct dxgprocess *process) +{ + int ret; + struct dxgkvmb_command_createprocess *command; + struct dxgkvmb_command_createprocess_return result =3D { 0 }; + struct dxgvmbusmsg msg; + char s[WIN_MAX_PATH]; + int i; + struct dxgglobal *dxgglobal =3D dxggbl(); + + ret =3D init_message(&msg, NULL, process, sizeof(*command)); + if (ret) + return ret; + command =3D (void *)msg.msg; + + ret =3D dxgglobal_acquire_channel_lock(); + if (ret < 0) + goto cleanup; + + command_vm_to_host_init1(&command->hdr, DXGK_VMBCOMMAND_CREATEPROCESS); + command->process =3D process; + command->process_id =3D process->pid; + command->linux_process =3D 1; + s[0] =3D 0; + __get_task_comm(s, WIN_MAX_PATH, current); + for (i =3D 0; i < WIN_MAX_PATH; i++) { + command->process_name[i] =3D s[i]; + if (s[i] =3D=3D 0) + break; + } + + ret =3D dxgvmb_send_sync_msg(&dxgglobal->channel, msg.hdr, msg.size, + &result, sizeof(result)); + if (ret < 0) { + DXG_ERR("create_process failed %d", ret); + } else if (result.hprocess.v =3D=3D 0) { + DXG_ERR("create_process returned 0 handle"); + ret =3D -ENOTRECOVERABLE; + } else { + process->host_handle =3D result.hprocess; + DXG_TRACE("create_process returned %x", + process->host_handle.v); + } + + dxgglobal_release_channel_lock(); + +cleanup: + free_message(&msg, process); + if (ret) + DXG_TRACE("err: %d", ret); + return ret; +} + +int dxgvmb_send_destroy_process(struct d3dkmthandle process) +{ + int ret; + struct dxgkvmb_command_destroyprocess *command; + struct dxgvmbusmsg msg; + struct dxgglobal *dxgglobal =3D dxggbl(); + + ret =3D init_message(&msg, NULL, NULL, sizeof(*command)); + if (ret) + return ret; + command =3D (void *)msg.msg; + + ret =3D dxgglobal_acquire_channel_lock(); + if (ret < 0) + goto cleanup; + command_vm_to_host_init2(&command->hdr, DXGK_VMBCOMMAND_DESTROYPROCESS, + process); + ret =3D dxgvmb_send_sync_msg_ntstatus(&dxgglobal->channel, + msg.hdr, msg.size); + dxgglobal_release_channel_lock(); + +cleanup: + free_message(&msg, NULL); + if (ret) + DXG_TRACE("err: %d", ret); + return ret; +} + /* * Virtual GPU messages to the host */ @@ -591,3 +672,86 @@ int dxgvmb_send_get_internal_adapter_info(struct dxgad= apter *adapter) DXG_ERR("Failed to get adapter info: %d", ret); return ret; } + +int dxgvmb_send_query_adapter_info(struct dxgprocess *process, + struct dxgadapter *adapter, + struct d3dkmt_queryadapterinfo *args) +{ + struct dxgkvmb_command_queryadapterinfo *command; + u32 cmd_size =3D sizeof(*command) + args->private_data_size - 1; + int ret; + u32 private_data_size; + void *private_data; + struct dxgvmbusmsg msg =3D {.hdr =3D NULL}; + struct dxgglobal *dxgglobal =3D dxggbl(); + + ret =3D init_message(&msg, adapter, process, cmd_size); + if (ret) + goto cleanup; + command =3D (void *)msg.msg; + + ret =3D copy_from_user(command->private_data, + args->private_data, args->private_data_size); + if (ret) { + DXG_ERR("Faled to copy private data"); + ret =3D -EINVAL; + goto cleanup; + } + + command_vgpu_to_host_init2(&command->hdr, + DXGK_VMBCOMMAND_QUERYADAPTERINFO, + process->host_handle); + command->private_data_size =3D args->private_data_size; + command->query_type =3D args->type; + + if (dxgglobal->vmbus_ver >=3D DXGK_VMBUS_INTERFACE_VERSION) { + private_data =3D msg.msg; + private_data_size =3D command->private_data_size + + sizeof(struct ntstatus); + } else { + private_data =3D command->private_data; + private_data_size =3D command->private_data_size; + } + + ret =3D dxgvmb_send_sync_msg(msg.channel, msg.hdr, msg.size, + private_data, private_data_size); + if (ret < 0) + goto cleanup; + + if (dxgglobal->vmbus_ver >=3D DXGK_VMBUS_INTERFACE_VERSION) { + ret =3D ntstatus2int(*(struct ntstatus *)private_data); + if (ret < 0) + goto cleanup; + private_data =3D (char *)private_data + sizeof(struct ntstatus); + } + + switch (args->type) { + case _KMTQAITYPE_ADAPTERTYPE: + case _KMTQAITYPE_ADAPTERTYPE_RENDER: + { + struct d3dkmt_adaptertype *adapter_type =3D + (void *)private_data; + adapter_type->paravirtualized =3D 1; + adapter_type->display_supported =3D 0; + adapter_type->post_device =3D 0; + adapter_type->indirect_display_device =3D 0; + adapter_type->acg_supported =3D 0; + adapter_type->support_set_timings_from_vidpn =3D 0; + break; + } + default: + break; + } + ret =3D copy_to_user(args->private_data, private_data, + args->private_data_size); + if (ret) { + DXG_ERR("Faled to copy private data to user"); + ret =3D -EINVAL; + } + +cleanup: + free_message(&msg, process); + if (ret) + DXG_TRACE("err: %d", ret); + return ret; +} diff --git a/drivers/hv/dxgkrnl/dxgvmbus.h b/drivers/hv/dxgkrnl/dxgvmbus.h index 584cdd3db6c0..a805a396e083 100644 --- a/drivers/hv/dxgkrnl/dxgvmbus.h +++ b/drivers/hv/dxgkrnl/dxgvmbus.h @@ -14,7 +14,11 @@ #ifndef _DXGVMBUS_H #define _DXGVMBUS_H =20 +struct dxgprocess; +struct dxgadapter; + #define DXG_MAX_VM_BUS_PACKET_SIZE (1024 * 128) +#define DXG_VM_PROCESS_NAME_LENGTH 260 =20 enum dxgkvmb_commandchanneltype { DXGKVMB_VGPU_TO_HOST, @@ -169,6 +173,26 @@ struct dxgkvmb_command_setiospaceregion { u32 shared_page_gpadl; }; =20 +struct dxgkvmb_command_createprocess { + struct dxgkvmb_command_vm_to_host hdr; + void *process; + u64 process_id; + u16 process_name[DXG_VM_PROCESS_NAME_LENGTH + 1]; + u8 csrss_process:1; + u8 dwm_process:1; + u8 wow64_process:1; + u8 linux_process:1; +}; + +struct dxgkvmb_command_createprocess_return { + struct d3dkmthandle hprocess; +}; + +// The command returns ntstatus +struct dxgkvmb_command_destroyprocess { + struct dxgkvmb_command_vm_to_host hdr; +}; + struct dxgkvmb_command_openadapter { struct dxgkvmb_command_vgpu_to_host hdr; u32 vmbus_interface_version; @@ -211,4 +235,16 @@ struct dxgkvmb_command_getinternaladapterinfo_return { struct winluid host_vgpu_luid; }; =20 +struct dxgkvmb_command_queryadapterinfo { + struct dxgkvmb_command_vgpu_to_host hdr; + enum kmtqueryadapterinfotype query_type; + u32 private_data_size; + u8 private_data[1]; +}; + +struct dxgkvmb_command_queryadapterinfo_return { + struct ntstatus status; + u8 private_data[1]; +}; + #endif /* _DXGVMBUS_H */ diff --git a/drivers/hv/dxgkrnl/hmgr.c b/drivers/hv/dxgkrnl/hmgr.c new file mode 100644 index 000000000000..526b50f46d96 --- /dev/null +++ b/drivers/hv/dxgkrnl/hmgr.c @@ -0,0 +1,563 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2022, Microsoft Corporation. + * + * Author: + * Iouri Tarassov + * + * Dxgkrnl Graphics Driver + * Handle manager implementation + * + */ + +#include +#include +#include + +#include "misc.h" +#include "dxgkrnl.h" +#include "hmgr.h" + +#undef pr_fmt +#define pr_fmt(fmt) "dxgk: " fmt + +const struct d3dkmthandle zerohandle; + +/* + * Handle parameters + */ +#define HMGRHANDLE_INSTANCE_BITS 6 +#define HMGRHANDLE_INDEX_BITS 24 +#define HMGRHANDLE_UNIQUE_BITS 2 + +#define HMGRHANDLE_INSTANCE_SHIFT 0 +#define HMGRHANDLE_INDEX_SHIFT \ + (HMGRHANDLE_INSTANCE_BITS + HMGRHANDLE_INSTANCE_SHIFT) +#define HMGRHANDLE_UNIQUE_SHIFT \ + (HMGRHANDLE_INDEX_BITS + HMGRHANDLE_INDEX_SHIFT) + +#define HMGRHANDLE_INSTANCE_MASK \ + (((1 << HMGRHANDLE_INSTANCE_BITS) - 1) << HMGRHANDLE_INSTANCE_SHIFT) +#define HMGRHANDLE_INDEX_MASK \ + (((1 << HMGRHANDLE_INDEX_BITS) - 1) << HMGRHANDLE_INDEX_SHIFT) +#define HMGRHANDLE_UNIQUE_MASK \ + (((1 << HMGRHANDLE_UNIQUE_BITS) - 1) << HMGRHANDLE_UNIQUE_SHIFT) + +#define HMGRHANDLE_INSTANCE_MAX ((1 << HMGRHANDLE_INSTANCE_BITS) - 1) +#define HMGRHANDLE_INDEX_MAX ((1 << HMGRHANDLE_INDEX_BITS) - 1) +#define HMGRHANDLE_UNIQUE_MAX ((1 << HMGRHANDLE_UNIQUE_BITS) - 1) + +/* + * Handle entry + */ +struct hmgrentry { + union { + void *object; + struct { + u32 prev_free_index; + u32 next_free_index; + }; + }; + u32 type:HMGRENTRY_TYPE_BITS + 1; + u32 unique:HMGRHANDLE_UNIQUE_BITS; + u32 instance:HMGRHANDLE_INSTANCE_BITS; + u32 destroyed:1; +}; + +#define HMGRTABLE_SIZE_INCREMENT 1024 +#define HMGRTABLE_MIN_FREE_ENTRIES 128 +#define HMGRTABLE_INVALID_INDEX (~((1 << HMGRHANDLE_INDEX_BITS) - 1)) +#define HMGRTABLE_SIZE_MAX 0xFFFFFFF + +static u32 table_size_increment =3D HMGRTABLE_SIZE_INCREMENT; + +static u32 get_unique(struct d3dkmthandle h) +{ + return (h.v & HMGRHANDLE_UNIQUE_MASK) >> HMGRHANDLE_UNIQUE_SHIFT; +} + +static u32 get_index(struct d3dkmthandle h) +{ + return (h.v & HMGRHANDLE_INDEX_MASK) >> HMGRHANDLE_INDEX_SHIFT; +} + +static bool is_handle_valid(struct hmgrtable *table, struct d3dkmthandle h, + bool ignore_destroyed, enum hmgrentry_type t) +{ + u32 index =3D get_index(h); + u32 unique =3D get_unique(h); + struct hmgrentry *entry; + + if (index >=3D table->table_size) { + DXG_ERR("Invalid index %x %d", h.v, index); + return false; + } + + entry =3D &table->entry_table[index]; + if (unique !=3D entry->unique) { + DXG_ERR("Invalid unique %x %d %d %d %p", + h.v, unique, entry->unique, index, entry->object); + return false; + } + + if (entry->destroyed && !ignore_destroyed) { + DXG_ERR("Invalid destroyed value"); + return false; + } + + if (entry->type =3D=3D HMGRENTRY_TYPE_FREE) { + DXG_ERR("Entry is freed %x %d", h.v, index); + return false; + } + + if (t !=3D HMGRENTRY_TYPE_FREE && t !=3D entry->type) { + DXG_ERR("type mismatch %x %d %d", h.v, t, entry->type); + return false; + } + + return true; +} + +static struct d3dkmthandle build_handle(u32 index, u32 unique, u32 instanc= e) +{ + struct d3dkmthandle handle; + + handle.v =3D (index << HMGRHANDLE_INDEX_SHIFT) & HMGRHANDLE_INDEX_MASK; + handle.v |=3D (unique << HMGRHANDLE_UNIQUE_SHIFT) & + HMGRHANDLE_UNIQUE_MASK; + handle.v |=3D (instance << HMGRHANDLE_INSTANCE_SHIFT) & + HMGRHANDLE_INSTANCE_MASK; + + return handle; +} + +inline u32 hmgrtable_get_used_entry_count(struct hmgrtable *table) +{ + DXGKRNL_ASSERT(table->table_size >=3D table->free_count); + return (table->table_size - table->free_count); +} + +bool hmgrtable_mark_destroyed(struct hmgrtable *table, struct d3dkmthandle= h) +{ + if (!is_handle_valid(table, h, false, HMGRENTRY_TYPE_FREE)) + return false; + + table->entry_table[get_index(h)].destroyed =3D true; + return true; +} + +bool hmgrtable_unmark_destroyed(struct hmgrtable *table, struct d3dkmthand= le h) +{ + if (!is_handle_valid(table, h, true, HMGRENTRY_TYPE_FREE)) + return true; + + DXGKRNL_ASSERT(table->entry_table[get_index(h)].destroyed); + table->entry_table[get_index(h)].destroyed =3D 0; + return true; +} + +static bool expand_table(struct hmgrtable *table, u32 NumEntries) +{ + u32 new_table_size; + struct hmgrentry *new_entry; + u32 table_index; + u32 new_free_count; + u32 prev_free_index; + u32 tail_index =3D table->free_handle_list_tail; + + /* The tail should point to the last free element in the list */ + if (table->free_count !=3D 0) { + if (tail_index >=3D table->table_size || + table->entry_table[tail_index].next_free_index !=3D + HMGRTABLE_INVALID_INDEX) { + DXG_ERR("corruption"); + DXG_ERR("tail_index: %x", tail_index); + DXG_ERR("table size: %x", table->table_size); + DXG_ERR("free_count: %d", table->free_count); + DXG_ERR("NumEntries: %x", NumEntries); + return false; + } + } + + new_free_count =3D table_size_increment + table->free_count; + new_table_size =3D table->table_size + table_size_increment; + if (new_table_size < NumEntries) { + new_free_count +=3D NumEntries - new_table_size; + new_table_size =3D NumEntries; + } + + if (new_table_size > HMGRHANDLE_INDEX_MAX) { + DXG_ERR("Invalid new table size"); + return false; + } + + new_entry =3D (struct hmgrentry *) + vzalloc(new_table_size * sizeof(struct hmgrentry)); + if (new_entry =3D=3D NULL) { + DXG_ERR("allocation failed"); + return false; + } + + if (table->entry_table) { + memcpy(new_entry, table->entry_table, + table->table_size * sizeof(struct hmgrentry)); + vfree(table->entry_table); + } else { + table->free_handle_list_head =3D 0; + } + + table->entry_table =3D new_entry; + + /* Initialize new table entries and add to the free list */ + table_index =3D table->table_size; + + prev_free_index =3D table->free_handle_list_tail; + + while (table_index < new_table_size) { + struct hmgrentry *entry =3D &table->entry_table[table_index]; + + entry->prev_free_index =3D prev_free_index; + entry->next_free_index =3D table_index + 1; + entry->type =3D HMGRENTRY_TYPE_FREE; + entry->unique =3D 1; + entry->instance =3D 0; + prev_free_index =3D table_index; + + table_index++; + } + + table->entry_table[table_index - 1].next_free_index =3D + (u32) HMGRTABLE_INVALID_INDEX; + + if (table->free_count !=3D 0) { + /* Link the current free list with the new entries */ + struct hmgrentry *entry; + + entry =3D &table->entry_table[table->free_handle_list_tail]; + entry->next_free_index =3D table->table_size; + } + table->free_handle_list_tail =3D new_table_size - 1; + if (table->free_handle_list_head =3D=3D HMGRTABLE_INVALID_INDEX) + table->free_handle_list_head =3D table->table_size; + + table->table_size =3D new_table_size; + table->free_count =3D new_free_count; + + return true; +} + +void hmgrtable_init(struct hmgrtable *table, struct dxgprocess *process) +{ + table->process =3D process; + table->entry_table =3D NULL; + table->table_size =3D 0; + table->free_handle_list_head =3D HMGRTABLE_INVALID_INDEX; + table->free_handle_list_tail =3D HMGRTABLE_INVALID_INDEX; + table->free_count =3D 0; + init_rwsem(&table->table_lock); +} + +void hmgrtable_destroy(struct hmgrtable *table) +{ + if (table->entry_table) { + vfree(table->entry_table); + table->entry_table =3D NULL; + } +} + +void hmgrtable_lock(struct hmgrtable *table, enum dxglockstate state) +{ + if (state =3D=3D DXGLOCK_EXCL) + down_write(&table->table_lock); + else + down_read(&table->table_lock); +} + +void hmgrtable_unlock(struct hmgrtable *table, enum dxglockstate state) +{ + if (state =3D=3D DXGLOCK_EXCL) + up_write(&table->table_lock); + else + up_read(&table->table_lock); +} + +struct d3dkmthandle hmgrtable_alloc_handle(struct hmgrtable *table, + void *object, + enum hmgrentry_type type, + bool make_valid) +{ + u32 index; + struct hmgrentry *entry; + u32 unique; + + DXGKRNL_ASSERT(type <=3D HMGRENTRY_TYPE_LIMIT); + DXGKRNL_ASSERT(type > HMGRENTRY_TYPE_FREE); + + if (table->free_count <=3D HMGRTABLE_MIN_FREE_ENTRIES) { + if (!expand_table(table, 0)) { + DXG_ERR("hmgrtable expand_table failed"); + return zerohandle; + } + } + + if (table->free_handle_list_head >=3D table->table_size) { + DXG_ERR("hmgrtable corrupted handle table head"); + return zerohandle; + } + + index =3D table->free_handle_list_head; + entry =3D &table->entry_table[index]; + + if (entry->type !=3D HMGRENTRY_TYPE_FREE) { + DXG_ERR("hmgrtable expected free handle"); + return zerohandle; + } + + table->free_handle_list_head =3D entry->next_free_index; + + if (entry->next_free_index !=3D table->free_handle_list_tail) { + if (entry->next_free_index >=3D table->table_size) { + DXG_ERR("hmgrtable invalid next free index"); + return zerohandle; + } + table->entry_table[entry->next_free_index].prev_free_index =3D + HMGRTABLE_INVALID_INDEX; + } + + unique =3D table->entry_table[index].unique; + + table->entry_table[index].object =3D object; + table->entry_table[index].type =3D type; + table->entry_table[index].instance =3D 0; + table->entry_table[index].destroyed =3D !make_valid; + table->free_count--; + DXGKRNL_ASSERT(table->free_count <=3D table->table_size); + + return build_handle(index, unique, table->entry_table[index].instance); +} + +int hmgrtable_assign_handle_safe(struct hmgrtable *table, + void *object, + enum hmgrentry_type type, + struct d3dkmthandle h) +{ + int ret; + + hmgrtable_lock(table, DXGLOCK_EXCL); + ret =3D hmgrtable_assign_handle(table, object, type, h); + hmgrtable_unlock(table, DXGLOCK_EXCL); + return ret; +} + +int hmgrtable_assign_handle(struct hmgrtable *table, void *object, + enum hmgrentry_type type, struct d3dkmthandle h) +{ + u32 index =3D get_index(h); + u32 unique =3D get_unique(h); + struct hmgrentry *entry =3D NULL; + + DXG_TRACE("%x, %d %p, %p", h.v, index, object, table); + + if (index >=3D HMGRHANDLE_INDEX_MAX) { + DXG_ERR("handle index is too big: %x %d", h.v, index); + return -EINVAL; + } + + if (index >=3D table->table_size) { + u32 new_size =3D index + table_size_increment; + + if (new_size > HMGRHANDLE_INDEX_MAX) + new_size =3D HMGRHANDLE_INDEX_MAX; + if (!expand_table(table, new_size)) { + DXG_ERR("failed to expand handle table %d", + new_size); + return -ENOMEM; + } + } + + entry =3D &table->entry_table[index]; + + if (entry->type !=3D HMGRENTRY_TYPE_FREE) { + DXG_ERR("the entry is not free: %d %x", entry->type, + hmgrtable_build_entry_handle(table, index).v); + return -EINVAL; + } + + if (index !=3D table->free_handle_list_tail) { + if (entry->next_free_index >=3D table->table_size) { + DXG_ERR("hmgr: invalid next free index %d", + entry->next_free_index); + return -EINVAL; + } + table->entry_table[entry->next_free_index].prev_free_index =3D + entry->prev_free_index; + } else { + table->free_handle_list_tail =3D entry->prev_free_index; + } + + if (index !=3D table->free_handle_list_head) { + if (entry->prev_free_index >=3D table->table_size) { + DXG_ERR("hmgr: invalid next prev index %d", + entry->prev_free_index); + return -EINVAL; + } + table->entry_table[entry->prev_free_index].next_free_index =3D + entry->next_free_index; + } else { + table->free_handle_list_head =3D entry->next_free_index; + } + + entry->prev_free_index =3D HMGRTABLE_INVALID_INDEX; + entry->next_free_index =3D HMGRTABLE_INVALID_INDEX; + entry->object =3D object; + entry->type =3D type; + entry->instance =3D 0; + entry->unique =3D unique; + entry->destroyed =3D false; + + table->free_count--; + DXGKRNL_ASSERT(table->free_count <=3D table->table_size); + return 0; +} + +struct d3dkmthandle hmgrtable_alloc_handle_safe(struct hmgrtable *table, + void *obj, + enum hmgrentry_type type, + bool make_valid) +{ + struct d3dkmthandle h; + + hmgrtable_lock(table, DXGLOCK_EXCL); + h =3D hmgrtable_alloc_handle(table, obj, type, make_valid); + hmgrtable_unlock(table, DXGLOCK_EXCL); + return h; +} + +void hmgrtable_free_handle(struct hmgrtable *table, enum hmgrentry_type t, + struct d3dkmthandle h) +{ + struct hmgrentry *entry; + u32 i =3D get_index(h); + + DXG_TRACE("%p %x", table, h.v); + + /* Ignore the destroyed flag when checking the handle */ + if (is_handle_valid(table, h, true, t)) { + DXGKRNL_ASSERT(table->free_count < table->table_size); + entry =3D &table->entry_table[i]; + entry->unique =3D 1; + entry->type =3D HMGRENTRY_TYPE_FREE; + entry->destroyed =3D 0; + if (entry->unique !=3D HMGRHANDLE_UNIQUE_MAX) + entry->unique +=3D 1; + else + entry->unique =3D 1; + + table->free_count++; + DXGKRNL_ASSERT(table->free_count <=3D table->table_size); + + /* + * Insert the index to the free list at the tail. + */ + entry->next_free_index =3D HMGRTABLE_INVALID_INDEX; + entry->prev_free_index =3D table->free_handle_list_tail; + entry =3D &table->entry_table[table->free_handle_list_tail]; + entry->next_free_index =3D i; + table->free_handle_list_tail =3D i; + } else { + DXG_ERR("Invalid handle to free: %d %x", i, h.v); + } +} + +void hmgrtable_free_handle_safe(struct hmgrtable *table, enum hmgrentry_ty= pe t, + struct d3dkmthandle h) +{ + hmgrtable_lock(table, DXGLOCK_EXCL); + hmgrtable_free_handle(table, t, h); + hmgrtable_unlock(table, DXGLOCK_EXCL); +} + +struct d3dkmthandle hmgrtable_build_entry_handle(struct hmgrtable *table, + u32 index) +{ + DXGKRNL_ASSERT(index < table->table_size); + + return build_handle(index, table->entry_table[index].unique, + table->entry_table[index].instance); +} + +void *hmgrtable_get_object(struct hmgrtable *table, struct d3dkmthandle h) +{ + if (!is_handle_valid(table, h, false, HMGRENTRY_TYPE_FREE)) + return NULL; + + return table->entry_table[get_index(h)].object; +} + +void *hmgrtable_get_object_by_type(struct hmgrtable *table, + enum hmgrentry_type type, + struct d3dkmthandle h) +{ + if (!is_handle_valid(table, h, false, type)) { + DXG_ERR("Invalid handle %x", h.v); + return NULL; + } + return table->entry_table[get_index(h)].object; +} + +void *hmgrtable_get_entry_object(struct hmgrtable *table, u32 index) +{ + DXGKRNL_ASSERT(index < table->table_size); + DXGKRNL_ASSERT(table->entry_table[index].type !=3D HMGRENTRY_TYPE_FREE); + + return table->entry_table[index].object; +} + +static enum hmgrentry_type hmgrtable_get_entry_type(struct hmgrtable *tabl= e, + u32 index) +{ + DXGKRNL_ASSERT(index < table->table_size); + return (enum hmgrentry_type)table->entry_table[index].type; +} + +enum hmgrentry_type hmgrtable_get_object_type(struct hmgrtable *table, + struct d3dkmthandle h) +{ + if (!is_handle_valid(table, h, false, HMGRENTRY_TYPE_FREE)) + return HMGRENTRY_TYPE_FREE; + + return hmgrtable_get_entry_type(table, get_index(h)); +} + +void *hmgrtable_get_object_ignore_destroyed(struct hmgrtable *table, + struct d3dkmthandle h, + enum hmgrentry_type type) +{ + if (!is_handle_valid(table, h, true, type)) + return NULL; + return table->entry_table[get_index(h)].object; +} + +bool hmgrtable_next_entry(struct hmgrtable *tbl, + u32 *index, + enum hmgrentry_type *type, + struct d3dkmthandle *handle, + void **object) +{ + u32 i; + struct hmgrentry *entry; + + for (i =3D *index; i < tbl->table_size; i++) { + entry =3D &tbl->entry_table[i]; + if (entry->type !=3D HMGRENTRY_TYPE_FREE) { + *index =3D i + 1; + *object =3D entry->object; + *handle =3D build_handle(i, entry->unique, + entry->instance); + *type =3D entry->type; + return true; + } + } + return false; +} diff --git a/drivers/hv/dxgkrnl/hmgr.h b/drivers/hv/dxgkrnl/hmgr.h new file mode 100644 index 000000000000..23eec301137f --- /dev/null +++ b/drivers/hv/dxgkrnl/hmgr.h @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Copyright (c) 2022, Microsoft Corporation. + * + * Author: + * Iouri Tarassov + * + * Dxgkrnl Graphics Driver + * Handle manager definitions + * + */ + +#ifndef _HMGR_H_ +#define _HMGR_H_ + +#include "misc.h" + +struct hmgrentry; + +/* + * Handle manager table. + * + * Implementation notes: + * A list of free handles is built on top of the array of table entries. + * free_handle_list_head is the index of the first entry in the list. + * m_FreeHandleListTail is the index of an entry in the list, which is + * HMGRTABLE_MIN_FREE_ENTRIES from the head. It means that when a handle= is + * freed, the next time the handle can be re-used is after allocating + * HMGRTABLE_MIN_FREE_ENTRIES number of handles. + * Handles are allocated from the start of the list and free handles are + * inserted after the tail of the list. + * + */ +struct hmgrtable { + struct dxgprocess *process; + struct hmgrentry *entry_table; + u32 free_handle_list_head; + u32 free_handle_list_tail; + u32 table_size; + u32 free_count; + struct rw_semaphore table_lock; +}; + +/* + * Handle entry data types. + */ +#define HMGRENTRY_TYPE_BITS 5 + +enum hmgrentry_type { + HMGRENTRY_TYPE_FREE =3D 0, + HMGRENTRY_TYPE_DXGADAPTER =3D 1, + HMGRENTRY_TYPE_DXGSHAREDRESOURCE =3D 2, + HMGRENTRY_TYPE_DXGDEVICE =3D 3, + HMGRENTRY_TYPE_DXGRESOURCE =3D 4, + HMGRENTRY_TYPE_DXGALLOCATION =3D 5, + HMGRENTRY_TYPE_DXGOVERLAY =3D 6, + HMGRENTRY_TYPE_DXGCONTEXT =3D 7, + HMGRENTRY_TYPE_DXGSYNCOBJECT =3D 8, + HMGRENTRY_TYPE_DXGKEYEDMUTEX =3D 9, + HMGRENTRY_TYPE_DXGPAGINGQUEUE =3D 10, + HMGRENTRY_TYPE_DXGDEVICESYNCOBJECT =3D 11, + HMGRENTRY_TYPE_DXGPROCESS =3D 12, + HMGRENTRY_TYPE_DXGSHAREDVMOBJECT =3D 13, + HMGRENTRY_TYPE_DXGPROTECTEDSESSION =3D 14, + HMGRENTRY_TYPE_DXGHWQUEUE =3D 15, + HMGRENTRY_TYPE_DXGREMOTEBUNDLEOBJECT =3D 16, + HMGRENTRY_TYPE_DXGCOMPOSITIONSURFACEOBJECT =3D 17, + HMGRENTRY_TYPE_DXGCOMPOSITIONSURFACEPROXY =3D 18, + HMGRENTRY_TYPE_DXGTRACKEDWORKLOAD =3D 19, + HMGRENTRY_TYPE_LIMIT =3D ((1 << HMGRENTRY_TYPE_BITS) - 1), + HMGRENTRY_TYPE_MONITOREDFENCE =3D HMGRENTRY_TYPE_LIMIT + 1, +}; + +void hmgrtable_init(struct hmgrtable *tbl, struct dxgprocess *process); +void hmgrtable_destroy(struct hmgrtable *tbl); +void hmgrtable_lock(struct hmgrtable *tbl, enum dxglockstate state); +void hmgrtable_unlock(struct hmgrtable *tbl, enum dxglockstate state); +struct d3dkmthandle hmgrtable_alloc_handle(struct hmgrtable *tbl, void *ob= ject, + enum hmgrentry_type t, bool make_valid); +struct d3dkmthandle hmgrtable_alloc_handle_safe(struct hmgrtable *tbl, + void *obj, + enum hmgrentry_type t, + bool reserve); +int hmgrtable_assign_handle(struct hmgrtable *tbl, void *obj, + enum hmgrentry_type, struct d3dkmthandle h); +int hmgrtable_assign_handle_safe(struct hmgrtable *tbl, void *obj, + enum hmgrentry_type t, struct d3dkmthandle h); +void hmgrtable_free_handle(struct hmgrtable *tbl, enum hmgrentry_type t, + struct d3dkmthandle h); +void hmgrtable_free_handle_safe(struct hmgrtable *tbl, enum hmgrentry_type= t, + struct d3dkmthandle h); +struct d3dkmthandle hmgrtable_build_entry_handle(struct hmgrtable *tbl, + u32 index); +enum hmgrentry_type hmgrtable_get_object_type(struct hmgrtable *tbl, + struct d3dkmthandle h); +void *hmgrtable_get_object(struct hmgrtable *tbl, struct d3dkmthandle h); +void *hmgrtable_get_object_by_type(struct hmgrtable *tbl, enum hmgrentry_t= ype t, + struct d3dkmthandle h); +void *hmgrtable_get_object_ignore_destroyed(struct hmgrtable *tbl, + struct d3dkmthandle h, + enum hmgrentry_type t); +bool hmgrtable_mark_destroyed(struct hmgrtable *tbl, struct d3dkmthandle h= ); +bool hmgrtable_unmark_destroyed(struct hmgrtable *tbl, struct d3dkmthandle= h); +void *hmgrtable_get_entry_object(struct hmgrtable *tbl, u32 index); +bool hmgrtable_next_entry(struct hmgrtable *tbl, + u32 *start_index, + enum hmgrentry_type *type, + struct d3dkmthandle *handle, + void **object); + +#endif diff --git a/drivers/hv/dxgkrnl/ioctl.c b/drivers/hv/dxgkrnl/ioctl.c index 23ecd15b0cd7..60e38d104517 100644 --- a/drivers/hv/dxgkrnl/ioctl.c +++ b/drivers/hv/dxgkrnl/ioctl.c @@ -22,3 +22,63 @@ =20 #undef pr_fmt #define pr_fmt(fmt) "dxgk: " fmt + +struct ioctl_desc { + int (*ioctl_callback)(struct dxgprocess *p, void __user *arg); + u32 ioctl; + u32 arg_size; +}; + +static struct ioctl_desc ioctls[] =3D { + +}; + +/* + * IOCTL processing + * The driver IOCTLs return + * - 0 in case of success + * - positive values, which are Windows NTSTATUS (for example, STATUS_PEND= ING). + * Positive values are success codes. + * - Linux negative error codes + */ +static int dxgk_ioctl(struct file *f, unsigned int p1, unsigned long p2) +{ + int code =3D _IOC_NR(p1); + int status; + struct dxgprocess *process; + + if (code < 1 || code >=3D ARRAY_SIZE(ioctls)) { + DXG_ERR("bad ioctl %x %x %x %x", + code, _IOC_TYPE(p1), _IOC_SIZE(p1), _IOC_DIR(p1)); + return -ENOTTY; + } + if (ioctls[code].ioctl_callback =3D=3D NULL) { + DXG_ERR("ioctl callback is NULL %x", code); + return -ENOTTY; + } + if (ioctls[code].ioctl !=3D p1) { + DXG_ERR("ioctl mismatch. Code: %x User: %x Kernel: %x", + code, p1, ioctls[code].ioctl); + return -ENOTTY; + } + process =3D (struct dxgprocess *)f->private_data; + if (process->tgid !=3D current->tgid) { + DXG_ERR("Call from a wrong process: %d %d", + process->tgid, current->tgid); + return -ENOTTY; + } + status =3D ioctls[code].ioctl_callback(process, (void *__user)p2); + return status; +} + +long dxgk_compat_ioctl(struct file *f, unsigned int p1, unsigned long p2) +{ + DXG_TRACE("compat ioctl %x", p1); + return dxgk_ioctl(f, p1, p2); +} + +long dxgk_unlocked_ioctl(struct file *f, unsigned int p1, unsigned long p2) +{ + DXG_TRACE("unlocked ioctl %x Code:%d", p1, _IOC_NR(p1)); + return dxgk_ioctl(f, p1, p2); +} diff --git a/drivers/hv/dxgkrnl/misc.h b/drivers/hv/dxgkrnl/misc.h index d292e9a9bb7f..dc849a8ed3f2 100644 --- a/drivers/hv/dxgkrnl/misc.h +++ b/drivers/hv/dxgkrnl/misc.h @@ -27,10 +27,11 @@ extern const struct d3dkmthandle zerohandle; * * channel_lock (VMBus channel lock) * fd_mutex - * plistmutex (process list mutex) - * table_lock (handle table lock) - * core_lock (dxgadapter lock) - * device_lock (dxgdevice lock) + * plistmutex + * table_lock + * core_lock + * device_lock + * process_adapter_mutex * adapter_list_lock * device_mutex (dxgglobal mutex) */ diff --git a/include/uapi/misc/d3dkmthk.h b/include/uapi/misc/d3dkmthk.h index 2ea04cc02a1f..c675d5827ed5 100644 --- a/include/uapi/misc/d3dkmthk.h +++ b/include/uapi/misc/d3dkmthk.h @@ -58,4 +58,107 @@ struct winluid { __u32 b; }; =20 +#define D3DKMT_ADAPTERS_MAX 64 + +struct d3dkmt_adapterinfo { + struct d3dkmthandle adapter_handle; + struct winluid adapter_luid; + __u32 num_sources; + __u32 present_move_regions_preferred; +}; + +struct d3dkmt_enumadapters2 { + __u32 num_adapters; + __u32 reserved; +#ifdef __KERNEL__ + struct d3dkmt_adapterinfo *adapters; +#else + __u64 *adapters; +#endif +}; + +struct d3dkmt_closeadapter { + struct d3dkmthandle adapter_handle; +}; + +struct d3dkmt_openadapterfromluid { + struct winluid adapter_luid; + struct d3dkmthandle adapter_handle; +}; + +struct d3dkmt_adaptertype { + union { + struct { + __u32 render_supported:1; + __u32 display_supported:1; + __u32 software_device:1; + __u32 post_device:1; + __u32 hybrid_discrete:1; + __u32 hybrid_integrated:1; + __u32 indirect_display_device:1; + __u32 paravirtualized:1; + __u32 acg_supported:1; + __u32 support_set_timings_from_vidpn:1; + __u32 detachable:1; + __u32 compute_only:1; + __u32 prototype:1; + __u32 reserved:19; + }; + __u32 value; + }; +}; + +enum kmtqueryadapterinfotype { + _KMTQAITYPE_UMDRIVERPRIVATE =3D 0, + _KMTQAITYPE_ADAPTERTYPE =3D 15, + _KMTQAITYPE_ADAPTERTYPE_RENDER =3D 57 +}; + +struct d3dkmt_queryadapterinfo { + struct d3dkmthandle adapter; + enum kmtqueryadapterinfotype type; +#ifdef __KERNEL__ + void *private_data; +#else + __u64 private_data; +#endif + __u32 private_data_size; +}; + +union d3dkmt_enumadapters_filter { + struct { + __u64 include_compute_only:1; + __u64 include_display_only:1; + __u64 reserved:62; + }; + __u64 value; +}; + +struct d3dkmt_enumadapters3 { + union d3dkmt_enumadapters_filter filter; + __u32 adapter_count; + __u32 reserved; +#ifdef __KERNEL__ + struct d3dkmt_adapterinfo *adapters; +#else + __u64 adapters; +#endif +}; + +/* + * Dxgkrnl Graphics Port Driver ioctl definitions + * + */ + +#define LX_DXOPENADAPTERFROMLUID \ + _IOWR(0x47, 0x01, struct d3dkmt_openadapterfromluid) +#define LX_DXQUERYADAPTERINFO \ + _IOWR(0x47, 0x09, struct d3dkmt_queryadapterinfo) +#define LX_DXENUMADAPTERS2 \ + _IOWR(0x47, 0x14, struct d3dkmt_enumadapters2) +#define LX_DXCLOSEADAPTER \ + _IOWR(0x47, 0x15, struct d3dkmt_closeadapter) +#define LX_DXENUMADAPTERS3 \ + _IOWR(0x47, 0x3e, struct d3dkmt_enumadapters3) + #endif /* _D3DKMTHK_H */